Version d'initialisation

This commit is contained in:
Etienne Loupias 2018-03-08 11:50:35 +01:00
commit 67bd9619b6
22 changed files with 518 additions and 0 deletions

10
CartADS.egg-info/PKG-INFO Normal file
View File

@ -0,0 +1,10 @@
Metadata-Version: 1.0
Name: CartADS
Version: 0.0.0
Summary: UNKNOWN
Home-page: http://example.net/
Author: Grand Lyon
Author-email: toto@example.net
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN

View File

@ -0,0 +1,8 @@
README
setup.py
CartADS.egg-info/PKG-INFO
CartADS.egg-info/SOURCES.txt
CartADS.egg-info/dependency_links.txt
CartADS.egg-info/top_level.txt
cartads/__init__.py
cartads/models.py

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
cartads

0
README Normal file
View File

0
cartads/__init__.py Normal file
View File

BIN
cartads/__init__.pyc Normal file

Binary file not shown.

81
cartads/formdata.py Normal file
View File

@ -0,0 +1,81 @@
# 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/>.
from datetime import datetime
def is_required(value):
if not value:
raise ValueError('is required')
return value
def to_datetime(value):
if not value:
return
return datetime.strptime(value[:19], '%Y-%m-%dT%H:%M:%S')
def default_to_now(value):
if not value:
return datetime.now()
return value
CREATION_SCHEMA = (
)
def list_schema_fields(schema):
for fieldname in schema:
yield fieldname[0] if isinstance(fieldname, tuple) else fieldname
class FormData(object):
def __init__(self, formdata, schema):
if not isinstance(formdata, dict):
raise ValueError('formdata must be a dict')
if 'fields' in formdata and isinstance(formdata['fields'], dict):
values = formdata['fields']
if 'extra' in formdata:
values.update(formdata['extra'])
else:
values = formdata
# extract/create/validate fields according to schema
self.fields = {}
for fieldname in schema:
if isinstance(fieldname, tuple):
value = values.get(fieldname[0])
for modifier in fieldname[1:]:
try:
value = modifier(value)
except ValueError as e:
raise ValueError('%s: %s' % (fieldname[0], e.message))
fieldname = fieldname[0]
else:
value = values.get(fieldname)
if value is not None:
self.fields[fieldname] = value
# extract attachments
self.attachments = []
attachments = {
key: value
for key, value in values.items()
if isinstance(value, dict) and ('filename' in value and
'content_type' in value and
'content' in value)
}
for key in sorted(attachments.keys()):
self.attachments.append(attachments[key])

BIN
cartads/formdata.pyc Normal file

Binary file not shown.

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0007_auto_20180129_1355'),
]
operations = [
migrations.CreateModel(
name='CartADS',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('title', models.CharField(max_length=50, verbose_name='Title')),
('description', models.TextField(verbose_name='Description')),
('slug', models.SlugField(unique=True)),
('log_level', models.CharField(default=b'INFO', max_length=10, verbose_name='Log Level', choices=[(b'NOTSET', b'NOTSET'), (b'DEBUG', b'DEBUG'), (b'INFO', b'INFO'), (b'WARNING', b'WARNING'), (b'ERROR', b'ERROR'), (b'CRITICAL', b'CRITICAL'), (b'FATAL', b'FATAL')])),
('users', models.ManyToManyField(to='base.ApiUser', blank=True)),
],
options={
'verbose_name': 'Webservice CartADS',
},
),
]

Binary file not shown.

View File

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cartads', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='cartads',
name='application',
field=models.CharField(default='CartADS', max_length=200, verbose_name='Application identifier'),
preserve_default=False,
),
migrations.AddField(
model_name='cartads',
name='token_authorization',
field=models.CharField(default='', max_length=128, verbose_name='Token Authorization'),
preserve_default=False,
),
migrations.AddField(
model_name='cartads',
name='token_url',
field=models.URLField(default='', max_length=256, verbose_name='Token URL'),
preserve_default=False,
),
migrations.AddField(
model_name='cartads',
name='verify_cert',
field=models.BooleanField(default=True, verbose_name='Check HTTPS Certificate validity'),
),
migrations.AddField(
model_name='cartads',
name='wsdl_url',
field=models.CharField(default='', max_length=256, verbose_name='WSDL URL'),
preserve_default=False,
),
]

Binary file not shown.

View File

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cartads', '0002_auto_20180129_1403'),
]
operations = [
migrations.AlterModelOptions(
name='cartads',
options={'verbose_name': 'CartADS Webservices'},
),
]

Binary file not shown.

View File

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cartads', '0003_auto_20180129_1405'),
]
operations = [
migrations.RemoveField(
model_name='cartads',
name='application',
),
]

Binary file not shown.

View File

Binary file not shown.

299
cartads/models.py Normal file
View File

@ -0,0 +1,299 @@
# 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
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\\'
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")
# a priori attachement inutile pour cartads, a voir si on supprime
if self.attachments:
# SOAP Attachement format
message = MIMEMultipart('related', type="text/xml",
start="<rootpart@entrouvert.org>")
xml = MIMEText(None, _subtype='xml', _charset='utf-8')
xml.add_header('Content-ID', '<rootpart@entrouvert.org>')
# do not base64-encode the soap message
xml.replace_header('Content-Transfer-Encoding', '8bit')
xml_payload = request.message
# hack payload to include attachment filenames in
# SOAP-ENV:Header.
soap_headers = []
for num, attachment in enumerate(self.attachments):
filename = attachment.get('filename') or 'file%s.bin' % num
if isinstance(filename, unicode):
filename = filename.encode('utf-8', 'ignore')
soap_headers.append('<filename%s>%s</filename%s>' % (num, filename, num))
xml_payload = xml_payload.replace('<SOAP-ENV:Header/>',
'<SOAP-ENV:Header>%s</SOAP-ENV:Header>' % ''.join(soap_headers))
xml.set_payload(xml_payload)
message.attach(xml)
for num, attachment in enumerate(self.attachments):
filename = attachment.get('filename') or 'file%s.bin' % num
content = base64.b64decode(attachment.get('content') or '')
content_type = attachment.get('content_type') or 'application/octet-stream'
maintype, subtype = content_type.split('/', 1)
if maintype == 'text':
part = MIMEText(content, _subtype=subtype)
else:
part = MIMEBase(maintype, subtype, name=filename)
part.set_payload(content)
part.add_header('Content-Transfer-Encoding', 'binary')
encoders.encode_noop(part)
part.add_header('Content-Disposition', 'attachment', name=filename, filename=filename)
part.add_header('Content-ID', '<%s>' % filename)
message.attach(part)
message._write_headers = lambda x: None
msg_x = message.as_string(unixfrom=False)
# RFC 2045 defines MIME multipart boundaries:
# * boundary := 0*69<bchars> bcharsnospace
# * dash-boundary := "--" boundary
# * delimiter := CRLF dash-boundary
# but Python doesn't use CRLF, will only use LF (on Unix systems
# at least). This is http://bugs.python.org/issue1349106 and has
# been fixed in Python 3.2.
#
# Manually hack message to put \r\n so that the message is
# correctly read by Apache Axis strict parser.
boundary = message.get_boundary()
request.message = message.as_string(unixfrom=False
).replace(boundary + '\n', boundary + '\r\n'
).replace('\n--' + boundary, '\r\n--' + boundary)
request.headers.update(dict(message._headers))
request.headers['Authorization'] = self.instance.get_token()
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):
# get creation fields from payload
try:
formdata = FormData(json.loads(request.body), CREATION_SCHEMA)
except ValueError as e:
raise ParameterTypeError(e.message)
if formdata.attachments:
print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "nb attach: ", len(formdata.attachments)
for num, attachment in enumerate(formdata.attachments):
filename = attachment.get('filename') or 'file%s.bin' % num
print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "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
# Pour l'instant pour test, on envoie le dernier fichier du formulaires
# TODO : construire le zip de tous les fichiers du form
# Nom du fichier zip a envoyer
fileName = str(uuid.uuid4()) + ".zip";
# 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 choisi pour eviter erreur OpenSSL.SSL.WantWriteError due au openssl socket send buffer size (https://pyopenssl.org/en/stable/api/ssl.html)
# TODO: demander a EO si on peut l'augmenter, car en curl on peut envoyer plus de 7 Mo d'un coup d'apres nos tests
size_max = 65536 # 64 ko, d'apres test semble etre la limite par defaut du openssl socket send buffer size
# TODO : mettre en parametre l'url du ws sendfile
for x in range(0, len(b64_fileContent)/size_max + 1):
resp = self.requests.post('https://api-rec.grandlyon.com/ads-sendfile-dev/sendfile.aspx',
data={'fileName': self.dirname+fileName,
'b64_fileContent': b64_fileContent[x*size_max:(x+1)*size_max],
'part': str(x),
'total': int(len(b64_fileContent)/size_max)}
) #.json()
return {'data': resp.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# fournispar GFI
return 'ieTluf0q6vwjitxope55YZ2ud0CEtuO9BBHr2hQaxySeDrz66mntHl83Wqj7oadMSyZqwSkzVdZJrQ92Zg2p3bwkAuv5yUzwmpBfdtAYYLE='
def get_type_compte_utilisateur(self):
#TODO : a encoder d'apres les exemples php et c# fournispar 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):
resp = self.get_client().service.GetObjetsDemande(self.get_token_cartads(), type_dossier)
# 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)}

BIN
cartads/models.pyc Normal file

Binary file not shown.

11
setup.py Normal file
View File

@ -0,0 +1,11 @@
#! /usr/bin/env python
from setuptools import setup, find_packages
setup(
name='CartADS',
author='Grand Lyon',
author_email='toto@example.net',
url='http://example.net/',
packages=find_packages(),
)