| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- '''
- process(xmlpath) : Process principal de Pardit
- * Parse un fichier XML
- * Transforme les données selon les consignes issues des fichiers de configurations pardit.yaml et userdata.yaml
- * Génère un fichier XFDF avec ces données
- * Injecte ce fichier dans le formulaire PDF.
- Retourne le(s) chemin(s) d'accès du/des fichier(s) généré(s), ainsi que la ou les adresses mails des contacts correspondants.
- @author: olivier.massot, sept. 2017
- '''
- import datetime
- import re
- from shutil import SameFileError
- import subprocess
- import tempfile
- from path import Path
- from core import config
- from core.constants import TEMPLATE_PATH
- from core.outlook import Mail
- from core.pdfform import fill_form, gen_xfdf
- from core.xmlparser import parse
- class XmlFileError(Exception):
- pass
- class ProcessNotNeeded(Exception):
- pass
- class MissingValue(ValueError):
- """ erreur levée par les fonctions additionnelles lorsque la valeur manque """
- pass
- # *************** METHODES SPECIALES DU FICHIER DE CONFIG ***************
- def _parsedate(datestring):
- """ parse a string to a datetime """
- if not datestring:
- raise MissingValue
- return datetime.datetime.strptime(datestring[:10], "%Y-%m-%d")
- def _day(datestring):
- """ extrait le jour 'dd' d'une datestring ('dd/mm/yyyy') """
- return _parsedate(datestring).strftime("%d")
- def _month(datestring):
- """ extrait le mois 'mm' d'une datestring ('dd/mm/yyyy') """
- return _parsedate(datestring).strftime("%m")
- def _year(datestring):
- """ extrait l'année 'yyyy' d'une datestring ('dd/mm/yyyy') """
- return _parsedate(datestring).strftime("%Y")
- def _now(*args): # @UnusedVariable
- """ retourne la date du jour ('dd/mm/yyyy') """
- # return datestring: dd/mm/yyyy
- return datetime.date.today().strftime("%Y-%m-%d")
- _FUNCTIONS = {"JOUR": _day,
- "MOIS": _month,
- "ANNEE": _year,
- "MAINTENANT": _now}
- # *************** FONCTIONS DE TRAITEMENT DES DONNEES ***************
- def process(xmlpath):
- """ Traite les données XML au moyen du fichier de configuration
- pour générer le ou les Pdf de réponse """
- # Chemin d'accès au fichier de données
- xmlpath = Path(xmlpath)
- # Détermine et créé le répertoire de sortie
- output_dir = Path(config.get("repertoire_sortie")).abspath() / xmlpath.namebase
- output_dir.makedirs_p()
- # Parse une premiere fois les données du fichier XML
- xmldata = parse(xmlpath)
- # Determine le type de demande (dt, dict, conjointe)
- first_nodes = {node.split(".")[0] for node in xmldata}
- if "dtDictConjointes" in first_nodes:
- if xmldata.get("dtDictConjointes.partieDT.souhaitsPourLeRecepisse.souhaiteRecevoirLeRecepisse", "") == "true":
- a_traiter = {"dict": parse(xmlpath, "partieDICT"), "dt": parse(xmlpath, "partieDT")}
- else:
- a_traiter = {"dict": parse(xmlpath, "partieDICT")}
- elif "DT" in first_nodes:
- a_traiter = {"dt": parse(xmlpath, "DT")}
- elif "DICT" in first_nodes:
- a_traiter = {"dict": parse(xmlpath, "DICT")}
- else:
- raise XmlFileError("Le format du fichier XML n'est pas reconnu")
- # Traite la ou les demandes de réponse
- outputfiles = []
- mails = []
- for doctype, xmldata in a_traiter.items():
- # Construit le modele de reponse
- modele = {}
- modele.update(config.get("donnees", "commun")) # Donnees communes à toutes les réponses
- modele.update(config.get("donnees", doctype)) # Données spécifique au type de réponse
- # inject_data remplace les <...> par les valeurs des champs correspondants
- # eval_ parse et évalue les éventuelles fonctions de l'expression
- reponse = {field: eval_(inject_data(str(value), xmldata)) for field, value in modele.items()}
- # Patch 1: on coche la case 'demande conjointe' lorsque la demande est conjointe
- if "dtDictConjointes" in first_nodes:
- reponse["Recepisse_DT"] = "Non"
- reponse["Recepisse_DICT"] = "Non"
- reponse["Recepisse_DC"] = "Oui"
- outputname = output_dir / "Recepisse_{}.pdf".format(doctype.upper())
- make_pdf(reponse, outputname, keep_xfdf=True)
- outputfiles.append(outputname)
- if doctype == "dict" or xmldata.get("souhaitsPourLeRecepisse.souhaiteRecevoirLeRecepisse") == "true":
- contact = eval_(inject_data(config.get("mail", doctype, "dest"), xmldata))
- subject = eval_(inject_data(config.get("mail", doctype, "objet"), xmldata))
- content = eval_(inject_data(config.get("mail", doctype, "texte"), xmldata))
- mail = Mail(contact, subject, content)
- if not contact in [mail.to for mail in mails]:
- mails.append(mail)
- with open(output_dir / "contact.txt", "w+") as f:
- f.write("\n".join([mail.to for mail in mails]))
- # Patch 2: en cas de demande conjointe, et si le demandeur DT est le même que le demandeur DICT, on ne joint qu'un seul document au mail.
- # On supprime le recepisse DT, et on renomme le recepisse DICT en recepisse DC
- if len(mails) == 1 and len(outputfiles) > 1:
- rec_dict, rec_dt = outputfiles
- rec_dc = rec_dict.parent / "Recepisse_DC.pdf"
- rec_dc.remove_p()
- rec_dict.rename(rec_dc)
- rec_dt.remove_p()
- outputfiles = [rec_dc]
- # Créé une svg du fichier XML traité
- try:
- xmlpath.copy(output_dir)
- except SameFileError:
- pass
- return outputfiles, mails
- def inject_data(value, xmldata):
- """ injecte les données issues du fichier XML parsé
- dans la valeur du champ. """
- def _get(matchobj):
- key = matchobj.group(1)
- return xmldata.get(key, "")
- value = re.sub(r"<([^<>]*)>", _get, value)
- return value
- def eval_(value):
- """ applique les éventuelles fonctions à la valeur du champ """
- value = str(value) if value != None else ""
- for name, fct in _FUNCTIONS.items():
- match = re.search(r"{}\((.*)\)".format(name), str(value))
- if match:
- value = match.group(1) if match.group(1) else ''
- value = eval_(value)
- try:
- value = fct(value)
- except MissingValue:
- value = ""
- return value
- def make_pdf(data, outputname, keep_xfdf=False):
- """ injecte les données 'data' dans le Pdf modèle """
- with tempfile.TemporaryDirectory() as tmpdir:
- tmpdir = Path(tmpdir)
- tmpform = TEMPLATE_PATH.copy(tmpdir) # @UndefinedVariable
- filledform = tmpdir / "filled.pdf"
- xfdf = gen_xfdf(tmpdir / "data.fdf", data)
- try:
- fill_form(tmpform, xfdf, filledform, flatten=False)
- except subprocess.CalledProcessError:
- print("Erreur lors de l'écriture du PDF, assurez-vous qu'il n'est pas ouvert")
- return
- if outputname.isfile():
- outputname.remove()
- filledform.move(outputname)
- if keep_xfdf:
- xfdf.copy(outputname.parent)
|