2018-04-05 11:42:17 +02:00
# coding: utf8
2018-03-08 11:50:35 +01:00
# Copyright (C) 2016 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
import json
import datetime
import uuid
2018-03-29 15:37:56 +02:00
import zipfile
import StringIO
2018-04-05 11:42:17 +02:00
import HTMLParser
2018-03-29 15:37:56 +02:00
import os # TODO: inutile si on genere le zip in-memory
2018-03-08 11:50:35 +01:00
from email import encoders
from email . mime . audio import MIMEAudio
from email . mime . base import MIMEBase
from email . mime . image import MIMEImage
from email . mime . multipart import MIMEMultipart
from email . mime . text import MIMEText
from suds . client import Client
from suds . transport import Reply
from suds . transport . http import HttpAuthenticated
import suds . sudsobject
from django . db import models
from django . utils . translation import ugettext_lazy as _
from django . core . cache import cache
from passerelle . base . models import BaseResource
from passerelle . utils . api import endpoint
from . formdata import FormData , CREATION_SCHEMA , list_schema_fields
class ParameterTypeError ( Exception ) :
http_status = 400
log_error = False
def fill_sudsobject_with_dict ( sudsobject , fields , prefix = None ) :
for key , value in sudsobject :
if prefix :
attr = ' %s _ %s ' % ( prefix , key )
else :
attr = key
if isinstance ( value , suds . sudsobject . Object ) :
fill_sudsobject_with_dict ( value , fields , attr )
else :
if attr in fields :
# sudsobject.foo.bar <- fields['foo_bar']
setattr ( sudsobject , key , fields [ attr ] )
def sudsobject_to_dict ( sudsobject ) :
out = { }
for key , value in suds . sudsobject . asdict ( sudsobject ) . iteritems ( ) :
if hasattr ( value , ' __keylist__ ' ) :
out [ key ] = sudsobject_to_dict ( value )
elif isinstance ( value , list ) :
out [ key ] = [ ]
for item in value :
if hasattr ( item , ' __keylist__ ' ) :
out [ key ] . append ( sudsobject_to_dict ( item ) )
else :
out [ key ] . append ( item )
else :
out [ key ] = value
return out
class CartADS ( BaseResource ) :
token_url = models . URLField ( _ ( ' Token URL ' ) , max_length = 256 )
token_authorization = models . CharField ( _ ( ' Token Authorization ' ) , max_length = 128 )
wsdl_url = models . CharField ( _ ( ' WSDL URL ' ) , max_length = 256 ) # not URLField, it can be file://
verify_cert = models . BooleanField ( default = True ,
verbose_name = _ ( ' Check HTTPS Certificate validity ' ) )
# TODO : a mettre en param du connecteur
2018-03-29 15:37:56 +02:00
#dirname = 'D:\LSM\Gfi\DepotDossierFTP\client1\\' #dev cartads
dirname = ' E: \ LSM \ Gfi \ DepotDossierFTP \ client1 \\ ' #recette cartads
2018-03-08 11:50:35 +01:00
category = _ ( ' Business Process Connectors ' )
class Meta :
verbose_name = _ ( ' CartADS Webservices ' )
def get_token ( self , renew = False ) :
cache_key = ' cartads- %s -token ' % self . id
if not renew :
token = cache . get ( cache_key )
if token :
return token
headers = { ' Authorization ' : ' Basic %s ' % self . token_authorization }
resp = self . requests . post ( self . token_url , headers = headers ,
data = { ' grant_type ' : ' client_credentials ' } ,
verify = self . verify_cert ) . json ( )
token = ' %s %s ' % ( resp . get ( ' token_type ' ) , resp . get ( ' access_token ' ) )
timeout = int ( resp . get ( ' expires_in ' ) )
cache . set ( cache_key , token , timeout )
self . logger . debug ( ' new token: %s (timeout %s s) ' , token , timeout )
return token
def get_client ( self , attachments = [ ] ) :
class Transport ( HttpAuthenticated ) :
def __init__ ( self , instance , attachments ) :
self . instance = instance
self . attachments = attachments
HttpAuthenticated . __init__ ( self )
def send ( self , request ) :
request . message = request . message . replace ( " contentType " , " xm:contentType " )
request . headers [ ' Authorization ' ] = self . instance . get_token ( )
2018-03-29 15:37:56 +02:00
''' inutile pour test avec traitement dans la minute toujours active en dev et rec
#print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "message: ", request.message
if " <ns0:informationsComplementaires></ns0:informationsComplementaires> " in request . message :
# fait planter
#request.message = request.message.replace('xmlns:ns0="http://tempuri.org/"',
#'xmlns:ns0="http://tempuri.org/" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays')
request . message = request . message . replace ( " <ns0:informationsComplementaires></ns0:informationsComplementaires> " ,
" <ns0:informationsComplementaires><arr:KeyValueOfstringstring><arr:Key>traitementImmediat</arr:Key><arr:Value>1</arr:Value></arr:KeyValueOfstringstring></ns0:informationsComplementaires> " )
'''
2018-03-08 11:50:35 +01:00
resp = self . instance . requests . post ( request . url , data = request . message ,
headers = request . headers ,
verify = self . instance . verify_cert )
if resp . status_code == 401 :
# ask for a new token, and retry
request . headers [ ' Authorization ' ] = self . instance . get_token ( renew = True )
resp = self . instance . requests . post ( request . url , data = request . message ,
headers = request . headers ,
verify = self . instance . verify_cert )
return Reply ( resp . status_code , resp . headers , resp . content )
return Client ( url = self . wsdl_url , transport = Transport ( self , attachments ) )
@endpoint ( perm = ' can_access ' , methods = [ ' get ' , ' post ' ] )
def create ( self , request ) :
2018-03-29 15:37:56 +02:00
# Create the in-memory file-like object for working w/imz (https://stackoverflow.com/questions/10908877/extracting-a-zipfile-to-memory)
self . in_memory_zip = StringIO . StringIO ( )
# Zip creation (https://pymotw.com/2/zipfile/)
# Get a handle to the in-memory zip in append mode
#zf = zipfile.ZipFile(self.in_memory_zip, "a", zipfile.ZIP_DEFLATED, False)
# Nom du fichier zip a envoyer
zipFileName = str ( uuid . uuid4 ( ) ) + " .zip " ;
#b64_fileContent = base64.b64encode(self.in_memory_zip.read())
# TODO : temp : on genere fichier physique parce que in_memory_zip genere un zip vide pour l'instant
localFileName = ' /home/grandlyon/temp/ ' + zipFileName
zf = zipfile . ZipFile ( localFileName ,
mode = ' w ' ,
compression = zipfile . ZIP_DEFLATED ,
)
2018-03-08 11:50:35 +01:00
# get creation fields from payload
try :
formdata = FormData ( json . loads ( request . body ) , CREATION_SCHEMA )
except ValueError as e :
raise ParameterTypeError ( e . message )
2018-03-29 15:37:56 +02:00
#print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "commune_raw=", formdata.values['commune_raw']
#return
2018-03-08 11:50:35 +01:00
if formdata . attachments :
print >> open ( ' /home/grandlyon/log/cartads.debug ' , ' a+ ' ) , datetime . datetime . now ( ) , " nb attach: " , len ( formdata . attachments )
2018-03-29 15:37:56 +02:00
for key , attachment in formdata . attachments . items ( ) :
2018-03-08 11:50:35 +01:00
filename = attachment . get ( ' filename ' ) or ' file %s .bin ' % num
2018-03-29 15:37:56 +02:00
print >> open ( ' /home/grandlyon/log/cartads.debug ' , ' a+ ' ) , datetime . datetime . now ( ) , " key: " , key , " ; filename: " , filename
content = base64 . b64decode ( attachment . get ( ' content ' ) or ' ' )
#b64_fileContent = attachment.get('content')
2018-03-08 11:50:35 +01:00
#print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "b64_fileContent: ", b64_fileContent
2018-03-29 15:37:56 +02:00
try :
#zf.writestr("Pieces/"+filename, content)
# pour l'instant test en dur avec nom du cerfa pour le CU
if " cerfa " in key :
print >> open ( ' /home/grandlyon/log/cartads.debug ' , ' a+ ' ) , datetime . datetime . now ( ) , " cerfa_nom: " , formdata . values [ ' cerfa_nom ' ]
filenameInZip = formdata . values [ ' cerfa_nom ' ] + " .pdf "
else :
obj , idPiece , codePiece = key . split ( " _ " )
filenameInZip = " Pieces/ " + idPiece + " - " + codePiece
if " . " in filename :
filenameInZip + = filename [ filename . find ( " . " ) : ]
print >> open ( ' /home/grandlyon/log/cartads.debug ' , ' a+ ' ) , datetime . datetime . now ( ) , " filenameInZip: " , filenameInZip
zf . writestr ( filenameInZip , content )
except ValueError as e :
raise ParameterTypeError ( e . message )
zf . close ( )
'''
os . remove ( localFileName )
return
'''
2018-03-08 11:50:35 +01:00
2018-03-29 15:37:56 +02:00
with open ( localFileName , " rb " ) as image_file :
b64_fileContent = base64 . b64encode ( image_file . read ( ) )
2018-03-08 11:50:35 +01:00
2018-03-29 15:37:56 +02:00
#print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "test apres:", b64_fileContent
2018-03-08 11:50:35 +01:00
# test base64
#b64_fileContent = "UEsDBBQAAAAAAMqEQUzI2HQpGgAAABoAAAAKAAAAdGVzdDAxLnR4dHRlc3QgYmFzZTY0IG1pbmkgdG90byB0YXRhUEsBAhQAFAAAAAAAyoRBTMjYdCkaAAAAGgAAAAoAAAAAAAAAAQAgAAAAAAAAAHRlc3QwMS50eHRQSwUGAAAAAAEAAQA4AAAAQgAAAAAA"
# size_max doit etre un multiple de 4 pour avoir un nb de caracteres valide en base 64 (car 3 octets y sont encodes sur 4 caracteres)
2018-03-08 16:12:34 +01:00
size_max = 16777216 # 16 mo, choisi arbitrairement pour ne pas envoyer trop d'un coup en http post
2018-03-08 11:50:35 +01:00
# TODO : mettre en parametre l'url du ws sendfile
for x in range ( 0 , len ( b64_fileContent ) / size_max + 1 ) :
2018-03-29 15:37:56 +02:00
#respHttp = self.requests.post('https://api-rec.grandlyon.com/ads-sendfile-dev/sendfile.aspx',
respHttp = self . requests . post ( ' https://api-rec.grandlyon.com/ads-sendfile-rec/sendfile.aspx ' ,
data = { ' fileName ' : self . dirname + zipFileName ,
2018-03-08 11:50:35 +01:00
' b64_fileContent ' : b64_fileContent [ x * size_max : ( x + 1 ) * size_max ] ,
' part ' : str ( x ) ,
' total ' : int ( len ( b64_fileContent ) / size_max ) }
) #.json()
2018-03-29 15:37:56 +02:00
os . remove ( localFileName )
infos = { " traitementImmediat " : 1 }
#infos = dict(traitementImmediat='1')
#TODO : a voir si on met traitementImmediat a 1
resp = self . get_client ( ) . service . NotifierDepotDossier ( self . get_token_cartads ( ) ,
formdata . values [ ' commune_raw ' ] ,
formdata . values [ ' type_dossier ' ] ,
zipFileName ,
formdata . values [ ' email ' ] ,
self . get_type_compte_utilisateur ( ) ,
' '
)
#return {'data': sudsobject_to_dict(resp), 'respHttp': respHttp.content, 'length': len(b64_fileContent)}
return { ' respHttp ' : respHttp . content , ' length ' : len ( b64_fileContent ) }
2018-03-08 11:50:35 +01:00
@classmethod
def creation_fields ( cls ) :
''' used in cartads_detail.html template '''
return list_schema_fields ( CREATION_SCHEMA )
def get_token_cartads ( self ) :
2018-03-08 16:12:34 +01:00
#TODO : a encoder d'apres les exemples php et c# fournis par GFI
2018-03-08 11:50:35 +01:00
return ' ieTluf0q6vwjitxope55YZ2ud0CEtuO9BBHr2hQaxySeDrz66mntHl83Wqj7oadMSyZqwSkzVdZJrQ92Zg2p3bwkAuv5yUzwmpBfdtAYYLE= '
def get_type_compte_utilisateur ( self ) :
2018-03-08 16:12:34 +01:00
#TODO : a encoder d'apres les exemples php et c# fournis par GFI
2018-03-08 11:50:35 +01:00
return ' ContactService '
@endpoint ( perm = ' can_access ' )
def get_communes ( self , request ) :
resp = self . get_client ( ) . service . GetCommunes ( self . get_token_cartads ( ) ,
self . get_type_compte_utilisateur ( )
)
'''
#TODO: Ca serait mieux mais ca marche pas...
resp = self . get_client ( ) . service . GetCommunes ( {
' token ' : self . get_token_cartads ( ) ,
' typeCompteUtilisateur ' : self . get_type_compte_utilisateur ( ) ,
} ) '''
return { ' data ' : sudsobject_to_dict ( resp ) }
@endpoint ( perm = ' can_access ' )
def get_objets_demande ( self , request , type_dossier ) :
2018-04-05 11:42:17 +02:00
if len ( type_dossier ) != 2 :
raise ParameterTypeError ( " invalid type_dossier parameter " )
2018-03-08 11:50:35 +01:00
resp = self . get_client ( ) . service . GetObjetsDemande ( self . get_token_cartads ( ) , type_dossier )
2018-03-29 15:37:56 +02:00
dict_resp = sudsobject_to_dict ( resp )
out = [ ]
for objet in dict_resp [ ' KeyValueOfintstring ' ] :
out_item = { }
out_item [ ' id ' ] = objet [ " Key " ]
out_item [ ' text ' ] = objet [ " Value " ]
out . append ( out_item )
return { ' data ' : out }
2018-03-08 11:50:35 +01:00
# TODO : parcourir la liste en json devrait etre plus simple qu'en suds, mais j'ai pas reussi pour l'instant
# On parcourt la liste pour la mettre sous la forme id, text, pour l'utiliser comme source de donnees dans le formulaire
out = { }
for key , value in suds . sudsobject . asdict ( resp ) . iteritems ( ) :
out [ key ] = [ ]
for item in value :
if hasattr ( item , ' __keylist__ ' ) :
out_item = { }
for key_item , value_item in suds . sudsobject . asdict ( item ) . iteritems ( ) :
out_item [ ' id ' ] = item . Key
out_item [ ' text ' ] = item . Value
out [ key ] . append ( out_item )
else :
item . id = item . Key
item . text = item . Value
out [ key ] . append ( item )
return { ' data ' : out [ ' KeyValueOfintstring ' ] }
@endpoint ( perm = ' can_access ' )
def get_liste_pdf ( self , request , type_dossier ) :
resp = self . get_client ( ) . service . GetListePdf ( self . get_token_cartads ( ) ,
type_dossier ,
self . get_type_compte_utilisateur ( )
)
return { ' data ' : sudsobject_to_dict ( resp ) }
@endpoint ( perm = ' can_access ' )
def get_pieces ( self , request , type_dossier , objet_demande ) :
resp = self . get_client ( ) . service . GetPieces ( self . get_token_cartads ( ) ,
type_dossier ,
objet_demande
)
return { ' data ' : sudsobject_to_dict ( resp ) }
2018-03-29 15:37:56 +02:00
@endpoint ( perm = ' can_access ' )
def write_wcs_files ( self , request , type_dossier ) :
2018-04-05 11:42:17 +02:00
h = HTMLParser . HTMLParser ( ) # h.unescape(string) decode le html qui vient du web service en unicode : http://fredericiana.com/2010/10/08/decoding-html-entities-to-text-in-python/
output_string = u " <!--#### A inserer apres field ' Un code de suivi de votre demande... ' jusqu ' a la fin des </fields> --> \n "
2018-03-29 15:37:56 +02:00
dict_resp = self . get_objets_demande ( request , type_dossier )
for objet in dict_resp [ ' data ' ] :
#print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "objet: ", objet
#return {'data': dict_resp}
resp = self . get_pieces ( request , type_dossier , objet [ " id " ] )
dict_resp = resp [ " data " ]
#print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "dict_resp: ", dict_resp
date_file = datetime . datetime . now ( )
#TODO: faire un id unique et invariant si on reimporte le form (si ajout de champs dans le BO cartads)
#(par exemple : 1000*objet["id"]+10*no du champ+no de la piece si elle est envoyee 6 fois)
id = 100 * objet [ " id " ]
2018-04-05 11:42:17 +02:00
output_string + = u " <field> \n "
output_string + = u " <label>Pièces à joindre</label> \n "
output_string + = u " <type>page</type> \n "
output_string + = u " <condition> \n "
output_string + = u " <type>python</type> \n "
output_string + = u ' <value>form_var_objet_raw == " ' + str ( objet [ " id " ] ) . decode ( ' utf8 ' ) + u ' " </value> \n '
output_string + = u " </condition><id> " + str ( id ) . decode ( ' utf8 ' ) + u " </id> \n "
2018-03-29 15:37:56 +02:00
id + = 1
2018-04-05 11:42:17 +02:00
output_string + = u " </field><field> \n "
output_string + = u ' <label>[is form_option_type_dossier " foo " ] [end] '
output_string + = u " \n "
output_string + = u ' <span id= " logo_commune " ></span> \n '
output_string + = u " \n "
output_string + = u " \n "
output_string + = u ' <script type= " text/javascript " > \n '
output_string + = u " function slugify(text) \n "
output_string + = u " { \n "
output_string + = u " return text.toString().toLowerCase() \n "
output_string + = u " .replace(/ \ s+/g, ' - ' ) // Replace spaces with - \n "
output_string + = u " .replace(/ \' /g, ' - ' ) // Replace ' with - \n "
output_string + = u " .replace(/ô/g, ' o ' ).replace(/é/g, ' e ' ).replace(/è/g, ' e ' ) // Remove accent \n "
output_string + = u " .replace(/[^ \ w \ -]+/g, ' ' ) // Remove all non-word chars \n "
output_string + = u " .replace(/ \ - \ -+/g, ' - ' ) // Replace multiple - with single - \n "
output_string + = u " .replace(/^-+/, ' ' ) // Trim - from start of text \n "
output_string + = u " .replace(/-+$/, ' ' ); // Trim - from end of text \n "
output_string + = u " } \n "
output_string + = u " function setLogo() \n "
output_string + = u " { \n "
output_string + = u ' var selectedText = " [form_var_commune] " ; \n '
output_string + = u " var communeChanged = slugify(selectedText); \n "
output_string + = u " // Pour enlever arrondissement de Lyon \n "
output_string + = u ' if( selectedText.startsWith( " Lyon " ) ) communeChanged = " lyon " \n '
output_string + = u ' $( " #logo_commune " ).html( \' <img src= " [portal_url]static/grandlyon-gnm/producers/ \' +communeChanged+ \' .png " /> \' ); \n '
output_string + = u " } \n "
output_string + = u " setLogo(); \n "
output_string + = u " </script> \n "
output_string + = u " \n "
output_string + = u " </label> \n "
output_string + = u " <type>comment</type> \n "
output_string + = u " <extra_css_class>imgCommune</extra_css_class> \n "
output_string + = u " <id> " + str ( id ) . decode ( ' utf8 ' ) + u " </id> \n "
2018-03-29 15:37:56 +02:00
id + = 1
2018-04-05 11:42:17 +02:00
output_string + = u " </field><field> \n "
output_string + = u " <label><p><strong>2/ Votre dossier est pr&ecirc;t &agrave; &ecirc;tre d&eacute;pos&eacute;.</strong></p><p>Merci de nous faire parvenir les pi&egrave;ces ci-dessous.</p><p>&nbsp;</p></label> \n "
output_string + = u " <type>comment</type> \n "
output_string + = u " <id> " + str ( id ) . decode ( ' utf8 ' ) + u " </id> \n "
2018-03-29 15:37:56 +02:00
id + = 1
2018-04-05 11:42:17 +02:00
output_string + = u " </field><field> \n "
output_string + = u " <label>Cerfa rempli</label> \n "
output_string + = u " <type>file</type> \n "
output_string + = u " <required>True</required> \n "
output_string + = u " <varname>objet " + str ( objet [ " id " ] ) . decode ( ' utf8 ' ) + u " _cerfa</varname> \n "
output_string + = u " <in_listing>True</in_listing> \n "
output_string + = u " <prefill> \n "
output_string + = u " <type>none</type> \n "
output_string + = u " </prefill><max_file_size>200M</max_file_size> \n "
output_string + = u " <allow_portfolio_picking>True</allow_portfolio_picking> \n "
output_string + = u " <id> " + str ( id ) . decode ( ' utf8 ' ) + u " </id> \n "
2018-03-29 15:37:56 +02:00
id + = 1
2018-04-05 11:42:17 +02:00
output_string + = u " </field> \n "
2018-03-29 15:37:56 +02:00
for piece in dict_resp [ ' Piece ' ] :
#print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "IdPiece: ", piece["IdPiece"]
#for key, value in piece.items():
#print >> open('/home/grandlyon/log/'+type_dossier+'_'+date_file.strftime('%Y%m%d%H%M%S')+'.wcs', 'a+'), key,": ", value
#self.print_wcs_files(type_dossier, date_file, key+": "+str(value))
2018-04-05 11:42:17 +02:00
output_string + = u " <field> \n "
output_string + = u " <label> " + h . unescape ( piece [ " Libelle " ] ) + u " </label> \n "
output_string + = u " <type>file</type> \n "
output_string + = u " <required> " + str ( piece [ " Reglementaire " ] ) . title ( ) . decode ( ' utf8 ' ) + u " </required> \n "
hint = u " "
if piece [ " Descriptif " ] : hint = h . unescape ( piece [ " Descriptif " ] )
output_string + = u " <hint> " + hint + u " </hint> \n "
output_string + = u " <varname>objet " + str ( objet [ " id " ] ) . decode ( ' utf8 ' ) + u " _ " + str ( piece [ " IdPiece " ] ) . decode ( ' utf8 ' ) + u " _ " + str ( piece [ " CodePiece " ] ) . decode ( ' utf8 ' ) + u " </varname> \n "
output_string + = u " <in_listing>False</in_listing> \n "
output_string + = u " <prefill> \n "
output_string + = u " <type>none</type> \n "
output_string + = u " </prefill><max_file_size>50M</max_file_size> \n "
output_string + = u " <allow_portfolio_picking>True</allow_portfolio_picking> \n "
output_string + = u " <id> " + str ( id ) . decode ( ' utf8 ' ) + u " </id> \n "
output_string + = u " </field> \n "
2018-03-29 15:37:56 +02:00
id + = 1
2018-04-05 11:42:17 +02:00
output_string + = u " </fields> \n "
2018-03-29 15:37:56 +02:00
2018-04-05 11:42:17 +02:00
output_string = output_string . encode ( ' ascii ' , errors = ' xmlcharrefreplace ' )
2018-03-29 15:37:56 +02:00
print >> open ( ' /home/grandlyon/log/ ' + type_dossier + ' _ ' + date_file . strftime ( ' % Y % m %d % H % M % S ' ) + ' .wcs ' , ' a+ ' ) , output_string
return { ' data ' : output_string }