From 0e93b8430e06b8cf77f057a0f7332a560f5cd2b0 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 11 Jun 2019 17:19:23 +0200 Subject: [PATCH] sp_fr: implement response flow (#33838) --- passerelle/apps/sp_fr/models.py | 131 +++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 4 deletions(-) diff --git a/passerelle/apps/sp_fr/models.py b/passerelle/apps/sp_fr/models.py index b3048d3e..8304e812 100644 --- a/passerelle/apps/sp_fr/models.py +++ b/passerelle/apps/sp_fr/models.py @@ -23,6 +23,7 @@ import collections import base64 import datetime import unicodedata +import uuid from lxml import etree as ET @@ -170,16 +171,20 @@ class Resource(BaseResource): try: move, error = handler() except Exception: + count -= 1 self.logger.exception('handling of file "%s" failed', filename) - # sftp.rename(filename, 'FAILED/' + filename) + sftp.rename(filename, 'FAILED/' + filename) else: if move and error: + count -= 1 self.logger.error('handling of file "%s" failed: %s', filename, error) # sftp.rename(filename, 'FAILED/' + filename) else: if error: + count -= 1 self.logger.warning('handling of file "%s" failed: %s', filename, error) elif move: + count -= 1 sftp.rename(filename, 'DONE/' + filename) if not count: break @@ -226,7 +231,8 @@ class Resource(BaseResource): return False, 'error during response to service-public.fr %r' % e self.request.state = Request.STATE_RETURNED self.request.save() - return True, None + self.resource.logger.info('%s responded, closed', self.request.filename) + return False, None def process(self, fd): try: @@ -330,7 +336,18 @@ class Resource(BaseResource): return submitter.result.backoffice_url def response(self): - raise NotImplementedError + with self.resource.output_sftp.client() as client: + with client.open(self.request.response_zip_filename, mode='w') as fd: + self.request.build_response_zip( + fd, + etat='100', + commentaire=u'Demande transmise à la collectivité') + with self.resource.input_sftp.client() as client: + with client.open('DONE/' + self.request.response_zip_filename, mode='w') as fd: + self.request.build_response_zip( + fd, + etat='100', + commentaire=u'Demande transmise à la collectivité') def get_data(self, data, name): # prevent error in manual mapping @@ -627,7 +644,7 @@ class Request(models.Model): STATES = [ (STATE_RECEIVED, _('Received')), (STATE_TRANSFERED, _('Transfered')), - (STATE_ERROR, _('Transfered')), + (STATE_ERROR, _('Error')), (STATE_RETURNED, _('Returned')), ] @@ -668,6 +685,112 @@ class Request(models.Model): self.resource.logger.error('could not delete %s', self.archive) return super(Request, self).delete(*args, **kwargs) + @property + def message_xml(self): + # FileField can be closed, or open, you never know, and used as a + # contextmanager, __enter__ does not re-open/re-seek(0) it :/ + self.archive.open() + + with self.archive as fd: + with zipfile.ZipFile(fd) as archive: + with archive.open('message.xml') as message_xml_fd: + s = message_xml_fd.read() + return ET.fromstring(s) + + @property + def id_enveloppe(self): + message_xml = self.message_xml + ns = { + 'pec': 'http://finances.gouv.fr/dgme/pec/message/v1', + 'mdel': 'http://finances.gouv.fr/dgme/gf/composants/teledemarchexml/donnee/metier' + } + return message_xml.find('.//{%(pec)s}MessageId' % ns).text.split()[1] + + def build_message_xml_retour(self, etat, commentaire): + message_xml = self.message_xml + + ns = { + 'pec': 'http://finances.gouv.fr/dgme/pec/message/v1', + 'mdel': 'http://finances.gouv.fr/dgme/gf/composants/teledemarchexml/donnee/metier' + } + + template = ''' + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + +''' # NOQA E501 + + response = ET.XML(template) + + message_id = message_xml.find('.//{%(pec)s}MessageId' % ns).text + response.find('.//{%(pec)s}MessageId' % ns).text = 'RET-1-' + message_id # str(uuid.uuid4().hex) + response.find('.//{%(pec)s}RefToMessageId' % ns).text = message_id + response.find('.//{%(pec)s}FlowType' % ns).text = message_xml.find('.//{%(pec)s}FlowType' % ns).text + response.find('.//{%(pec)s}Sender' % ns).extend( + message_xml.find('.//{%(pec)s}Recipient' % ns)) + response.find('.//{%(pec)s}Recipient' % ns).extend( + message_xml.find('.//{%(pec)s}Sender' % ns)) + + response.find('.//{%(pec)s}FlowType' % ns).text = message_xml.find('.//{%(pec)s}FlowType' % ns).text + + # Strangely the same node in the response does not have the same + # namespace as the node in the request, whatever... + response.find('.//{%(pec)s}NumeroTeledemarche' % ns).text = message_xml.find( + './/{%(mdel)s}NumeroTeledemarche' % ns).text + response.find('.//{%(pec)s}MotDePasse' % ns).text = message_xml.find('.//{%(mdel)s}MotDePasse' % ns).text + response.find('.//{%(pec)s}Etat' % ns).text = '100' + response.find('.//{%(pec)s}Commentaire' % ns).text = u'Dossier transmis à la collectivité' + return response + + def build_response_zip(self, fd_or_filename, etat, commentaire): + with zipfile.ZipFile(fd_or_filename, 'w') as archive: + message_xml = self.build_message_xml_retour( + etat=etat, commentaire=commentaire) + archive.writestr('message.xml', + '' + + ET.tostring(message_xml, encoding='utf-8')) + + @property + def response_zip_filename(self): + m = FILE_PATTERN.match(self.filename) + + numero_teledossier = m.group('identifier') + code_demarche = m.group('procedure') + id_enveloppe = self.id_enveloppe + numero_sequence = '1' + + return '%s-%s-%s-%s.zip' % ( + numero_teledossier, + code_demarche, + id_enveloppe, + numero_sequence) + class Meta: verbose_name = _('MDEL request') verbose_name_plural = _('MDEL requests') -- 2.20.1