Files
dumas_gestion/mondumas/views/dossier.py

1360 lines
54 KiB
Python

# -*- coding: utf8 -*-
from pyramid.response import Response
from pyramid.renderers import render, get_renderer
from pyramid.view import (
view_config,
forbidden_view_config,
)
from pyramid.security import (
remember,
forget,
)
from pyramid.httpexceptions import (
HTTPFound,
HTTPNotFound,
HTTPForbidden,
)
from pyramid_mailer import get_mailer
from pyramid_mailer.message import Message, Attachment
from datetime import *
from sqlalchemy.exc import DBAPIError
from ..security import groupfinder
from PIL import Image
import os
import io
import shutil
import pdfkit
import imaplib
import base64
import email
from pdfminer3.layout import LAParams, LTTextBox, LTTextLine
from pdfminer3.pdfpage import PDFPage
from pdfminer3.pdfparser import PDFParser
from pdfminer3.pdfdocument import PDFDocument
from pdfminer3.pdfdevice import PDFDevice
from pdfminer3.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer3.converter import PDFPageAggregator
from ..views.default import *
from ..models.default import *
from ..models.dossier import *
@view_config(route_name='dossier_lookup', renderer='../templates/dossier/dossier_lookup.pt', permission='view')
@view_config(route_name='dossier_select', renderer='../templates/dossier/dossier_lookup.pt', permission='view')
def dossier_lookup(request):
if 'dossier_select' in request.current_route_path() :
# récupérer les paramètres de l'appel de la view
datePlan = request.matchdict['date']
# sélectionner dossier -> goto planning
goto_url = '/dossier_selected/agenda/%s/' % datePlan
url = request.route_url('dossier_select', date=datePlan)
else:
# recherche dossier -> goto fiche dossier
goto_url = '/dossier_selected/dossier_view/%s/' % date.today().strftime('%Y-%m-%d')
url = request.route_url('dossier_lookup')
logged_in = request.authenticated_userid.upper()
message = ''
member = get_member_by_id(request, logged_in)
societe_defaut = member.societe
societe = societe_defaut
access_defaut = member.access
liste=[]
name = ''
cb_tous = "non"
if 'form.submitted' in request.params:
name = request.params['name']
societe = request.params['societe']
# lire les chantiers
chantiers = get_chantiers_byName(request, societe, name)
if len(chantiers) == 0:
message = "Chantier non trouvé : %s" % name
# construire la liste
for item in chantiers:
d = ('%s-%s' % (societe, item.numero),item.date.strftime('%d-%m-%Y'), item.nomcli, item.chantier, to_euro(item.montant),
item.nosin, item.status, item.usermaj)
liste.append(d)
if len(name) == 0 :
order_option = 'desc'
else:
order_option = 'asc'
return {
'page_title': "Rechercher un chantier",
'url': url,
'goto_url': goto_url,
'message': message,
'dt_data': json.dumps(liste),
'societe': societe,
'name': name,
'order_option': order_option,
}
@view_config(route_name='dossier_view', renderer='../templates/dossier/dossier_view.pt', permission='view')
def dossier_view(request):
nodossier = request.matchdict['nodossier']
societe = nodossier[0:2]
logged_in = request.authenticated_userid.upper()
# lire son niveau d'accès
member = get_member_by_id(request, logged_in)
access = member.access
url = request.route_url("dossier_view", nodossier=nodossier)
dossier = get_dossier_by_no(request, nodossier)
if dossier is None:
request.session.flash(u"Le dossier no %s est introuvable" % (nodossier), 'danger')
return HTTPFound(location=request.route_url("dossier_lookup"))
# lire tous le suivi du dossier
details = get_dossier_rdv_by_no(request, nodossier, '0')
# lire toutes les dossiers du chantiers
documents = get_documents_byChantier(request, nodossier)
# lire toutes les dossiers similaires
similaires = get_similaires_byChantier(request, dossier.societe, dossier.C_NOM, dossier.C_ADR, dossier.C_CP, dossier.C_VILLE)
# lire tous les documents attachés
docs_attaches = get_docs_attaches(request, nodossier, 0, 0, '')
if nodossier.startswith('PL'):
# lire rapport de rdf
rapports = get_rapport_by_no(request, nodossier, '')
else:
rapports = []
# select background color according to society
bg_color = "bg-%s" % societe
return {
'page_title': "Dossier : %s" % (nodossier),
'nodossier': nodossier,
'dossier': dossier,
'details': details,
'rapports': rapports,
'documents': documents,
'similaires': similaires,
'docs_attaches': docs_attaches,
'docs_url': request.static_url(request.registry.settings['mondumas.devfac_url']),
'bg_color': bg_color,
'access': access,
}
@view_config(route_name='devis_view', renderer='../templates/dossier/devis_view.pt', permission='view')
def devis_view(request):
nodossier = request.matchdict['nodossier']
societe = nodossier[0:2]
url = request.route_url("devis_view", nodossier=nodossier)
type_doc = nodossier[3:5]
if type_doc == 'DE':
page_title = "Devis no : %s" % (nodossier)
elif type_doc == 'FA':
page_title = "Facture no : %s" % (nodossier)
else:
page_title = "Proforma no : %s" % (nodossier)
dossier = get_devis_by_no(request, nodossier)
if dossier is None:
request.session.flash(u"Le document no %s est introuvable" % (nodossier), 'danger')
return HTTPFound(location=request.route_url("dossier_lookup"))
# lire tous les lignes du devis
details = get_devis_lig_by_no(request, nodossier)
# select background color according to society
bg_color = "bg-%s" % societe
return {
'page_title': page_title,
'nodossier': nodossier,
'dossier': dossier,
'details': details,
'bg_color': bg_color,
}
@view_config(route_name='dossier_selected', permission='view')
def dossier_selected(request):
# récupérer les paramètres de l'appel de la view
goto = request.matchdict['goto']
datePlan = request.matchdict['date']
nodossier = request.matchdict['nodossier']
# fiche dossier
dossier = get_dossier_by_no(request, nodossier)
if goto == 'dossier_view':
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
else:
return HTTPFound(location=request.route_url('agenda', date=datePlan))
@view_config(route_name='suivi_edit', renderer='../templates/dossier/suivi_edit.pt', permission='view')
def suivi_edit(request):
logged_in = request.authenticated_userid.upper()
nodossier = request.matchdict['nodossier']
nolig = request.matchdict['nolig']
url = request.route_url("suivi_edit", nodossier=nodossier, nolig=nolig)
message = ''
if nolig == '0':
# nouveau
suivi = {}
suivi['attached_text'] = ''
suivi['COMMENT'] = ''
suivi['USERMAJ'] = logged_in
suivi['DATEMAJ'] = datetime.now()
page_title= 'Nouveau suivi'
else:
# lire le suivi
suivi = get_dossier_rdv_by_no(request, nodossier, nolig)
if not suivi:
request.session.flash(u"Suivi non trouvé : %s" % nodossier, 'warning')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
page_title= "Modification du suivi %s" % (nolig)
if 'form.submitted' in request.params:
new_values = {}
for param, db_value in suivi.items():
if param in request.params and request.params[param] != db_value:
new_values[param] = request.params[param]
if new_values:
new_values['USERMAJ'] = logged_in
new_values['DATE'] = date.today()
update_suivi(request, nodossier, nolig, new_values)
request.session.flash(u"Le suivi a été mis à jour avec succès.", 'success')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
if 'form.deleted' in request.params:
delete_rdv(request, nodossier, nolig)
request.session.flash(u"Le suivi a été supprimé avec succès.", 'success')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
return {
'page_title': page_title,
'logged_in': logged_in,
'url': url,
'nodossier': nodossier,
'nolig': nolig,
'suivi': suivi,
'message': message,
}
@view_config(route_name='dossier_edit', renderer='../templates/dossier/dossier_edit.pt', permission='view')
def dossier_edit(request):
logged_in = request.authenticated_userid
nodossier = request.matchdict['nodossier']
url = request.route_url('dossier_edit', nodossier=nodossier)
message = ''
dossier = get_dossier_by_no(request, nodossier)
if not dossier:
request.session.flash(u"Le dossier no %s est introuvable" % (nodossier), 'danger')
return HTTPFound(location=request.route_url('dossier_lookup'))
code_postal = '%s - %s' % (dossier.C_CP, dossier.C_VILLE)
# lire table expert
experts = get_experts(request, dossier.CABINET, 0)
if 'form.submitted' in request.params:
new_values = {}
for param, db_value in dossier.items():
if param in request.params and request.params[param] != db_value:
new_values[param] = request.params[param]
# controle saisie code postal
code_postal = request.params['code_postal']
cp = code_postal.split(' - ')
cp = code_postal.split(' - ')
if len(cp) < 2:
message = 'Code postal invalide. Veuillez sélectionner un parmi la liste.'
else:
new_values['c_cp'] = cp[0]
new_values['c_ville'] = cp[1]
new_values['USERMAJ'] = logged_in.upper()
# ascenseur coché ?
if 'ascenseur' in request.params:
new_values['c_ascenseur'] = 1
else:
new_values['c_ascenseur'] = 0
update_dossier(request, nodossier, new_values)
request.session.flash(u"Le dossier a été mis à jour avec succès.", 'success')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
return {
'page_title': "Modifier le dossier : %s" % nodossier,
'url': url,
'message': message,
'dossier': dossier,
'nodossier': nodossier,
'experts': experts,
'code_postal': code_postal,
}
@view_config(route_name='upload_doc', renderer='../templates/dossier/upload_doc.pt', permission='view')
def upload_doc(request):
logged_in = request.authenticated_userid.upper()
nodossier = request.matchdict['nodossier']
societe = nodossier[0:2]
nochantier = nodossier[3:]
url = request.route_url("upload_doc", nodossier=nodossier)
message = ""
dossier = get_dossier_by_no(request, nodossier)
if dossier is None:
request.session.flash(u"Le dossier no %s est introuvable" % (nodossier), 'danger')
return HTTPFound(location=request.route_url("dossier_lookup"))
# lire tous les documents attachés
docs_attaches = get_docs_attaches(request, nodossier, 0, 0, '')
if 'form.submitted' in request.params:
# récupère le fichier download dans le dossier /tmp
input_file = request.POST['filename'].file
input_name = request.POST['filename'].filename
ext_allowed = ['jpeg','jpg','png','pdf']
temp_file = downloadFile2Temp(input_file, input_name, ext_allowed)
if temp_file[:8] == 'ERREUR: ':
request.session.flash(temp_file, 'danger')
return HTTPFound(location=url)
# fabriquer le nom du document
filename = '%s-DD%s-%s' % (societe, nochantier, input_name)
tempFile2Dossier(request, societe, nochantier, '0', 0, temp_file, filename, logged_in)
request.session.flash('%s : Ce fichier est téléchargé avec succès.' % input_name, 'success')
# lire tous les documents attachés
docs_attaches = get_docs_attaches(request, nodossier, 0, 0, '')
return {
'page_title': "Télécharger un document",
'url': url,
'nodossier': nodossier,
'dossier': dossier,
'docs_attaches': docs_attaches,
'docs_url': request.static_url(request.registry.settings['mondumas.devfac_url']),
}
@view_config(route_name='upload_img', renderer='../templates/dossier/upload_img.pt', permission='view')
def upload_img(request):
# Paramètres d'appel :
# - gérer images d'un dossier : upload_img/nodossier/0
# - gérer images d'un rapport RDF : upload_img/norapport/nosection
nosection = request.matchdict['nosection']
# Gérer les photos d'un dossier (nosection=0) ?
if nosection == '0':
norapport = '0'
# oui, lire le dossier
nodossier = request.matchdict['norapport']
rapport = get_dossier_by_no(request, nodossier)
if rapport is None:
request.session.flash(u"Le dossier no %s est introuvable" % (norapport), 'danger')
return HTTPFound(location=request.route_url("rdf_list"))
societe = rapport.societe
nochantier = rapport.NO_ID
url = request.route_url("upload_img", norapport=nodossier, nosection=nosection)
url_retour = request.route_url('dossier_view', nodossier=nodossier)
titre = "Gérer les photos du dossier %s/%s" % (nodossier, nosection)
else:
norapport = request.matchdict['norapport']
rapport = get_rapport_by_no_id(request, norapport)
if rapport is None:
request.session.flash(u"Le rapport no %s est introuvable" % (norapport), 'danger')
return HTTPFound(location=request.route_url("rdf_list"))
societe = 'PL'
nochantier = rapport.nochantier
nodossier = '%s-%s' % (societe, nochantier)
url = request.route_url("upload_img", norapport=norapport, nosection=nosection)
url_retour = request.route_url('rdf_view', no_id=norapport)
titre = "Gérer les photos du rapport %s-%s/%s" % (societe, norapport, nosection)
logged_in = request.authenticated_userid.upper()
message = ""
if 'form.submitted' in request.params:
# récupère le(s) fichier(s) download dans le dossier /tmp
fileslist = request.POST.getall('files')
for f in fileslist:
input_file = f.file
input_name = f.filename
ext_allowed = ['jpeg','jpg','png']
temp_file = downloadFile2Temp(input_file, input_name, ext_allowed)
if temp_file[:8] == 'ERREUR: ':
request.session.flash(temp_file, 'danger')
return HTTPFound(location=url)
# fabriquer le nom du rapport
filename = '%s-DD%s-%s-%s' % (societe, nochantier, norapport, input_name)
tempFile2Dossier(request, societe, nochantier, norapport, nosection, temp_file, filename, logged_in)
request.session.flash('%s : Ce fichier est téléchargé avec succès.' % input_name, 'success')
# lire tous les photos attachées
photos = get_photos(request, nodossier, int(norapport), int(nosection))
return {
'page_title': titre,
'url': url,
'url_retour': url_retour,
'nodossier': nodossier,
'nochantier': nochantier,
'norapport': norapport,
'nosection': nosection,
'photos': photos,
'total_size' : photos_size(photos),
'docs_url': request.static_url(request.registry.settings['mondumas.devfac_url']),
}
@view_config(route_name='rdf_edit', renderer='../templates/dossier/rdf_edit.pt', permission='view')
def rdf_edit(request):
logged_in = request.authenticated_userid.upper()
nodossier = request.matchdict['nodossier']
date_inter = request.matchdict['date_inter']
if date_inter == 'new':
date_inter = date.today().strftime('%Y-%m-%d')
rapport = get_rapport_by_no(request, nodossier, date_inter)
if not rapport:
# creer un nouveau rapport
insert_rapport(request, nodossier, logged_in)
# lire le rapport
rapport = get_rapport_by_no(request, nodossier, date_inter)
url = request.route_url('rdf_edit', nodossier=nodossier, date_inter=date_inter)
caracteristiques = ["Maison individuelle", "Immeuble collectif", "Copropriété", "Commerce", "Bureaux"]
equipements = get_rdf_causes(request, 'C01')
reseaux_int = get_rdf_causes(request, 'C02')
reseaux_ext = get_rdf_causes(request, 'C03')
reseaux_local = get_rdf_causes(request, 'C04')
elements_clos = get_rdf_causes(request, 'C05')
elements_couvert = get_rdf_causes(request, 'C06')
voisins = get_rdf_causes(request, 'C07')
tierce_personnes = get_rdf_causes(request, 'C08')
# liste des users avec agenda
users = get_users_agenda(request, '')
# lire code accès du user
access = get_userAccess(request, logged_in)
if 'form.submitted' in request.params:
if 'signature_svg' in request.params:
if len(request.params['signature_svg']) > 7000:
request.session.flash(u"La signature est trop grande !", 'danger')
return HTTPFound(location=url)
new_values = {}
for param, db_value in rapport.items():
if param in request.params and request.params[param] != db_value:
new_values[param] = request.params[param]
if 'date_inter' in request.params:
ddate = datetime.strptime(request.params['date_inter'], '%d-%m-%Y')
new_values['date_inter'] = ddate.strftime("%Y-%m-%d")
# case à cocher ?
if 'sonde_capa' in request.params:
new_values['sonde_capa'] = 1
else:
new_values['sonde_capa'] = 0
if 'sonde_cond' in request.params:
new_values['sonde_cond'] = 1
else:
new_values['sonde_cond'] = 0
if 'test_mano' in request.params:
new_values['test_mano'] = 1
else:
new_values['test_mano'] = 0
if 'test_gaz' in request.params:
new_values['test_gaz'] = 1
else:
new_values['test_gaz'] = 0
if 'visu_camera' in request.params:
new_values['visu_camera'] = 1
else:
new_values['visu_camera'] = 0
if 'visu_endoscope' in request.params:
new_values['visu_endoscope'] = 1
else:
new_values['visu_endoscope'] = 0
if 'visu_tele' in request.params:
new_values['visu_tele'] = 1
else:
new_values['visu_tele'] = 0
if 'rech_magnetique' in request.params:
new_values['rech_magnetique'] = 1
else:
new_values['rech_magnetique'] = 0
if 'rech_accoustique' in request.params:
new_values['rech_accoustique'] = 1
else:
new_values['rech_accoustique'] = 0
if 'test_accoustique' in request.params:
new_values['test_accoustique'] = 1
else:
new_values['test_accoustique'] = 0
if 'test_mise_en_eau' in request.params:
new_values['test_mise_en_eau'] = 1
else:
new_values['test_mise_en_eau'] = 0
if 'test_fumigenes' in request.params:
new_values['test_fumigenes'] = 1
else:
new_values['test_fumigenes'] = 0
if new_values:
update_rapport(request, nodossier, date_inter, new_values)
request.session.flash(u"Le dossier a été mis à jour avec succès.", 'success')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
return {
'page_title': "Rapport de RDF : %s du %s" % (nodossier, rapport.date_inter.strftime('%d-%m-%Y')),
'url': url,
'logged_in': logged_in,
'rapport': rapport,
'nodossier': nodossier,
'caracteristiques': caracteristiques,
'equipements': equipements,
'reseaux_int': reseaux_int,
'reseaux_ext': reseaux_ext,
'reseaux_local': reseaux_local,
'elements_clos': elements_clos,
'elements_couvert': elements_couvert,
'voisins':voisins,
'tierce_personnes': tierce_personnes,
'users': users,
'access': access,
}
@view_config(route_name='rdf_list', renderer='../templates/dossier/rdf_list.pt', permission='view')
def rdf_list(request):
# lire les rapports de RDF
items = get_rapport_rdf(request)
# construire la liste
liste=[]
for item in items:
if item.date_relu :
date_relu = item.date_relu.strftime('%d-%m-%Y')
else:
date_relu = ''
if item.date_rapport :
date_rapport = item.date_rapport.strftime('%d-%m-%Y')
else:
date_rapport = ''
d = (item.no_id, item.date_inter.strftime('%d-%m-%Y'), date_relu, date_rapport, '%s - %s %s' % (item.nochantier, item.C_QUALITE, item.C_NOM), item.NOMCLI, item.NOSIN,
item.auteur_code)
liste.append(d)
return {
'page_title': 'Liste des rapports de RDF',
'dt_data': json.dumps(liste),
}
@view_config(route_name='rdf_client', renderer='../templates/dossier/rdf_client.pt', permission='view')
def rdf_client(request):
logged_in = request.authenticated_userid.upper()
norapport = request.matchdict['no_id']
message = ''
# lire code accès du user
access = get_userAccess(request, logged_in)
if access == 0:
request.session.flash("Vous n'avez pas les droits nécessaires pour changer de client.", 'danger')
return HTTPFound(location=request.route_url('rdf_view', no_id=norapport))
url = request.route_url('rdf_client', no_id=norapport)
# lire le rapport
rapport = get_rapport_by_no_id(request, norapport)
nodossier = 'PL-' + str(rapport.nochantier)
if 'form.submitted' in request.params:
nomClient = request.params['name'].split(' | ')
if len(nomClient) == 2:
update_rapport_client(request, norapport, nomClient[0], nomClient[1])
request.session.flash("Le client du rapporta été modifié avec succès.", 'success')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
else:
message = "Veuillez saisir un nom de client ou Annuler"
return {
'page_title': "Changer le client du RDF n° %s" % norapport,
'url': url,
'message': message,
'access': access,
'norapport': norapport,
'rapport': rapport,
}
@view_config(route_name='rdf_view', renderer='../templates/dossier/rdf_view.pt', permission='view')
def rdf_view(request):
logged_in = request.authenticated_userid.upper()
# lire code accès du user
access = get_userAccess(request, logged_in)
norapport = request.matchdict['no_id']
url = request.route_url('rdf_view', no_id=norapport)
# lire le rapport
rapport = get_rapport_by_no_id(request, norapport)
nodossier = 'PL-' + str(rapport.nochantier)
if rapport.date_relu:
date_relu = rapport.date_relu.strftime('%d-%m-%Y')
else:
date_relu = ''
if rapport.date_rapport:
date_rapport = rapport.date_rapport.strftime('%d-%m-%Y')
else:
date_rapport = ''
if rapport.date_facture:
date_facture = rapport.date_facture.strftime('%d-%m-%Y')
else:
date_facture = ''
# lire tous les photos attachées
photos1 = get_photos(request, nodossier, norapport, 1)
photos2 = get_photos(request, nodossier, norapport, 2)
if 'form.generate' in request.params:
options = {
'page-size': 'A4',
'margin-top': '0.65in',
'margin-right': '0.75in',
'margin-bottom': '0.65in',
'margin-left': '0.75in',
}
# créer le répertoire du rapport s'il n'existe pas encore
path = '%s/%s/%s/%s' % (request.registry.settings['mondumas.devfac_dir'], 'PL', rapport.nochantier, norapport)
os.makedirs(path, exist_ok=True)
# générer le rapport en PDF
filename = "PL-DD%s-rapport_RDF_no_%s.pdf" % (str(rapport.nochantier), norapport)
dest = "mondumas/static/DEVFAC/DOCS_ATTACHES/PL/%s/%s" % (str(rapport.nochantier), filename)
# developpement ou production
if request.registry.settings["mail.username"] == "sas.dumas@orange.fr":
origin = 'https://gestion.entreprise-dumas.com/rdf_rapport/%s' % norapport
pdfkit.from_url(origin, dest, options=options)
else:
origin = request.route_url('rdf_rapport', no_id=norapport)
config = pdfkit.configuration(wkhtmltopdf="C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe")
pdfkit.from_url(origin, dest, configuration=config, options=options)
insert_dossier_attaches(request, nodossier, 0, 0, filename, '160 Ko', logged_in)
# si generation pdf, maj de la date du rapport
new_values = {}
new_values['date_rapport'] = date.today()
update_rapport(request, nodossier, rapport.date_inter.strftime('%Y-%m-%d'), new_values)
request.session.flash(u"Le rapport a été généré avec succès.", 'success')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
if 'form.validate' in request.params:
update_rapport_validate(request, norapport)
request.session.flash(u"Le rapporta été validé avec succès.", 'success')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
if 'form.delete' in request.params:
# le rapport a-t-il des photos ?
if len(photos1) > 0 or len(photos2) > 0:
request.session.flash(u"Veuillez supprimer les photos avant de supprimer Le rapport.", 'warning')
return HTTPFound(location=url)
delete_rapport(request, norapport)
request.session.flash(u"Le rapport a été supprimé avec succès.", 'success')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
return {
'page_title': "Rapport no %s du %s" % (norapport, rapport.date_inter.strftime('%d-%m-%Y')),
'url': url,
'pt_name': 'rdf_view',
'access': access,
'rapport': rapport,
'nodossier': nodossier,
'nochantier': rapport.nochantier,
'norapport': norapport,
'date_rapport': date_rapport,
'date_relu': date_relu,
'date_facture': date_facture,
'photos1': photos1,
'photos2': photos2,
'docs_url': request.static_url(request.registry.settings['mondumas.devfac_url']),
}
@view_config(route_name='rdf_rapport', renderer='../templates/dossier/rdf_view.pt')
def rdf_rapport(request):
norapport = request.matchdict['no_id']
# lire le rapport
rapport = get_rapport_by_no_id(request, norapport)
nodossier = 'PL-' + str(rapport.nochantier)
if rapport.date_rapport:
date_rapport = rapport.date_rapport.strftime('%d-%m-%Y')
else:
date_rapport = ''
if rapport.date_relu:
date_relu = rapport.date_relu.strftime('%d-%m-%Y')
else:
date_relu = ''
# lire tous les photos attachées
photos1 = get_photos(request, nodossier, norapport, 1)
photos2 = get_photos(request, nodossier, norapport, 2)
return {
'page_title': '',
'url': '',
'pt_name': 'rdf_rapport',
'rapport': rapport,
'nodossier': nodossier,
'nochantier': rapport.nochantier,
'norapport': norapport,
'date_rapport': date_rapport,
'date_relu': date_relu,
'photos1': photos1,
'photos2': photos2,
'docs_url': request.static_url(request.registry.settings['mondumas.devfac_url']),
}
@view_config(route_name='delete_img', permission='view')
def delete_img(request):
logged_in = request.authenticated_userid.upper()
nodossier = request.matchdict['nodossier']
societe = nodossier[0:2]
nochantier = nodossier[3:]
norapport = request.matchdict['norapport']
nosection = request.matchdict['nosection']
nomfic = request.matchdict['nomfic']
if nosection == '0':
url_retour = location=request.route_url("upload_img", norapport=nodossier, nosection=nosection)
else:
url_retour = location=request.route_url("upload_img", norapport=norapport, nosection=nosection)
delete_photos(request, nodossier, norapport, nosection, nomfic)
request.session.flash(u"La photo %s a été supprimé avec succès" % (nomfic), 'success')
return HTTPFound(url_retour)
@view_config(route_name='rdf_bill', renderer='../templates/dossier/rdf_bill.pt', permission='view')
def rdf_bill(request):
logged_in = request.authenticated_userid.upper()
norapport = request.matchdict['no_id']
url = request.route_url('rdf_bill', no_id=norapport)
# lire le rapport
rapport = get_rapport_by_no_id(request, norapport)
nodossier = 'PL-' + str(rapport.nochantier)
if not rapport.date_rapport:
request.session.flash(u"Le rapport n'a été encore généré. Facturation impossible", 'danger')
return HTTPFound(location=request.route_url('rdf_view', no_id=norapport))
# lire tous les articles RDF
articles = get_articles_by_fam(request, 'RDF')
article = 'RDF1'
if 'form.submitted' in request.params:
# lire article à facturer
article = request.params['article']
insert_facture_rdf(request, 'PL', rapport.nochantier, rapport.CD_CLI, rapport.NOMCLI, logged_in, article, rapport.date_inter.strftime('%d-%m-%Y'))
# marquer le rapport comme facturé
update_rapport_facture(request, norapport)
request.session.flash("Le rapport a été généré avec succès.", 'success')
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
return {
'page_title': "Générer une facture pour le rapport du %s" % (rapport.date_inter.strftime('%d-%m-%Y')),
'url': url,
'pt_name': 'rdf_view',
'logged_in': logged_in,
'rapport': rapport,
'nodossier': nodossier,
'nochantier': rapport.nochantier,
'norapport': norapport,
'articles': articles,
'article' : article,
}
@view_config(route_name='demandes', renderer='../templates/dossier/demandes.pt', permission='view')
def demandes(request):
def mailbox_connect(societe):
# connecter au serveur IMAP de la societe
if societe == 'PE':
mbx_name = 'peinture-dumas@entreprise-dumas.com'
mbx_pwd = 'sasdumas'
elif societe == 'ME':
mbx_name = 'menuiserie-dumas@entreprise-dumas.com'
mbx_pwd = 'sasdumas'
elif societe == 'PL':
mbx_name = 'versanit-dumas@entreprise-dumas.com'
mbx_pwd = 'sasdumas'
else:
request.session.flash("Cette société est inconnue ou non traitée : %s" % societe, 'danger')
return HTTPFound(location=request.route_url('home'))
conn = imaplib.IMAP4_SSL('imap.entreprise-dumas.com')
try:
# se connecter à la mailbox
conn.login(mbx_name, mbx_pwd)
except imaplib.IMAP4.error:
request.session.flash("ERREUR connexion au compte %s" % mbx_name, 'danger')
return HTTPFound(location=request.route_url('home'))
return conn
def demandes_lister(societe, search_criteria):
# connecter au serveur de mail
conn = mailbox_connect(societe)
# select INBOX
rv, data = conn.select('INBOX', readonly =True)
# créer la liste des entêtes des messages à afficher
liste = []
for criteria in search_criteria:
rv, data = conn.search(None, criteria)
if rv != 'OK':
request.session.flash("ERREUR de lecture de la boîte de réception", 'danger')
return HTTPFound(location=request.route_url('home'))
mail_ids = data[0]
for num in mail_ids.split():
rv, msg_data = conn.fetch(num, '(RFC822)')
if rv != 'OK':
request.session.flash("ERREUR de lecture du message %s" % num, 'danger')
return HTTPFound(location=request.route_url('home'))
msg = email.message_from_bytes(msg_data[0][1])
hdr = email.header.make_header(email.header.decode_header(msg['Subject']))
email_subject = str(hdr)
email_from = email.utils.parseaddr(msg['from'])[1]
# Now convert to local date-time
date_tuple = email.utils.parsedate_tz(msg['Date'])
if date_tuple:
email_date = datetime.fromtimestamp(email.utils.mktime_tz(date_tuple))
else:
email_date = datetime.now()
d = (societe, email_date.strftime('%d-%m-%Y %H:%M:%S'), email_from, email_subject)
liste.append(d)
# deconnexion du serveur
conn.close()
conn.logout()
return liste
def demandes_generer(request, societe, search_criteria):
def generer_mission(request, societe, mbx_search, extracted_file, temp_file_path):
if 'maif.fr' in mbx_search:
# extraire les infos de la demmande MAIF
dem_info = get_pdf_infos1(extracted_file)
if societe == 'PE':
cd_cli = 2813
elif societe == 'ME':
cd_cli = 3428
else:
# VERSANIT
cd_cli = 1743
elif 'domus-services.fr' in mbx_search:
# extraire les infos de la demmande DOMUS
dem_info = get_pdf_infos2(extracted_file)
if societe == 'PE':
cd_cli = 8991
elif societe == 'ME':
cd_cli = 5276
else:
# VERSANIT
cd_cli = 3209
# extraction OK ? oui, créer une dem_devis et récupèrer son no_id
traite = 0
if dem_info['c_nom'] != '':
# oui, rechercher la dem_devis concerné par le no de sinistre
nosin = dem_info['no_sinistre']
dem_devis = get_dossier_by_sinistre(request,societe, nosin)
if dem_devis:
# dem_devis existe, ajouter le PDF dans ce dossier
nochantier = dem_devis.NO_ID
nodossier = "%s-%s" % (societe, nochantier)
# insérer une ligne de suivi ANNULATION
insert_suivi(request, nodossier, '!!MISSION CONFIRMEE ou MODIFIEE PAR la MAIF')
# log de nuit
print('--> MODIFIER DOSSIER sinistre %s <--' % nodossier)
else:
# dem_devis n'existe pas, creer nouveau dossier
nochantier = insert_dossier(request, societe, cd_cli, dem_info['c_nom'], dem_info['c_adr'], dem_info['c_adr2'], \
dem_info['c_cp'], dem_info['c_ville'], dem_info['c_telp'], dem_info['c_email'], nosin, dem_info['c_obs'], dem_info['tx_trav'])
nodossier = "%s-%s" % (societe, nochantier)
# log de nuit
print('--> CREER DOSSIER sinistre %s <--' % nodossier)
# récupère le nom du fichier et ajouter le no de dossier
filename = os.path.basename(temp_file_path)
filename = '%s-DD%s-%s' % (societe, nochantier, filename)
tempFile2Dossier(request, societe, nochantier, '0', 0, temp_file_path, filename, 'EMAIL')
traite = 1
return traite
def generer_annul_maif(request, societe, extracted_file, temp_file_path):
# extraire les infos de la demmande MAIF
dem_info = get_pdf_infos1(extracted_file)
# extraction OK ? oui, rechercher la dem_devis concerné
traite = 0
if dem_info['c_nom'] != '':
# oui, rechercher la dem_devis concerné par le no de sinistre
nosin = dem_info['no_sinistre']
dem_devis = get_dossier_by_sinistre(request,societe, nosin)
if dem_devis:
nodossier = "%s-%s" % (societe, dem_devis.NO_ID)
# récupère le nom du fichier et ajouter le no de dossier
filename = '%s-DD%s-%s' % (societe, dem_devis.NO_ID, 'ANNULATION.pdf')
tempFile2Dossier(request, societe, dem_devis.NO_ID, '0', 0, temp_file_path, filename, 'EMAIL')
# insérer une ligne de suivi ANNULATION
insert_suivi(request, nodossier, '!!MISSION ANNULEE PAR la MAIF')
# log de nuit
print('--> ANNULATION DOSSIER sinistre %s <--' % nodossier)
traite = 1
return traite
def generer_annul_domus(request, societe, nosin, temp_file_path):
traite = 0
# oui, rechercher la dem_devis concerné par le no de sinistre
dem_devis = get_dossier_by_sinistre(request,societe, nosin)
if dem_devis:
nodossier = "%s-%s" % (societe, dem_devis.NO_ID)
# récupère le nom du fichier et ajouter le no de dossier
filename = '%s-DD%s-%s' % (societe, dem_devis.NO_ID, 'ANNULATION.pdf')
tempFile2Dossier(request, societe, dem_devis.NO_ID, '0', 0, temp_file_path, filename, 'EMAIL')
# insérer une ligne de suivi ANNULATION
insert_suivi(request, nodossier, '!!MISSION ANNULEE PAR DOMUS')
# log de nuit
print('--> CREER DOSSIER sinistre %s <--' % nodossier)
traite = 1
return traite
# connecter au serveur de mail
conn = mailbox_connect(societe)
# select INBOX
rv, data = conn.select('INBOX')
conn.expunge()
nbLus = 0
nbCrees = 0
nbAnnules = 0
for criteria in search_criteria:
# rechercher les emails de demandes dans le INBOX
rv, data = conn.search(None, criteria)
if rv != 'OK':
request.session.flash("ERREUR de lecture de la boîte de réception", 'danger')
return HTTPFound(location=request.route_url('home'))
mail_ids = data[0]
# parcourir la liste des UID
for num in mail_ids.split():
rv, msg_data = conn.fetch(num, '(RFC822)')
if rv != 'OK':
request.session.flash("ERREUR de lecture du message %s" % num, 'danger')
return HTTPFound(location=request.route_url('home'))
raw_email = msg_data[0][1]
# converts byte literal to string removing b''
try:
raw_email_string = raw_email.decode('utf-8')
except:
# déplacer le message dans la poubelle
conn.store(num, '+FLAGS', '\\Deleted')
conn.expunge()
else:
email_message = email.message_from_string(raw_email_string)
# get the message's body
body = ''
for part in email_message.walk():
ctype = part.get_content_type()
cdispo = str(part.get('Content-Disposition'))
# skip any text/plain (txt) attachments
if ctype == 'text/html' and 'attachment' not in cdispo:
body = part.get_payload(decode=True) # decode
break
# downloading attachment
temp_file_path = download_pdf_to_tmp(email_message)
# convertir le fichier pdf en texte
texte, extracted_file = pdf_convert_to_txt(temp_file_path)
nbLus = nbLus + 1
# mission annulée ?
if 'Objet : ANNULATION MISSION' in texte :
# genere ANNULATION mission MAIF
# import pdb;pdb.set_trace()
n = generer_annul_maif(request, societe, extracted_file, temp_file_path)
if n > 0:
nbAnnules = nbAnnules + n
# déplacer le message dans la poubelle
conn.store(num, '+FLAGS', '\\Deleted')
elif 'ANNULATION ORDRE DE MISSION' in texte:
# genere ANNULATION mission DOMUS
nosin = str(body)[84:95]
# import pdb;pdb.set_trace()
n = generer_annul_domus(request, societe, nosin, temp_file_path)
if n > 0:
nbAnnules = nbAnnules + n
# déplacer le message dans la poubelle
conn.store(num, '+FLAGS', '\\Deleted')
else:
# genere le dossier d'après le mail
n = generer_mission(request, societe, criteria, extracted_file, temp_file_path)
if n > 0:
nbCrees = nbCrees + n
# déplacer le message dans la poubelle
conn.store(num, '+FLAGS', '\\Deleted')
conn.expunge()
conn.close()
# deconnexion du serveur
conn.logout()
return nbLus, nbCrees, nbAnnules
# ------- main -------
logged_in = request.authenticated_userid.upper()
url = request.route_url('demandes')
message = ''
societes = ['PE','ME','PL']
# critères de recherche des demandes d'interventions de la MAIF
# search_criteria = ['FROM service.sinistres@domus-services.fr SUBJECT "Ordre de mission DOMUS - Dossier" UNDELETED'] "
search_criteria = ['FROM gestionsinistre@maif.fr SUBJECT "Intervention entreprise partenaire"',
'FROM service.sinistres@domus-services.fr SUBJECT "Ordre de mission DOMUS - Dossier" UNDELETED']
if 'form.submitted' in request.params:
# traiter les demandes de la MAIF puis de DOMUS pour toutes les sociétes
nbLus = 0
nbCrees = 0
nbAnnules = 0
# log de nuit
insert_log_nuit(request, 'GENERER','- Début GENERATION des dossiers à partir des EMAILS', '')
for societe in societes:
nLus, nCrees, nAnnules = demandes_generer(request, societe, search_criteria)
nbLus = nbLus + nLus
nbCrees = nbCrees + nCrees
nbAnnules = nbAnnules + nAnnules
# log de nuit
insert_log_nuit(request, 'GENERER','- Fin GENERATION : %s emails traités, %s dossiers créés/modifiés, %s dossiers annulés' % (nbLus, nbCrees, nbAnnules), '')
message = "%s emails ont été traités." % nbLus
liste=[]
# lister les demandes par societe
for societe in societes:
liste = liste + demandes_lister(societe, search_criteria)
# messages lus
msglus = bool(liste)
# lire le log
log_creation = get_log_demandes(request)
dossiers_traites = get_dossiers_traites(request)
return {
'page_title': "Liste des emails de demandes d'intervention",
'url': url,
'dt_data': json.dumps(liste),
'msglus': msglus,
'message': message,
'log_creation': log_creation,
'dossiers_traites': dossiers_traites,
}
def downloadFile2Temp(input_file, input_name, ext_allowed):
# récupère son extension
input_extension = input_name.split('.')[-1]
# extensions autorisées ?
if input_extension.lower() not in ext_allowed :
return "ERREUR: Le format du fichier n'est pas valide. Téléchargement impossible."
# Finally write the data to a temporary file
temp_file_path = os.path.join('/tmp/', input_name)
# supprimer le fichier s'il existe déjà
if os.path.exists(temp_file_path):
os.remove(temp_file_path)
input_file.seek(0)
with open(temp_file_path, 'wb') as output_file:
shutil.copyfileobj(input_file, output_file)
# controler la taille du fichier < 4 Mo
filesize = round(os.path.getsize(temp_file_path) / 1024)
if filesize > 4096 :
os.remove(temp_file_path)
return "ERREUR: La taille du fichier dépasse la limite autorisée. Téléchargement impossible."
if input_extension in ['jpeg','jpg','png']:
# using the Python Image Library (PIL) to resize an image
resize_photos(temp_file_path)
return temp_file_path
def tempFile2Dossier(request, societe, nochantier, norapport, nosection, temp_file, filename, logged_in):
# créer le répertoire du chantier
if norapport == '0':
path = '%s/%s/%s' % (request.registry.settings['mondumas.devfac_dir'], societe, nochantier)
else:
path = '%s/%s/%s/%s' % (request.registry.settings['mondumas.devfac_dir'], societe, nochantier, norapport)
os.makedirs(path, exist_ok=True)
filepath = os.path.join('%s/%s' % (path, filename))
# supprimer le fichier s'il existe déjà
if os.path.exists(filepath):
os.remove(filepath)
# Finally move the temporary file to folder
shutil.move(temp_file, filepath)
filesize = round(os.path.getsize(filepath) / 1024)
insert_dossier_attaches(request, '%s-%s' % (societe, nochantier), norapport, nosection, filename, '%s Ko' % str(filesize), logged_in)
def download_pdf_to_tmp(email_message):
# downloading attachments
for part in email_message.walk():
# this part comes from the snipped I don't understand yet...
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
fileName = part.get_filename()
if bool(fileName):
# copier le fichier PDF dans le dossier /tmp
temp_file_path = os.path.join('/tmp/', fileName)
if not os.path.isfile(temp_file_path) :
fp = open(temp_file_path, 'wb')
fp.write(part.get_payload(decode=True))
fp.close()
return temp_file_path
def pdf_convert_to_txt(path):
resource_manager = PDFResourceManager()
laparams = LAParams()
converter = PDFPageAggregator(resource_manager, laparams=laparams)
page_interpreter = PDFPageInterpreter(resource_manager, converter)
extracted_text = ""
with open(path, 'rb') as fh:
for page in PDFPage.get_pages(fh,
caching=True,
check_extractable=True):
page_interpreter.process_page(page)
# The converter renders the layout from interpreter
layout = converter.get_result()
# Out of the many LT objects within layout, we are interested in LTTextBox and LTTextLine
for lt_obj in layout:
if isinstance(lt_obj, LTTextBox) or isinstance(lt_obj, LTTextLine):
extracted_text += lt_obj.get_text()
# close open handles
converter.close()
fh.close()
# ecrire le texte dans un fichier
extracted_file = '/tmp/log_file.txt'
with open(extracted_file, "w", encoding="utf-8") as my_log:
my_log.write(extracted_text)
my_log.close()
return extracted_text, extracted_file
def get_pdf_infos1(extracted_file):
# à partir du fichier texte du pdf
# parcourir les lignes pour retrouver les infos utiles
with open(extracted_file, encoding="utf-8") as fp:
cnt = 1
line = fp.readline()
# première ligne doit être "MAIF"
if line[:-1] not in ['MAIF', 'FILIA-MAIF']:
return {'c_nom': ''}
c_obs = ''
tx_trav = ''
c_telp = ''
c_email = ''
while line:
if line.find('Nos références') == 0:
line = fp.readline()
line = fp.readline()
no_sinistre = line[:-1]
if line.find('Bénéficiaire des travaux :') == 0:
elt = line[:-1].split(' :')
import pdb;pdb.set_trace()
if len(elt) == 1:
# le nom du chantier est sur la ligne suivante
line = fp.readline()
c_nom = line[:-1]
c_nom = c_nom[:40] # ne prendre que 40 carac
else:
# le nom du chantier est sur la même ligne
c_nom = elt[1].strip()[:40]
line = fp.readline()
line = fp.readline()
line = fp.readline()
c_adr = line[:-1]
line = fp.readline()
c_adr2 = line[:-1]
line = fp.readline()
c_adr3 = line[:-1]
# début 3ème ligne adr est un code postal ?
if to_int(c_adr3[0:5]) > 0 :
# oui, mémoriser le code postal et la ville
c_cp = c_adr3[0:5]
c_ville = c_adr3[6:]
else:
# non, le code postal et la ville se trouvent dans la 2è ligne
c_cp = c_adr2[0:5]
c_ville = c_adr2[6:]
c_adr2 = ''
if ' téléphone : ' in line:
# les 10 derniers caratères
# import pdb;pdb.set_trace()
c_telp = line[-11:-1]
if 'E-mail : ' in line:
# séparer l'émail
# import pdb;pdb.set_trace()
elt = line[:-1].split(' : ')
if len(elt) == 2:
c_email = elt[1]
if 'une franchise de ' in line:
i1 = line.find('franchise de ')
i2 = line.find('')
c_obs = line[i1:i2+2]
if ' pour un montant de ' in line:
i1 = line.find('pour un montsant de ')
tx_trav = line[i1:-2].replace(',', '.')
# lire ligne suivante
line = fp.readline()
cnt += 1
fp.close()
return {'c_nom': c_nom,
'c_adr': c_adr,
'c_adr2': c_adr2,
'c_cp': c_cp,
'c_ville': c_ville,
'c_telp': c_telp,
'c_obs': c_obs,
'c_email': c_email,
'tx_trav': tx_trav,
'no_sinistre': no_sinistre,
}
def get_pdf_infos2(extracted_file):
# à partir du fichier texte du pdf de DOMUS
# parcourir les lignes pour retrouver les infos utiles
with open(extracted_file, encoding="utf-8") as fp:
cnt = 1
line = fp.readline()
# première ligne doit être :
if line[:-1] != 'ORDRE DE MISSION':
fp.close()
return {'c_nom': ''}
c_obs = ''
tx_trav = ''
c_telp = ''
c_email = ''
while line:
if line.find('SINISTRE N°') == 0:
line = fp.readline()
line = fp.readline()
no_sinistre = line[:-1]
if line.find('Adresse du sinistre :') == 0:
line = fp.readline()
c_nom = line[:-1]
line = fp.readline()
c_adr = line[:-1]
line = fp.readline()
c_adr2 = line[:-1]
# début 2ème ligne adr est un code postal ?
if to_int(c_adr2) > 0 :
# oui, mémoriser le code postal et la ville
c_cp = c_adr2
line = fp.readline()
c_ville = line[:-1]
c_adr2 = ''
else:
# non, le code postal et la ville se trouvent dans la 3è ligne
line = fp.readline()
c_cp = line[:-1]
c_ville = line[:-1]
line = fp.readline()
if 'Gsm : ' in line:
# les 10 derniers caratères
c_telp = line[-11:-1]
# lire ligne suivante
line = fp.readline()
cnt += 1
fp.close()
return {'c_nom': c_nom,
'c_adr': c_adr,
'c_adr2': c_adr2,
'c_cp': c_cp[:5],
'c_ville': c_ville,
'c_telp': c_telp,
'c_email': c_email,
'c_obs': c_obs,
'tx_trav': tx_trav,
'no_sinistre': no_sinistre,
}
def resize_photos(image_file):
# using the Python Image Library (PIL) to resize an image
img_org = Image.open(image_file)
# get the size of the original image
width_org, height_org = img_org.size
# set the resizing factor so the aspect ratio can be retained
# factor > 1.0 increases size
# factor < 1.0 decreases size
factor = 1
width = int(width_org * factor)
height = int(height_org * factor)
# best down-sizing filter
img_anti = img_org.resize((width, height), Image.ANTIALIAS)
# split image filename into name and extension
name, ext = os.path.splitext(image_file)
# create a new file name for saving the result
img_anti.save(image_file)
return
def photos_size(photos):
# calculer la taille totale des photos
sum_size = 0
for photo in photos :
# récupère la taille sans ' Ko'
sum_size += int(photo.taillefichier[:-3])
return '%s Mo' % round(sum_size / 1024, 1)