# coding: utf8 # 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 . import base64 import json import datetime import uuid import zipfile import StringIO import HTMLParser import os # TODO: inutile si on genere le zip in-memory 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 #dirname = 'D:\LSM\Gfi\DepotDossierFTP\client1\\' #dev cartads dirname = 'E:\LSM\Gfi\DepotDossierFTP\client1\\' #recette cartads 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 %ss)', 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() ''' 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 "" 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("", "traitementImmediat1") ''' 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): # 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, ) # get creation fields from payload try: formdata = FormData(json.loads(request.body), CREATION_SCHEMA) except ValueError as e: raise ParameterTypeError(e.message) #print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "commune_raw=", formdata.values['commune_raw'] #return if formdata.attachments: print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "nb attach: ", len(formdata.attachments) for key, attachment in formdata.attachments.items(): filename = attachment.get('filename') or 'file%s.bin' % num 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') #print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "b64_fileContent: ", b64_fileContent 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 ''' with open(localFileName, "rb") as image_file: b64_fileContent = base64.b64encode(image_file.read()) #print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "test apres:", b64_fileContent # 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) size_max = 16777216 # 16 mo, choisi arbitrairement pour ne pas envoyer trop d'un coup en http post # TODO : mettre en parametre l'url du ws sendfile for x in range(0, len(b64_fileContent)/size_max + 1): #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, 'b64_fileContent': b64_fileContent[x*size_max:(x+1)*size_max], 'part': str(x), 'total': int(len(b64_fileContent)/size_max)} ) #.json() 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)} @classmethod def creation_fields(cls): '''used in cartads_detail.html template''' return list_schema_fields(CREATION_SCHEMA) def get_token_cartads(self): #TODO : a encoder d'apres les exemples php et c# fournis par GFI return 'ieTluf0q6vwjitxope55YZ2ud0CEtuO9BBHr2hQaxySeDrz66mntHl83Wqj7oadMSyZqwSkzVdZJrQ92Zg2p3bwkAuv5yUzwmpBfdtAYYLE=' def get_type_compte_utilisateur(self): #TODO : a encoder d'apres les exemples php et c# fournis par GFI 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): if len(type_dossier) != 2: raise ParameterTypeError("invalid type_dossier parameter") resp = self.get_client().service.GetObjetsDemande(self.get_token_cartads(), type_dossier) 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} # 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)} @endpoint(perm='can_access') def write_wcs_files(self, request, type_dossier): 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"\n" 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"] output_string += u"\n" output_string += u" \n" output_string += u" page\n" output_string += u" \n" output_string += u" python\n" output_string += u' form_var_objet_raw == "'+str(objet["id"]).decode('utf8')+u'"\n' output_string += u" "+str(id).decode('utf8')+u"\n" id += 1 output_string += u" \n" output_string += u' \n" output_string += u" comment\n" output_string += u" imgCommune\n" output_string += u" "+str(id).decode('utf8')+u"\n" id += 1 output_string += u" \n" output_string += u" \n" output_string += u" comment\n" output_string += u" "+str(id).decode('utf8')+u"\n" id += 1 output_string += u" \n" output_string += u" \n" output_string += u" file\n" output_string += u" True\n" output_string += u" objet"+str(objet["id"]).decode('utf8')+u"_cerfa\n" output_string += u" True\n" output_string += u" \n" output_string += u" none\n" output_string += u" 200M\n" output_string += u" True\n" output_string += u" "+str(id).decode('utf8')+u"\n" id += 1 output_string += u" \n" 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)) output_string += u"\n" output_string += u" \n" output_string += u" file\n" output_string += u" "+str(piece["Reglementaire"]).title().decode('utf8')+u"\n" hint = u"" if piece["Descriptif"]: hint = h.unescape(piece["Descriptif"]) output_string += u" "+hint+u"\n" output_string += u" objet"+str(objet["id"]).decode('utf8')+u"_"+str(piece["IdPiece"]).decode('utf8')+u"_"+str(piece["CodePiece"]).decode('utf8')+u"\n" output_string += u" False\n" output_string += u" \n" output_string += u" none\n" output_string += u" 50M\n" output_string += u" True\n" output_string += u" "+str(id).decode('utf8')+u"\n" output_string += u"\n" id += 1 output_string += u" \n" output_string = output_string.encode('ascii', errors='xmlcharrefreplace') print >> open('/home/grandlyon/log/'+type_dossier+'_'+date_file.strftime('%Y%m%d%H%M%S')+'.wcs', 'a+'), output_string return {'data': output_string}