This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
passerelle-grandlyon-cartads/cartads/models.py

452 lines
22 KiB
Python

# 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 <http://www.gnu.org/licenses/>.
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 "<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>")
'''
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"<!--#### A inserer apres field 'Un code de suivi de votre demande...' jusqu'a la fin des </fields> -->\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"<field>\n"
output_string += u" <label>Pi&#232;ces &#224; 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"
id += 1
output_string += u" </field><field>\n"
output_string += u' <label>[is form_option_type_dossier "foo"] [end]'
output_string += u"\n"
output_string += u'&lt;span id="logo_commune"&gt;&lt;/span&gt;\n'
output_string += u"\n"
output_string += u"\n"
output_string += u'&lt;script type="text/javascript"&gt;\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(/&#244;/g, 'o').replace(/&#233;/g, 'e').replace(/&#232;/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(\'&lt;img src="[portal_url]static/grandlyon-gnm/producers/\'+communeChanged+\'.png" /&gt;\');\n'
output_string += u"}\n"
output_string += u"setLogo();\n"
output_string += u"&lt;/script&gt;\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"
id += 1
output_string += u" </field><field>\n"
output_string += u" <label>&lt;p&gt;&lt;strong&gt;2/ Votre dossier est pr&amp;ecirc;t &amp;agrave; &amp;ecirc;tre d&amp;eacute;pos&amp;eacute;.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Merci de nous faire parvenir les pi&amp;egrave;ces ci-dessous.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</label>\n"
output_string += u" <type>comment</type>\n"
output_string += u" <id>"+str(id).decode('utf8')+u"</id>\n"
id += 1
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"
id += 1
output_string += u" </field>\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"<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"
id += 1
output_string += u" </fields>\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}