initial upload

This commit is contained in:
2023-06-22 10:34:18 +02:00
parent 6df897b44d
commit 5c621cf669
1030 changed files with 355394 additions and 49 deletions

View File

446
aem_gestion/views/compta.py Normal file
View File

@@ -0,0 +1,446 @@
# -*- coding: utf8 -*-
from pyramid.view import (
view_config,
)
from pyramid.httpexceptions import (
HTTPFound,
)
from datetime import datetime as dt, date
from dateutil.relativedelta import relativedelta
import json
from ..models.compta import *
from ..models.default import (
get_users_by_code,
)
from ..models.eleves import get_permis
from ..views.default import (
to_date,
to_euro,
to_euroz,
to_time,
to_int,
to_percent,
)
@view_config(route_name='export_cpta', renderer='../templates/compta/export_cpta.pt', permission='manage')
def export_cpta(request):
logged_in = request.authenticated_userid.lower()
url = request.route_url('export_cpta')
message = ''
brouillard = []
total_debit = 0
total_credit = 0
NOW = dt.now()
if 'etape' in request.params:
etape = '2'
etape = request.params['etape']
# récuprérer la date de fin
date_fin = dt.strptime(request.params['date_fin'], '%d-%m-%Y')
else:
etape = '1'
date_fin = NOW.date()
nom_fichier = "C-AEM-%s-%s.PNM" % (date_fin.strftime("%y%m%d"), NOW.strftime("%H%M"))
if 'form.extraction' in request.params:
# générer le brouillard jusqu'à la date de fin
message = generer_brouillard(request, logged_in, date_fin)
if len(message) == 0:
etape = '2'
# si extraction OK, afficher le brouillard
brouillard = get_brouillard(request, logged_in)
for item in brouillard:
if item.SENS == 'C':
total_credit += float(item.MONTANT)
else:
total_debit += float(item.MONTANT)
else:
# afficher message erreur
request.session.flash(message, 'danger')
if 'form.generer' in request.params:
import os
# créer le répertoire /JUSTIFS/EXPORT
path = request.registry.settings['aem_gestion.justifs_dir'] + '/EXPORT/'
os.makedirs(path, exist_ok=True)
path_fichier = path + nom_fichier
# générer le fichier PNM dans le répertoire EXPORT
message = generer_export(request, logged_in, path_fichier)
# MAJ les comptes clients exportés dans la période
update_cpt_export(request, date_fin)
request.session.flash("La génération du fichier d'export " + nom_fichier + " est terminée.", 'success')
etape = '3'
return {
'page_title': "Exportation des écritures",
'url': url,
'message': message,
'etape' : etape,
'date_fin': date_fin,
'nom_fichier': nom_fichier,
'brouillard': brouillard,
'total_credit': to_euro(total_credit),
'total_debit': to_euro(total_debit),
}
@view_config(route_name='jnl_caisse', renderer='../templates/compta/jnl_caisse.pt', permission='view')
def jnl_caisse(request):
logged_in = request.authenticated_userid
url = request.route_url('jnl_caisse')
message = ''
member = get_users_by_code(request, logged_in)
if member.secu < 6 :
request.session.flash("Vous n'avez le niveau d'accès requis pour ce traitement", 'error')
return HTTPFound(location=request.route_url('home'))
date_edition = dt.now().date()
agence = member.agence
if 'form.submitted' in request.params:
# récuprérer la date d'édition
date_edition = request.params['date_edition']
return HTTPFound(location=request.route_url('prt_caisse', date_edition=date_edition))
return {
'page_title': 'Agence %s : Journal de caisse du %s' % (agence, date_edition.strftime('%d-%m-%Y')),
'url': url,
'message': message,
'date_edition': date_edition,
}
@view_config(route_name='prt_caisse', renderer='../templates/compta/prt_caisse.pt', permission='view')
def prt_caisse(request):
date_edition = dt.strptime(request.matchdict['date_edition'], '%d-%m-%Y')
logged_in = request.authenticated_userid
member = get_users_by_code(request, logged_in)
if member.secu <= 6 :
return HTTPFound(location=request.route_url('home'))
agence = member.agence
# lire la caisse du jour
items = get_caisse(request, agence, date_edition)
# construire la liste
liste=[]
# lire la caisse du jour
items = get_caisse(request, agence, date_edition)
totalMode = 0.0
totalGeneral = 0.0
groupe = ''
for item in items:
# rupture sur mode_regl
if groupe != item.lib4 :
if groupe != '':
# ajouter une ligne de sous-total
d = ('', '', '', '*** TOTAL pour ' + groupe, to_euro(totalMode), groupe, '', '')
liste.append(d)
totalGeneral += totalMode
totalMode = 0.0
cree_le = item.cree_le.strftime('%d-%m-%y')
modif_le = item.modif_le.strftime('%d-%m')
d = (item.CD_CLI, item.NOM, item.permis_demande, item.INTITULE, to_euro(item.CREDIT),
item.lib4, item.CD_UTI, cree_le + ', ' + modif_le)
liste.append(d)
totalMode += float(item.CREDIT)
groupe = item.lib4
# ajouter une ligne de sous-total
d = ('', '', '', '*** TOTAL pour ' + groupe, to_euro(totalMode), groupe, '', '')
liste.append(d)
# ajouter ligne Total général
totalGeneral += totalMode
d = ('', '', '', '*** TOTAL AGENCE ' + str(agence), to_euro(totalGeneral), '', '', '')
liste.append(d)
return {
'page_title': 'Agence %s : Journal de caisse du %s' % (agence, date_edition.strftime('%d-%m-%Y')),
'date_edition': date_edition,
'dt_data': json.dumps(liste),
'agence': agence,
}
@view_config(route_name='solder_contrats', renderer='../templates/compta/solder_contrats.pt', permission='manage')
def solder_contrats(request):
logged_in = request.authenticated_userid.lower()
url = request.route_url('solder_contrats')
message = ''
if 'form.solder_auto' in request.params:
# Solder les comptes clients
result = update_contrats_auto(request, 6, logged_in)
request.session.flash(str(result.NB) + " contrats AUTO ont été soldés !", 'success')
if 'form.solder_moto' in request.params:
# Solder les comptes clients
result = update_contrats_auto(request, 6, logged_in)
request.session.flash(str(result.NB) + " contrats MOTO ont été soldés !", 'success')
return {
'page_title': "Solder les contrats échus",
'url': url,
'message': message,
}
@view_config(route_name='balance_clients', renderer='../templates/compta/balance_clients.pt', permission='manage')
def balance_clients(request):
logged_in = request.authenticated_userid.lower()
url = request.route_url('balance_clients')
message = ''
TODAY = dt.now().date()
permis = get_permis(request)
cat = 'B'
# date début = 01/01 de l'année
date_debut = date(TODAY.year, 1, 1)
date_fin = date(TODAY.year, 12, 31)
if 'form.submitted' in request.params:
cat = request.params['cat']
# récuprérer la date de debut
date_debut = dt.strptime(request.params['date_debut'], '%d-%m-%Y')
# récuprérer la date de fin
date_fin = dt.strptime(request.params['date_fin'], '%d-%m-%Y')
# "La date de fin doit être supérieure à la date début"
if date_debut > date_fin :
message("La date de fin doit être supérieure à la date début")
else:
# générerles écritures de balance
generer_balance(request, logged_in, cat, date_debut, date_fin)
request.session.flash("La génération de la balance est terminée !", 'success')
if 'form.printed' in request.params:
return HTTPFound(location=request.route_url('balance_print'))
# dates de début et fin de la génération en cours
balance_debut, balance_fin, balance_criteres = get_dates_balance(request)
if balance_debut:
generer_debut = balance_debut.strftime('%d-%m-%Y %H:%M')
else:
generer_debut = "Pas de génération en cours"
if balance_fin:
generer_fin = balance_fin.strftime('%d-%m-%Y %H:%M')
else:
generer_fin = "Génération non terminée..."
return {
'page_title': "Balance CLIENTS",
'url': url,
'message': message,
'date_debut': date_debut,
'date_fin': date_fin,
'generer_debut': generer_debut,
'generer_fin' : generer_fin,
'balance_fin': balance_fin,
'balance_criteres': balance_criteres,
'permis': permis,
'cat': cat,
}
@view_config(route_name='balance_print', renderer='../templates/compta/balance_print.pt', permission='manage')
def balance_print(request):
logged_in = request.authenticated_userid.lower()
TODAY = dt.now().date()
# construire la liste
liste=[]
# lire la balance générée
items = get_balance(request, logged_in)
# dates de début et fin de la génération en cours
balance_debut, balance_fin, criteres = get_dates_balance(request)
if balance_fin == None:
return HTTPFound(location=request.route_url('balance_clients'))
# total agence
agence = 0
ta_solde = 0.0
ta_mtval = 0.0
ta_credit = 0.0
ta_solde_c = 0.0
ta_solde_d = 0.0
# total générale
tl_solde = 0.0
tl_mtval = 0.0
tl_credit = 0.0
tl_solde_c = 0.0
tl_solde_d = 0.0
for item in items:
# rupture sur mode_regl
if agence != item.AGENCE :
if agence != 0:
# ajouter une ligne de sous-total
d = ('', '*** TOTAL Agence ' + str(agence), '', to_euro(ta_solde),
to_euro(ta_mtval), to_euro(ta_credit), to_euro(ta_solde_d), to_euro(ta_solde_c), to_euro(ta_solde_c - ta_solde_d))
liste.append(d)
tl_solde += ta_solde
tl_mtval += ta_mtval
tl_credit += ta_credit
tl_solde_c += ta_solde_c
tl_solde_d += ta_solde_d
ta_solde = 0.0
ta_mtval = 0.0
ta_credit = 0.0
ta_solde_c = 0.0
ta_solde_d = 0.0
agence = item.AGENCE
solde = float(item.Solde + item.CREDIT - item.MTVAL)
if solde < 0 :
solde_c = 0
solde_d = solde * -1
else :
solde_c = solde
solde_d = 0
d = (item.CD_CLI, item.INTITULE, item.FORMULE, to_euro(item.Solde), to_euro(item.MTVAL), to_euro(item.CREDIT),
to_euro(solde_d), to_euro(solde_c), '')
liste.append(d)
ta_solde += float(item.Solde)
ta_mtval += float(item.MTVAL)
ta_credit += float(item.CREDIT)
ta_solde_c += solde_c
ta_solde_d += solde_d
# ajouter dernière ligne de sous-total
d = ('', '*** TOTAL Agence ' + str(agence), '', to_euro(ta_solde),
to_euro(ta_mtval), to_euro(ta_credit), to_euro(ta_solde_d), to_euro(ta_solde_c), to_euro(ta_solde_c - ta_solde_d))
liste.append(d)
# ajouter ligne Total général
tl_solde += ta_solde
tl_mtval += ta_mtval
tl_credit += ta_credit
tl_solde_c += ta_solde_c
tl_solde_d += ta_solde_d
d = ('', '*** TOTAL SOCIETE', '', to_euro(tl_solde),
to_euro(tl_mtval), to_euro(tl_credit), to_euro(tl_solde_d), to_euro(tl_solde_c), to_euro(tl_solde_c - tl_solde_d))
liste.append(d)
return {
'page_title': "Balance CLIENTS " + criteres,
'TODAY': TODAY,
'dt_data': json.dumps(liste),
}
@view_config(route_name='recap_moniteur', renderer='../templates/compta/recap_moniteur.pt', permission='manage')
def recap_moniteur(request):
logged_in = request.authenticated_userid.lower()
url = request.route_url('recap_moniteur')
message = ''
TODAY = dt.now().date()
# date début = 01 du mois
date_debut = date(TODAY.year, TODAY.month, 1)
date_fin = date_debut + relativedelta(day=31)
# lire les moniteurs
moniteurs = get_moniteurs_byCode(request)
cd_mon1 = moniteurs[0].CD_MON
cd_mon2 = moniteurs[len(moniteurs) - 1].CD_MON
if 'form.submitted' in request.params:
cd_mon1 = request.params['cd_mon1']
cd_mon2 = request.params['cd_mon2']
# récuprérer la date de debut
date_debut = dt.strptime(request.params['date_debut'], '%d-%m-%Y')
# récuprérer la date de fin
date_fin = dt.strptime(request.params['date_fin'], '%d-%m-%Y')
# "La date de fin doit être supérieure à la date début"
if date_debut > date_fin :
message("La date de fin doit être supérieure à la date début")
else:
return HTTPFound(location=request.route_url('recap_print',
cd_mon1=cd_mon1, cd_mon2=cd_mon2, date1=date_debut.date(), date2=date_fin.date()))
return {
'page_title': "Récap des activités Moniteurs",
'url': url,
'message': message,
'date_debut': date_debut,
'date_fin': date_fin,
'moniteurs': moniteurs,
'cd_mon1': cd_mon1,
'cd_mon2': cd_mon2,
}
@view_config(route_name='recap_print', renderer='../templates/compta/recap_print.pt', permission='manage')
def recap_print(request):
cd_mon1 = request.matchdict['cd_mon1']
cd_mon2 = request.matchdict['cd_mon2']
date1 = request.matchdict['date1']
date2 = request.matchdict['date2']
d1 = dt.strptime(date1, '%Y-%m-%d')
d2 = dt.strptime(date2, '%Y-%m-%d')
periode = 'du %s au %s' % (d1.strftime('%d-%m-%Y'), d2.strftime('%d-%m-%Y'))
TODAY = dt.now().date()
# lire le recap des activités des moniteurs
# rupture sur CD_MON / REF / STATUT
items = get_recap(request, cd_mon1, cd_mon2, date1, date2)
# total moniteur
nom = ''
tm_heures = 0
# construire la liste
liste=[]
for item in items:
# rupture sur NOM
if nom != item.NOM :
if nom != '':
# ajouter une ligne de total Moniteur
d = (nom,'','*** TOTAL', nom + ' - ' + cd_mon, str(tm_heures) + ' h')
liste.append(d)
tm_heures = 0
if item.STATUT == 15:
statut = '(%s)' % item.STATUT
qte = '(%s)' % item.SUM_QTE
else:
statut = str(item.STATUT)
qte = str(item.SUM_QTE)
d = (item.NOM, statut, item.REF, item.INTITULE, qte)
liste.append(d)
# cumuler les heures
tm_heures += item.SUM_QTE
nom = item.NOM
cd_mon = item.CD_MON
# ajouter une ligne de total Moniteur
d = (nom,'','*** TOTAL', nom + ' - ' + cd_mon, str(tm_heures) + ' h')
liste.append(d)
return {
'page_title': "Récap des activités Moniteurs",
'periode': periode,
'TODAY': TODAY,
'dt_data': json.dumps(liste),
}

1016
aem_gestion/views/crm.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,799 @@
# -*- coding: utf8 -*-
from pyramid.response import Response
from pyramid.view import (
view_config,
forbidden_view_config,
)
from pyramid.security import (
remember,
forget,
)
from pyramid.httpexceptions import (
HTTPFound,
)
from pyramid_mailer import get_mailer
from pyramid_mailer.message import Message
from datetime import date, datetime
from dateutil.relativedelta import *
from urllib.request import urlopen
from user_agents import parse
import transaction
import json
import locale
import hashlib
from ..models.default import *
from ..models.parametres import (
get_charts_data,
)
from ..models.eleves import (
get_eleves_by_code,
get_eleves_by_name,
get_eleves_by_name78,
get_eleves_not_b78_by_name,
get_eleves_ajax
)
from ..models.crm import (
get_prospects_by_name,
)
def to_age(dob, type):
today = date.today()
age = relativedelta(today, dob)
if type == '>':
if age.years >= 0:
a = '%s ans %s mois' % (age.years, age.months)
else:
a = '???'
else:
if age.years < 0 or age.years < 0:
if age.years <= -1:
a = '%sa %sm' % (age.years, age.months)
else:
a = '%sm' % (age.months)
else:
a = ''
return a.replace('-', '')
def to_decimal(x):
import decimal
return decimal.Decimal(str(x))
def to_sha1(message):
return hashlib.sha1(message.encode('utf-8')).hexdigest()
def to_int(x):
if x == None:
return 0
try:
number = int(x.replace(',', '.'))
return number
except ValueError:
return 0
def to_euro(x):
"""Takes a float and returns 12 345,67 €"""
locale.setlocale(locale.LC_ALL, '')
return locale.currency(x, True, True)
def to_euroz(x):
"""Takes a float and returns 12 345,67 € if non zero"""
if x == 0:
return ""
else:
return to_euro(x)
def to_decz(x):
"""Takes a float and returns a number with 2 dec"""
locale.setlocale(locale.LC_ALL, '')
if x == 0:
return ""
else:
return locale.format_string('%.2f', x, False)
def to_euroN(x):
"""Takes a float and returns a currency without decimal"""
locale.setlocale(locale.LC_ALL, '')
if x == 0:
return ""
else:
return locale.format_string('%.0f', x)
def to_percent(x):
"""Takes a float and returns a string"""
return ("%.2f " % x).replace('.', ',') + "%"
def to_date(date_text):
try:
madate = datetime.strptime(date_text, '%d/%m/%Y')
if madate.year >= 1900:
return madate
else:
return False
except ValueError:
return False
def to_time(date_text):
try:
madate = datetime.strptime(date_text, '%H:%M')
return madate
except ValueError:
return False
def to_str(s):
if s is None:
return ''
else:
return str(s)
# - - - - - - - - - - - - - - - - - - - - - - - - - -
@view_config(route_name='ajax_lookup')
def ajax_lookup(request):
recherche = request.GET['recherche']
type = recherche[:1] # 1er car.
name = recherche[1:]
if type == 'E':
# lire les élèves commencant par
items = get_eleves_not_b78_by_name(request, name, 10)
else:
# lire les propsects commencant par
items = get_prospects_by_name(request, name, False)
liste = []
for row in items:
if type == 'E':
d = "%s - %s | %s" % (row.nompren, row.permis_demande, str(row.cd_cli).zfill(6))
else:
d = "%s - %s | %s" % (row.nomprenom, row.permis_demande, str(row.cd_prospect).zfill(6))
liste.append(d)
return Response(json.dumps(liste))
@view_config(route_name='ajax_eleve')
def ajax_eleve(request):
recherche = request.GET['recherche']
items = get_eleves_ajax(request, recherche)
liste = []
for row in items:
d = "%s - %s | %s" % (row.nompren, row.permis_demande, str(row.cd_cli).zfill(6))
liste.append(d)
return Response(json.dumps(liste))
@view_config(route_name='ajax_lookupb78')
def ajax_lookupb78(request):
recherche = request.GET['recherche']
type = recherche[:1] # 1er car.
name = recherche[1:]
if type == 'E':
# lire les élèves commencant par
items = get_eleves_by_name78(request, name, 10)
else:
# lire les propsects commencant par
items = get_prospects_by_name(request, name, False)
liste = []
for row in items:
if type == 'E':
d = "%s - %s | %s" % (row.nompren, row.permis_demande, str(row.cd_cli).zfill(6))
else:
d = "%s - %s | %s" % (row.nomprenom, row.permis_demande, str(row.cd_prospect).zfill(6))
liste.append(d)
return Response(json.dumps(liste))
@view_config(route_name='ajax_moniteur')
def ajax_moniteur(request):
recherche = request.GET['recherche']
items = get_all_moniteur_active_by_name(request, recherche)
liste = []
for row in items:
code = row.CD_MON
name = row.NOM+ ' | ' + row.PLANNING
liste.append({"data": code, "value": name})
return Response(json.dumps(liste))
@view_config(route_name='ajax_codepostal')
def ajax_codepostal(request):
recherche = request.GET['recherche']
# lire les codes postaux commencant par
items = get_codespostaux(request, recherche)
liste = []
for row in items:
d = row.code_postal + " | " + row.libelle
liste.append({"value": d, "data": d})
return Response(json.dumps(liste))
@view_config(route_name='ajax_tarifs')
def ajax_tarifs(request):
recherche = request.GET['recherche']
# lire les tarifs commencant par
items = get_tarifs_byName(request, recherche)
liste = []
for row in items:
d = row.REF # + " - " + row.LIB
liste.append(d)
return Response(json.dumps(liste))
@view_config(route_name='ajax_type_devis', renderer='json')
def ajax_type_devis(request):
type_devis = request.POST['type_devis']
# lire le type de devis dans la BD
item = get_types_devis(request, type_devis)
if item:
montant = str(item.montantTTC)
montant_ht = str(item.montantHT)
nb_heures = str(item.nb_heures)
else:
montant = '0.00'
montant_ht = '0.00'
nb_heures = '0'
return {
'montant': montant,
'montant_ht': montant_ht,
'nb_heures': nb_heures,
}
@view_config(route_name='ajax_valid_formules')
def ajax_valid_formules(request):
permis = request.GET["permis"]
formules = get_all_formules(request, permis)
liste = []
for item in formules:
liste.append({"value": item.FORMULE, "data": item.FORMULE})
return Response(json.dumps(liste))
@view_config(route_name='ajax_formules')
def ajax_formules(request):
recherche = request.GET['recherche']
validite = 'EN-COURS'
# si validite a changé
if 'validite' in request.GET:
validite = request.GET["validite"]
# lire les formules
items = get_formules(request, recherche, validite)
# construire la liste
liste = []
for item in items:
liste.append({"value": item.FORMULE, "data": item.FORMULE})
# lire les codes postaux commencant par
return Response(json.dumps(liste))
@view_config(route_name='ajax_dept_nais')
def ajax_dept_nais(request):
recherche = request.GET['recherche']
items = get_dept_nais(request, recherche)
liste = []
for item in items:
liste.append({"value": item.code, "data": item.libelle})
return Response(json.dumps(liste))
@view_config(route_name='ajax_login')
def ajax_login(request):
login = request.params['login']
item = get_users_by_code(request, login)
data = {}
agences = []
if item != None:
data['find']= True
if item.mobile == True :
data['mobile']= True
list_agences = get_agences(request,0)
for agence in list_agences :
dict_agence ={
'code' : agence.CODE,
'libelle' : agence.LIBELLE
}
agences.append(dict_agence)
else:
data['mobile']= False
else:
data['find']= False
data['agence'] = agences
return Response(json.dumps(data))
@view_config(route_name='ajax_mode_regl')
def ajax_mode_regl(request):
recherche = request.GET['recherche']
items = get_mode_mode_regl(request, recherche)
# construire la liste
liste = []
for item in items:
liste.append({"value": item.LIBELLE, "data": item.CODE})
return Response(json.dumps(liste))
@view_config(route_name='ajax_libelle_regl')
def ajax_libelle_regl(request):
recherche = request.GET['recherche']
items = get_libelle_mode_regl(request, recherche)
# construire la liste
liste = []
for item in items:
liste.append({"value": item.LIBELLE, "data": item.CODE})
return Response(json.dumps(liste))
@view_config(route_name='ajax_activity')
def ajax_activity(request):
recherche = request.GET['recherche']
items = get_list_activity(request, recherche)
# construire la liste
liste = []
for item in items:
liste.append({"value": item.NOM, "data": item.CD_CLI, 'label': item.NOM})
return Response(json.dumps(liste))
def envoyerMail(request, destinataires, nom, objet, corps):
# si expediteur vide -> prendre le defaut
expediteur = request.registry.settings['aem_gestion.admin_email']
# ajouter entête et pied au corps du message
html_body = """
<p>Bonjour %s,</p>
%s
<p>
Cordialement,<br />
<b>Espace Moniteurs MARIETTON</b><br/>
<a href="https://moniteurs.marietton.com">https://moniteurs.marietton.com</a>
</p>
""" % (nom, corps)
message = Message(subject="[MARIETTON] %s" % objet,
sender=expediteur,
recipients=destinataires,
html=html_body)
mailer = get_mailer(request)
mailer.send_immediately(message, fail_silently=True)
@view_config(route_name='affiche_message', renderer='../templates/default/affiche_message.pt')
def affiche_message(request):
login = request.matchdict['login']
messages = request.session.pop_flash()
return {
'page_title': "Demande effectuée",
'login': login,
'messages': messages,
'url_identification': request.route_url('login_as', login=login)
}
def envoyerMailEleve(request, destinataires, objet, corps):
# si expediteur vide -> prendre le defaut
expediteur = request.registry.settings['aem_gestion.admin_email']
html_body = corps
message = Message(subject="[MARIETTON] %s" % objet,
sender=expediteur,
recipients=destinataires,
html=html_body)
mailer = get_mailer(request)
mailer.send_immediately(message, fail_silently=True)
@view_config(route_name='affiche_message', renderer='../templates/default/affiche_message.pt')
def affiche_message(request):
login = request.matchdict['login']
messages = request.session.pop_flash()
return {
'page_title': "Demande effectuée",
'login': login,
'messages': messages,
'url_identification': request.route_url('login_as', login=login)
}
@view_config(route_name='change_password', renderer='../templates/default/change_password.pt', permission='view')
def change_password(request):
url = request.route_url('change_password')
logged_in = request.authenticated_userid
message = ''
member = get_users_by_code(request, logged_in)
if 'form.submitted' in request.params:
old_password = request.params['old_password']
new_password = request.params['new_password1']
# On encode le password pour ne plus avoir de plantage avec les "é"
if (member.mdp_hash == to_sha1(old_password)):
update_password(request, logged_in, new_password)
request.session.flash("Votre mot de passe a été mis à jour avec succès.", 'success')
return HTTPFound(location=request.route_url('home'))
else:
message = "L'ancien mot de passe fourni est incorrect."
return {
'page_title': "Changer mon mot de passe",
'url': url,
'member': member,
'message': message,
}
@view_config(route_name='email_password', renderer='../templates/default/email_password.pt')
def email_password(request):
url = request.route_url('email_password')
message = ''
if 'form.submitted' in request.params:
login = request.params['login']
member = get_users_by_code(request, login)
if member:
if member.actif == 0:
message = "Le compte de cet utilisateur est désactivé. Contactez votre agence."
elif member.email:
# Fabrication du corps du Message
lien = update_membre_mdp_oublie(request, login)
body = """
<p>Vous avez oublié votre mot de passe. Par mesure de sécurité, nous vous demandons de bien vouloir le réinitialiser en cliquant sur ce lien :</p>
<p>%s</p>
<p>Ce lien ne sera valable que pendant 48 heures.</p>
""" % (request.route_url('redefinir_mdp', lien=lien))
destinataires = [member.email]
envoyerMail(request, destinataires, member.nom, "Oubli de mot de passe", body)
request.session.flash("Le lien permettant de redéfinir votre mot de passe vous a été envoyé à l'adresse : %s." % member.email, 'success')
return HTTPFound(location=request.route_url('affiche_message', login=login))
else:
message = "L'adresse e-mail de cet utilisateur n'est pas renseignée. Contactez votre agence."
else:
message = "Aucun utilisateur trouvé avec ce code. Veuillez réessayer."
return {
'page_title': "Mot de passe oublié ?",
'url': url,
'message': message,
}
@view_config(route_name='home', renderer='../templates/default/home.pt', permission='view')
def home(request):
def getChartColor(valeur):
# retourne la couleur du graphique selon la valeur
if valeur < 40:
color = 'red'
elif valeur < 50:
color = 'orange'
elif valeur < 60:
color = 'yellow'
else:
color = 'green'
return color
"""Home view"""
logged_in = request.authenticated_userid
member = get_users_by_code(request, logged_in)
now = datetime.now()
# examens = get_examen_by_agence(request,member.agence)
# récupère le nombre de messages non lus de l'utilisateur
try:
html = urlopen('https://suivi-eleve.marietton.com/MSG-nl.php?codeU=%s' % logged_in,timeout=30)
nlus = html.read()
except:
nlus = 0
barChart_annee_3 = []
mois_3 = now - relativedelta(months=+3)
mois_2 = now - relativedelta(months=+2)
mois_1 = now - relativedelta(months=+1)
mois = now - relativedelta(months=+0)
# lire les résultats exmanes mois M-2
items = get_charts_data(request, 'examens01', mois_3.strftime('%Y%m'))
# construire la liste pour barChart
barChart_annee_3 = []
barChart_annee_3.append(('Mode', '%', {'role': 'style'}, {'role': 'annotation'}, {'type': 'string','role': 'tooltip'}))
title_3 = ''
average = 0
sum_totale=0
sum_reussite=0
moyenne = 0
for item in items:
title_3 = item.group1_lib
average = round(item.moyenne)
valeur = round(item.valeur)
moyenne = item.moyenne
sum_totale = item.mar_totale
sum_reussite = item.mar_reussite
tooltip = 'Agence: '+item.group2_lib+'\nReussite: '+str(item.reussite)+'\nTotale: '+str(item.totale)+'\nTaux: '+str(round(item.valeur)) + ' %'
# construire la liste pour barChart cible
d = (item.group2_lib, valeur, getChartColor(valeur),'('+str(item.reussite)+'/'+str(item.totale)+')\xa0\xa0\xa0'+str(valeur) + ' %',tooltip)
barChart_annee_3.append(d)
# ajouter colonne MARIETTON
mar_tooltip = 'Marietton\nReussite: '+str(sum_reussite)+'\nTotale: '+str(sum_totale)+'\nTaux: '+str(round(moyenne)) + ' %'
d = ('MARIETTON', average, 'MidnightBlue','('+str(sum_reussite)+'/'+str(sum_totale)+')\xa0\xa0\xa0'+ str(average) + ' %', mar_tooltip)
barChart_annee_3.append(d)
# lire les résultats exmanes mois M-2
items = get_charts_data(request, 'examens01', mois_2.strftime('%Y%m'))
# construire la liste pour barChart
barChart_annee_2 = []
barChart_annee_2.append(('Mode', '%', {'role': 'style'}, {'role': 'annotation'}, {'type': 'string','role': 'tooltip'}))
title_2 = ''
average = 0
sum_totale=0
sum_reussite=0
moyenne = 0
for item in items:
title_2 = item.group1_lib
average = round(item.moyenne)
valeur = round(item.valeur)
moyenne = item.moyenne
sum_totale = item.mar_totale
sum_reussite = item.mar_reussite
tooltip = 'Agence: '+item.group2_lib+'\nReussite: '+str(item.reussite)+'\nTotale: '+str(item.totale)+'\nTaux: '+str(round(item.valeur)) + ' %'
# construire la liste pour barChart cible
d = (item.group2_lib, valeur, getChartColor(valeur),'('+str(item.reussite)+'/'+str(item.totale)+')\xa0\xa0\xa0'+str(valeur) + ' %',tooltip)
barChart_annee_2.append(d)
# ajouter colonne MARIETTON
mar_tooltip = 'Marietton\nReussite: '+str(sum_reussite)+'\nTotale: '+str(sum_totale)+'\nTaux: '+str(round(moyenne)) + ' %'
d = ('MARIETTON', average, 'MidnightBlue','('+str(sum_reussite)+'/'+str(sum_totale)+')\xa0\xa0\xa0'+ str(average) + ' %', mar_tooltip)
barChart_annee_2.append(d)
# lire les résultats exmanes mois M-
items = get_charts_data(request, 'examens01', mois_1.strftime('%Y%m'))
# construire la liste pour barChart
barChart_annee_1 = []
barChart_annee_1.append(('Mode', '%', {'role': 'style'}, {'role': 'annotation'}, {'type': 'string','role': 'tooltip'}))
title_1 = ''
average = 0
sum_totale = 0
sum_reussite = 0
moyenne = 0
for item in items:
title_1 = item.group1_lib
title_1h = str(round(item.moyenne))
average = round(item.moyenne)
valeur = (round(item.valeur))
totale = item.totale
reussite = item.reussite
moyenne = item.moyenne
sum_totale = item.mar_totale
sum_reussite = item.mar_reussite
tooltip = 'Agence: '+item.group2_lib+'\nReussite: ' + str(item.reussite)+'\nTotale: '+str(item.totale) + '\nTaux: '+str(round(item.valeur)) + ' %'
# construire la liste pour barChart cible
d = (item.group2_lib, valeur, getChartColor(valeur), '('+str(item.reussite)+'/'+str(item.totale)+')\xa0\xa0\xa0'+str(valeur) + ' %', tooltip)
barChart_annee_1.append(d)
mar_tooltip = 'Marietton\nReussite: '+str(sum_reussite)+'\nTotale: '+str(sum_totale)+'\nTaux: '+str(round(moyenne)) + ' %'
d = ('MARIETTON', average, 'MidnightBlue','('+str(sum_reussite)+'/'+str(sum_totale)+')\xa0\xa0\xa0'+ str(average) + ' %', mar_tooltip)
barChart_annee_1.append(d)
# lire les résultats exmanes mois M
items = get_charts_data(request, 'examens01', mois.strftime('%Y%m'))
barChart_annee = []
barChart_annee.append(('Mode', '%', {'role': 'style'}, {'role': 'annotation'}, {'type': 'string','role': 'tooltip'}))
title = ''
average = 0
sum_totale = 0
sum_reussite = 0
moyenne = 0
for item in items:
title = item.group1_lib
title_h = str(round(item.moyenne))
average = round(item.moyenne)
valeur = round(item.valeur)
totale = item.totale
reussite = item.reussite
moyenne = item.moyenne
sum_totale = item.mar_totale
sum_reussite = item.mar_reussite
tooltip = 'Agence: '+item.group2_lib+'\nReussite: ' + str(item.reussite)+'\nTotale: '+str(item.totale) + '\nTaux: '+str(round(item.valeur)) + ' %'
# construire la liste pour barChart cible
d = (item.group2_lib, valeur, getChartColor(valeur),'('+str(item.reussite)+'/'+str(item.totale)+')\xa0\xa0\xa0'+ str(valeur) + ' %', tooltip)
barChart_annee.append(d)
# ajouter colonne MARIETTON
mar_tooltip = 'Marietton\nReussite: '+str(sum_reussite)+'\nTotale: '+str(sum_totale)+'\nTaux: '+str(round(moyenne)) + ' %'
d = ('MARIETTON', average, 'MidnightBlue','('+str(sum_reussite)+'/'+str(sum_totale)+')\xa0\xa0\xa0'+ str(average) + ' %', mar_tooltip)
barChart_annee.append(d)
return {
'page_title': 'Bienvenue %s (Agence %s)' % (member.nom, member.agence),
'logged_in': logged_in,
'nlus': nlus,
'bar_annee_3': json.dumps(barChart_annee_3),
'bar_annee_2': json.dumps(barChart_annee_2),
'bar_annee_1': json.dumps(barChart_annee_1),
'bar_annee': json.dumps(barChart_annee),
'title': title,
'title_1': title_1,
'title_2': title_2,
'title_3': title_3,
'access': member.secu,
}
@view_config(route_name='login', renderer='../templates/default/login.pt', permission='view')
@view_config(route_name='login_as', renderer='../templates/default/login.pt', permission='view')
@view_config(route_name='login_from', renderer='../templates/default/login.pt', permission='view')
@forbidden_view_config(renderer='../templates/default/login.pt')
def login(request):
device = ''
login = ''
current_route_path = request.current_route_path()
if 'login_as' in current_route_path:
# memoriser l'identifiant du user
login = request.matchdict['login']
login_url = request.route_url('login_as', login=login)
elif 'login_from' in current_route_path:
logged_in = request.authenticated_userid
# if user is already logged in, go to home
if logged_in is not None:
return HTTPFound(location=request.route_url('home'))
# memoriser l'identifiant de la tablette
device = request.matchdict['device']
login_url = request.route_url('login_from', device=device)
else:
login_url = request.route_url('login')
referrer = request.url
if referrer == login_url:
referrer = '/' # never use the login form itself as came_from
came_from = request.params.get('came_from', referrer)
message = ''
password = ''
if 'form.submitted' in request.params:
login = request.params['login'].upper()
password = request.params['password']
record = get_users_by_code(request, login)
if record:
# controler que la fiche n'est pas cloturee
if record.actif != 0:
# On encode le password pour ne plus avoir de plantage avec les "é"
if record.mdp_hash == to_sha1(password):
# get user agent string from request
ua_string = request.user_agent
user_agent = parse(ua_string)
update_last_connection(request, login, user_agent.device.family + ' / ' + device)
# Update agence
if 'agence' in request.params:
agence = request.params['agence']
update_user_agency(request,record.cd_uti,agence)
if 'login_from' in current_route_path:
try:
urlopen(
'https://suivi-eleve.marietton.com/log_tablet.php?codeU=%s&device=%s' % (login, device),
timeout=30)
except Exception as e:
pass
# force le commit car il ne se fait pas automatiquement après l'update
transaction.commit()
headers = remember(request, login)
return HTTPFound(location=came_from, headers=headers)
else:
message = "Le code utilisateur ou le mot de passe est incorrect. Identification échouée."
else:
message = "Votre code utilisateur a été désactivé. Accès refusé."
else:
message = "Le code utilisateur ou le mot de passe est incorrect. Identification échouée."
return {
'page_title': "",
'message': message,
'url': login_url,
'came_from': came_from,
'login': login,
'password': password,
'device': device,
}
@view_config(route_name='logout')
def logout(request):
headers = forget(request)
request.session.flash("Vous avez bien été déconnecté.", 'success')
return HTTPFound(location=request.route_url('login', login=''),
headers=headers)
@view_config(route_name='connecter_a', permission='manage')
def connecter_a(request):
# paramètre fourni ?
login = request.matchdict["login"]
if len(login) > 0:
request.session.invalidate()
headers = forget(request)
headers = remember(request, login)
return HTTPFound(location="/", headers=headers)
else:
return HTTPFound(location="/")
@view_config(route_name='redefinir_mdp', renderer='..//templates/default/redefinir_mdp.pt')
def redefinir_mdp(request):
lien = request.matchdict["lien"]
# tester si le champ "motdepasse_oublie" est encore valide
if is_lien_mdp_oublie(request, lien) == True:
membre = get_mdp_oublie_infos(request, lien)
if 'form.submitted' in request.params:
mdp = request.params["new_password1"]
update_password(request, membre.cd_uti, mdp)
request.session.flash("Votre mot de passe a bien été mis à jour avec succès.", 'success')
return HTTPFound(location=request.route_url('login'))
else:
return HTTPFound(location=request.route_url('login'))
return {
'page_title': "Redéfinir le mot de passe",
}
@view_config(route_name='change_agency', renderer='../templates/default/change_agency.pt', permission='view')
def change_agency(request):
cd_uti = request.authenticated_userid
url = request.route_url('change_agency')
message = ''
agences = get_agences(request, 0)
# lire la fiche de l'individu
individu = get_users_by_code(request, cd_uti)
if not individu:
request.session.flash("Utilisateur non trouvé : %s" % cd_uti, 'warning')
return HTTPFound(location=request.route_url('home'))
if 'form.submitted' in request.params:
agence = request.params["agence"]
update_user_agency(request, cd_uti, agence)
request.session.flash("L'agence a été mise à jour avec succès.", 'success')
return HTTPFound(location=request.route_url('home'))
return {
'page_title': "Changement d'agence",
'url': url,
'individu': individu,
'agences': agences,
'message': message,
}

1471
aem_gestion/views/eleves.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,437 @@
# -*- coding: utf8 -*-
from pyramid.view import (
view_config,
)
from pyramid.httpexceptions import (
HTTPFound,
)
from datetime import date, datetime
from dateutil.relativedelta import *
from urllib.request import urlopen
import json
import os
import shutil
import imaplib
import email
from ..models.examens import *
from ..models.default import *
@view_config(route_name='examens_schd', renderer='../templates/examens/examens_schd.pt', permission='view')
def examens_schd(request):
def generer_planning(datedeb, permis):
rows = get_examens_byDate(request, datedeb, permis)
# construire la liste des events
events = []
for row in rows:
# déterminer la couleur de l'event
color = get_examens_aff_color(request, row)
debut = datetime.strptime('%s %s:%s:00' % (row.DATE.strftime('%Y-%m-%d'), row.HEURE.seconds//3600, (row.HEURE.seconds//60)%60),
'%Y-%m-%d %H:%M:00')
fin = debut + relativedelta(minutes = 60)
agence = getAgenceLib(request, row.AGENCE)
json_event = {
'title': '%s à %s' % (row.NO_EXA, row.LIEU),
'start': debut.strftime('%Y-%m-%d %H:%M:00'),
'end': fin.strftime('%Y-%m-%d %H:%M:00'),
'description': '%s | %s' % (row.CD_MON, agence[7:]),
'allDay': False,
'color': color,
'textColor': '#000000',
'url': '/examen_list/%s/%s' % (row.DATE.strftime('%Y-%m-%d'), row.NO_EXA),
}
events.append(json_event)
return json.dumps(events)
# récupérer les paramètres de l'appel de la view
datePlan = request.matchdict['date']
permis = request.matchdict['permis']
logged_in = request.authenticated_userid
types_permis = ['ETG','B','2R','GL']
if datePlan == 'today':
datePlan = date.today().strftime('%Y-%m-%d')
# si permis a été changé par le user
if 'permis' in request.params:
permis = request.params["permis"]
url = request.route_url('examens_schd', date=datePlan, permis=permis)
TODAY = date.today()
# début = aujourd'hui - 4 semaines
d = TODAY + relativedelta(weeks=-8)
datedeb = d.strftime('%Y-%m-%d')
# generer le planning
calendar_events = generer_planning(datedeb, permis)
return {
'page_title': "Examens %s" % permis,
'url': url,
'datePlan': datePlan,
'permis': permis,
'types_permis': types_permis,
'calendar_events': calendar_events,
}
@view_config(route_name='examen_list', renderer='../templates/examens/examen_list.pt', permission='view')
def examen_list(request):
date_exa = request.matchdict['date_exa']
no_exa = request.matchdict['no_exa']
# convertir date_exa en format date
date = datetime.strptime(date_exa, '%Y-%m-%d')
# lire examen
examen = get_examens(request, date_exa, no_exa)
if examen:
lieu_exa = examen.LIEU
permis = examen.PERMIS
accompgntr = examen.CD_MON
dateheure = datetime.strptime('%s %s:%s:00' % (examen.DATE.strftime('%Y-%m-%d'), examen.HEURE.seconds//3600, (examen.HEURE.seconds//60)%60),
'%Y-%m-%d %H:%M:00').strftime('%d-%m-%Y %H h')
else:
lieu_exa = 'Non trouvé'
permis = ""
accompgntr = ""
dateheure = ""
# lire les inscrits dans examen
items = get_examens_aff(request, date_exa, no_exa)
total_unites = 0
# construire la liste
liste=[]
for item in items:
# cumuler le total d'unité
total_unites += item.UNITE
if item.TR_T_OK:
ETG_OK = item.TR_T_OK.strftime('%d-%m-%Y')
else:
ETG_OK = ""
tel = ''
if item.TEL:
if len(item.TEL) > 0:
tel += item.TEL + ' '
if item.TEL2:
if len(item.TEL2) > 0:
tel += item.TEL2 + ' '
if item.TEL3:
if len(item.TEL3) > 0:
tel += 'P:' + item.TEL3 + ' '
if item.TEL4:
if len(item.TEL4) > 0:
tel += 'M:' + item.TEL4 + ' '
agence = getAgenceLib(request, item.AGENCE)
d = (agence[7:10], '%s - %s' % ("%06d" % item.CD_CLI, item.NOM), item.NOM_JF, item.filiere, item.CD_MON, tel, ETG_OK,
'%s - %s' % (item.INDICE, item.UNITE))
liste.append(d)
return {
'page_title': 'Examen N° %s du %s' % (no_exa, dateheure),
'lieu_exa': lieu_exa,
'permis': permis,
'total_unites': total_unites,
'accompgntr': accompgntr,
'dt_data': json.dumps(liste),
}
@view_config(route_name='results_import', renderer='../templates/examens/results_import.pt', permission='view')
def results_import(request):
def get_emails(lecture):
# créer la liste des entêtes des messages
liste = []
# connecter au serveur de mail
conn, data = mailbox_connect(request)
if conn == None:
request.session.flash("ERREUR de lecture de la boîte de réception", 'danger')
return liste
mail_ids = data[0]
for email_UID in mail_ids.split():
rv, msg_data = conn.fetch(email_UID, '(RFC822)')
if rv != 'OK':
request.session.flash("ERREUR de lecture du message %s" % email_UID, 'danger')
return liste
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 = {
"email_date": email_date,
"email_from": email_from.split('@')[1],
'email_subject':email_subject,
"email_uid": email_UID
}
liste.append(d)
if lecture == 'first':
break
# deconnexion du serveur
conn.close()
conn.logout()
return liste
def analyser_email(body):
neph = ""
date_exa = ''
resultat_exa = ''
# scan le body pour extraire les infos utiles
body = body.decode('latin-1')
# analyser la phrase suivante:
# Le candidat n°210269100510 que vous avez présenté à l'examen pratique (BC) du 19/07/2021 a été ajourné.
# récupère le neph (12 car.)
i = body.find('Le candidat n°')
if i > 0:
neph = body[i+14:i+26]
# récupère la date examen (10 car.)
i = body.find('du ', i)
if i > 0:
date_exa = body[i+3:i+13]
# récupère le résultat
if body.find("a été reçu") > 0:
resultat_exa = 10 # "BON"
resultat_lib = "BON"
elif body.find("a été ajourné") > 0:
resultat_exa = 1 # "ECHEC"
resultat_lib = "ECHEC"
else:
resultat_exa = 7 # "ABS"
resultat_lib = "ABS. EXCUSEE"
return neph, date_exa, resultat_exa, resultat_lib
def import_email(request, email_uid):
# importer le résulat dans la BD
# retour :
# - "completed" : importation réussi
# - "not_found" : éléve non trouvé
# - "error" : erreur détectée
# connecter au serveur de mail
conn, data = mailbox_connect(request)
if conn == None:
request.session.flash("ERREUR de lecture de la boîte de réception", 'danger')
return "error"
# lire le message avec UID
rv, msg_data = conn.fetch(email_uid, '(RFC822)')
if rv != 'OK':
request.session.flash("ERREUR de lecture du message %S" % (email_uid), 'danger')
return "error"
email_message = email.message_from_bytes(msg_data[0][1])
# 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/plain' and 'attachment' not in cdispo:
body = part.get_payload(decode=True) # decode
break
# Analyser le message pour récupérer les infos utiles
neph, date_exa, resultat_exa, resultat_lib = analyser_email(body)
# convertir la date en 'yyyy/mm/dd'
dateexa = datetime.strptime(date_exa, '%d/%m/%Y')
dateexa = dateexa.strftime('%Y-%m-%d')
if neph != '':
# éléve trouvé ?
eleve = get_eleves_by_neph(request, neph)
if eleve:
update_resultat(request, eleve.CD_CLI, dateexa, resultat_exa, logged_in)
try:
# envoyer un message interne au moniteur référent s'il
if len(eleve.CD_MON) > 0:
urlopen('https://Suivi-eleve.marietton.com/MSG-fromAEM.php?codeE=%s&type=cepcOK&date=%s&dest=%s&resultat=%s'
% (eleve.CD_CLI, dateexa, eleve.CD_MON, resultat_lib),timeout=30 )
except:
pass
else:
request.session.flash("NEPH %s non trouvé dans la BD, impossible d'importer l'examen" % neph, 'danger')
return "not_found"
# downloading attachment
temp_file_path = download_pdf_to_tmp(email_message)
if temp_file_path != '':
# si attachement existe, verifier qu'il n'est pas déjà importé
next_code = get_next_cepc(request, eleve.CD_CLI, dateexa)
if len(next_code) > 0:
# fabriquer le nom du document
filename = '%s_%s_%s_%s.PDF' % (eleve.CD_CLI, 'DOC', next_code, dateexa)
# l'insérer dans la fiche élève
cepcFile2Dossier(request, eleve.CD_CLI, temp_file_path, filename, next_code, logged_in)
# déplacer le message dans la corbeille
conn.store(email_uid, '+FLAGS', '\\Deleted')
conn.expunge()
conn.close()
# deconnexion du serveur
conn.logout()
return "completed"
# ------- main -------
logged_in = request.authenticated_userid.upper()
url = request.route_url('results_import')
message = ''
# -- importer les emails
if 'form.import' in request.params:
nbTraite = 0
for i in range(100):
# lire le premier email des résultats pratique
message = get_emails('first')
if message:
nbTraite += 1
res = import_email(request, message[0]['email_uid'])
if res == "not_found":
# si élève non trouvé, arrêter l'import
break
else:
# si plus d'email, arrêter
break
# message de fin
request.session.flash("%s Résultat(s) mis à jour avec succès." % str(nbTraite), 'success')
# lister les messages reçus
emails = []
# emails = emails + get_emails('objectif_code', 'all')
emails = emails + get_emails('all')
# ister les résultats mis à jour
examens_maj = get_examens_maj(request, logged_in)
return {
'page_title': "Liste des emails de résultats d'examen",
'url': url,
'emails': emails,
'examens_maj': examens_maj,
}
def download_pdf_to_tmp(email_message):
# import pdb;pdb.set_trace()
temp_file_path = ''
# 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 cepcFile2Dossier(request, cd_cli, temp_file, filename, next_code, logged_in):
# créer le répertoire de eleve
path = '%s/%s' % (request.registry.settings['aem_gestion.justifs_dir'], cd_cli)
os.makedirs(path, exist_ok=True)
filepath = os.path.join('%s/%s' % (path, filename))
# Finally move the temporary file to folder
shutil.move(temp_file, filepath)
filesize = round(os.path.getsize(filepath) / 1024)
insert_eleve_cepc(request, cd_cli, next_code, filename, filesize, logged_in)
@view_config(route_name='result_del', renderer='../templates/examens/result_del.pt', permission='view')
def result_del(request):
email_uid = request.matchdict['email_uid']
url = request.route_url('result_del',email_uid=email_uid)
message = ""
if 'form.submitted' in request.params:
# connecter au serveur de mail
conn, data = mailbox_connect(request)
# déplacer le message dans la corbeille
conn.store(email_uid, '+FLAGS', '\\Deleted')
# deconnexion du serveur
conn.close()
conn.logout()
request.session.flash("Le message %s a été supprimée avec succès." % email_uid, 'success')
return HTTPFound(location=request.route_url('results_import'))
return {
'page_title': "Supprimer le message %s" % email_uid,
'url': url,
'message': message,
}
def mailbox_connect(request):
# Cette fonction effectue :
# 1. la connexion au serveur de mail
# 2. la lecture des messages de INBOX
# puis retourne 2 résultats :
# - conn : la connexion ou None si erreur rencontré
# - data : la liste UID des messages
# connecter au serveur IMAP
mbx_name = 'resultats-examens@marietton.com'
mbx_pwd = '9KmKPn36a'
conn = imaplib.IMAP4_SSL('SSL0.OVH.NET', 993)
#mbx_name = 'phuoc@caotek.fr'
#mbx_pwd = 'pcao.8211'
#conn = imaplib.IMAP4_SSL('imap.gmail.com')
try:
# se connecter à la mailbox
conn.login(mbx_name, mbx_pwd)
except imaplib.IMAP4.error:
request.session.flash("ERREUR d'authentification à %s" % mbx_name, 'danger')
return None, None
# select INBOX
conn.select('INBOX')
criteria = 'SUBJECT "des examens du permis de conduire" UNDELETED'
rv, data = conn.search(None, criteria)
if rv != 'OK':
request.session.flash("ERREUR de lecture de la boîte de réception", 'danger')
return None, None
return conn, data

View File

@@ -0,0 +1,9 @@
# -*- coding: utf8 -*-
from pyramid.view import notfound_view_config
@notfound_view_config(renderer='../templates/default/404.pt')
def notfound_view(request):
request.response.status = 404
return {
'page_title': 'Oups!',
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

414
aem_gestion/views/utils.py Normal file
View File

@@ -0,0 +1,414 @@
# -*- coding: utf8 -*-
import json
import os
from pyramid.response import Response
from pyramid.view import (
view_config,
)
from pyramid_mailer import get_mailer
from pyramid_mailer.message import Message
import requests
from time import sleep
from datetime import datetime as dt
from ..models.utils import *
from ..models.default import *
@view_config(route_name='batch_nuit')
def batch_nuit(request):
"""
Ce traitement est lancé chaque nuit, sur le serveur du site web, par une tâche planifiée :
- Executer en tant que : root
- Commande : wget http://localhost:6544/batch_nuit/JonSn0w
- Quand executer : à 02:00 chaque jour
Par sécurité, ce view ne peut être appelé qu'avec un paramètre secret 'JonSn0w'
"""
# contrôle : paramètre correct ? non, terminer
par = request.matchdict['param']
if par != 'JonSn0w':
return Response('Erreur : paramètre incorrect')
# ----- effacer le log
truncate_log(request)
# ----- Début TRAITEMENT DE NUIT
insert_log(request, '****','- Début TRAITEMENT DE NUIT')
# ----- LETTRAGE AUTOMATIQUE
insert_log(request, 'LETTRAGE','- Début LETTRAGE AUTO')
nbLus = lettrage_auto(request)
insert_log(request, 'LETTRAGE','- Fin LETTRAGE : ' + str(nbLus) + ' élèves lus')
# ----- MAJ des statistiques examens
insert_log(request, 'STATS','- Début Mise à jour des STATS EXAMENS')
update_statistiques(request)
# ----- PURGE des données obsolètes
insert_log(request, 'PURGE','- Début PURGE DES DONNEES OBSOLETES')
purge_obsoletes(request)
# ----- CONFIRMATION DES RESERVATIONS HEURES
TODAY = date.today()
if TODAY.weekday() == 0 :
# ===== si lundi, pas de confirmation des RESA ===================
insert_log(request, 'RESA', '- Pas de CONFIRMATION des RESA le LUNDI')
else:
insert_log(request, 'RESA', '- Début CONFIRMATION des RESA HEURES')
confirm_reservations(request)
# ----- RAPPELS DES RENDEZ-VOUS
update_rappels(request)
# attendre 5 secondes
sleep(5)
# log : Début ENVOI emails RESA
insert_log(request, 'RESA','- Début ENVOI emails')
# ----- lancer les notifications
nbLus, nbConfirmes, nbAnnules = notifier_reservations(request, 'HEURES')
# log : fin ENVOI emails RESA
insert_log(request, 'RESA', "- Fin ENVOI emails : %s résa lus, %s confirmées, %s annulées." % (str(nbLus), str(nbConfirmes), str(nbAnnules)))
insert_log(request, '****', '- Fin TRAITEMENT DE NUIT')
# ----- ENVOI RAPPORTS di traitement
email_rapport(request)
return Response('Batch nuit terminé OK')
def notifier_rappels(request):
# log : Début ENVOI emails
insert_log(request, 'RAPPELS','- Début ENVOI emails')
# lire les rappels non encore envoyés
rappels = get_email_rappels(request)
nbLus = 0
nbLecons = 0
nbExamens = 0
for item in rappels:
nbLus += 1
# RDV ayant une heure (HCB) or(HCB78) ?
if item.rdv_date.hour > 0 :
date_heure = item.rdv_date.strftime('%d/%m/%Y - %H:%M')
else:
# RDV ayant une heure
date_heure = item.rdv_date.strftime('%d/%m/%Y')
# si examen 2R, notifier from Vaugneray sinon from agence de l'élève
if item.ref == "EPA" :
noAgence = 6
else:
noAgence = item.agence
szBody = get_msg_body(request, item, date_heure)
destinataires = [item.email]
if item.payeur_email:
destinataires.append(item.payeur_email)
reponse = email_notification(request, "RAPPEL : rendez-vous le " + date_heure, szBody, destinataires, noAgence)
if reponse == 0:
# marquer le rdv comme envoyé ou traité
update_email_rappels(request, item.no_id)
# rappels pour examens ?
if item.ref in ['EPA', 'EPB', 'EPC', 'ECHECT'] :
nbExamens += 1
else:
nbLecons += 1
if nbLus == 150:
# si quota atteint, arrêter les emails
insert_log(request, 'RAPPELS', "- Fin ENVOI emails : %s rdv lus, %s rappels leçons, %s rppels examens." % (str(nbLus), str(nbLecons), str(nbExamens)))
return
else:
# sinon on continue après une pause de 3 secondes, envoi de 15 emails par minute
sleep(3)
insert_log(request, 'RAPPELS', "- Fin ENVOI emails : %s rdv lus, %s rappels leçons, %s rppels examens." % (str(nbLus), str(nbLecons), str(nbExamens)))
return
def email_notification(request, objet, corps, destinataires, code):
# lire l'agence
agence = get_agences(request, code)
if agence:
expediteur = agence.email
else:
expediteur = "circuit@marietton.com"
# si annulation de résa, CC à l'agence de l'élève
if objet.find(' ANNULEE, ') > 0:
destinataires.append(expediteur)
reponse = 0
reponse = send_mail(request, expediteur, destinataires, objet, corps)
return reponse
def email_rapport(request):
NOW = dt.now()
corps = "<html><body><p>=============================================</p>"
corps += "<p>Rapport du traitement de nuit du " + NOW.strftime('%d/%m/%Y - %H:%M') + "</p>"
corps += "<p>=============================================</p><p></p><p>"
# Lire le fichier log
items = get_log(request)
for item in items:
corps += " - " + item.date.strftime('%d/%m/%Y - %H:%M') + " - " + item.proc + " : " + item.msg + "<br />"
corps += "</p><p></p><p>=============================================</p><p></p>"
expediteur = request.registry.settings['aem_gestion.admin_email']
destinataire = ["francois.varnier@groovelsoftware.com","comptabilite@marietton.com"]
send_mail(request, expediteur, destinataire, "Rapport des traitements de nuit", corps)
return
def send_mail(request, expediteur, destinataires, objet, corps):
body = """
%s
""" % (corps)
message = Message(subject=objet,
sender=expediteur,
recipients=destinataires,
html=body)
mailer = get_mailer(request)
# import pdb;pdb.set_trace()
msg = ''
try:
mailer.send_immediately(message)
except Exception as e:
# Just print(e) is cleaner and more likely what you want,
# but if you insist on printing message specifically whenever possible...
if hasattr(e, 'message'):
msg = e.message
else:
msg = e
# logguer l'erreur
insert_log(request, 'MAILER', "- ERROR : %s, TO : %s" % (msg[0:512], destinataires))
return len(str(msg))
def get_msg_body(request, rappel, date_heure):
szLieu = rappel.rdv_lieu
if szLieu.find("GORGE DE LOUP") > 0:
szLieu = szLieu + " (<a href=""https://monespace.marietton.com/faq_view/2771""><b>Navette pour Vaugneray</b></a>)"
# lire le message selon la référence du tarif
message = get_message(request, rappel.ref)
if message:
szBody = message.rappel_html
else:
szBody = "<blockquote>Rappel de rendez-vous " + rappel.ref + "</blockquote>"
# remplacer les mots clés
szBody = szBody.replace("!civNomPrenom!", rappel.nompren)
szBody = szBody.replace("!codeClient!", str(rappel.cd_cli))
szBody = szBody.replace("!dateRDV!", date_heure)
szBody = szBody.replace("!lieuRDV!", szLieu)
if rappel.rdv_statut:
szBody = szBody.replace("!statutRDV!", rappel.rdv_statut)
return szBody
def notifier_reservations(request, type_resa):
# lire les rappels non encore envoyés
rappels = get_email_resa(request, type_resa)
nbLus = 0
nbConfirmes = 0
nbAnnules = 0
for item in rappels:
# import pdb;pdb.set_trace()
nbLus += 1
# Résa ayant une heure (HCB) ?
if item.resa_date.hour > 0 :
date_heure = item.resa_date.strftime('%d/%m/%Y - %H:%M')
else:
date_heure = item.resa_date.strftime('%d/%m/%Y')
noAgence = item.agence
if item.resa_statut == "CONFIRMEE" :
nbConfirmes += 1
confirm = "CONFIRMEE"
else:
nbAnnules += 1
confirm = "ANNULEE, faute de crédit suffisant"
if item.ref == "HCA3" or item.resa_type == "Inscr.":
# envoyer CC à l'agence Vaugneray
noAgence = 6
# selon le type de résa, préparer le message
if item.resa_type == "Inscr.":
# résa de stage effectuée sur le WEB
message = "<p>Nous vous informons que votre inscription au stage %s du %s a été %s.</p>" % (item.ref, date_heure, confirm)
elif item.resa_type == "WEB_48H.":
# heure annulée en moins 48 heures sur le WEB remplacée
message = "<p>Nous vous informons que l'annulation de votre leçon de %s du %s ne vous sera pas débitée " + \
"car nous avons pu vous trouver un remplaçant.</p>" % (item.ref, date_heure)
elif item.resa_type in ["WEB_A", "WEB_B"] :
# résa d'heure A ou B effectuée sur le WEB
message = "<p>Nous vous informons que votre inscription au stage %s du %s a été %s.</p>" % (item.ref, date_heure, confirm)
else:
# résa d'heure A ou B effectuée en agence
message = "<p>Nous vous informons qu'une réservation de leçon a été <b>" + confirm + "</b>.</p>"
szBody = "<html><body><p>Bonjour " + item.nompren + ",</p>" + message + \
"<p>Merci de consulter votre carnet de rendez-vous remis à jour sur votre espace : " + \
"<a href=""http://monespace.marietton.com"">monespace.marietton.com</a></p>" + \
"<p>Nous vous rappelons votre code élève pour vous connecter : <b>" + str(item.cd_cli) + "</b></p>" + \
"<p>Veuillez agréer nos sincères salutations.</p>" + \
"<p>Votre équipe Marietton</p></body></html>"
destinataires = [item.email]
if len(item.payeur_email) > 0:
destinataires.append(item.payeur_email)
# import pdb;pdb.set_trace()
email_notification(request, "Réservation de leçon " + confirm, szBody, destinataires, noAgence)
# attendre 6 secondes, envoi de 10 emails par minute
sleep(6)
# marquer le rdv comme envoyé ou traité
update_email_resa(request, item.no_id)
# si résa WEB, logguer la confirmation
if type_resa == 'INSCR':
insert_log(request, item.resa_type, "%s -> %s : %s - %s" % (item.resa_type, confirm, str(item.cd_cli), item.nompren))
return nbLus, nbConfirmes, nbAnnules
def confirm_reservations(request):
"""
- LIRE LES RESA des PLANNINGS B et A
- REMPLIR la table EMAILS_RESA
"""
TODAY = date.today()
# LIRE LES RESA des PLANNINGS B et A
resas = get_reservations(request)
for resa in resas:
# --------------- PLANNING B et B78
if resa.ref == "HCB" or resa.ref == "HCB78":
# date de la leçon antérieur à aujourd'hui, confirmer de suite
if TODAY > resa.date :
# confimer la résa
update_planning_B_confirm(request, 'C', resa.no_ligne, resa.cd_cli)
else:
# controler le solde REEL de l'élève et le total des resa
eleve = get_solde_eleve(request, resa.cd_cli, resa.ref)
if eleve.solde + eleve.total_resa >= resa.debit :
# confirmer le rdv
update_planning_B_confirm(request, 'C', resa.no_ligne, resa.cd_cli)
# creer une notification de confirmation
insert_email_resa(request, resa.ref, resa.cd_cli, "d'heure "+resa.ref[2:], resa.date, resa.noplan, "CONFIRMEE")
else:
# date de fin de résa atteinte ?
if TODAY > resa.fin_reservation :
# annuler la résa
update_planning_B_confirm(request, 'A', resa.no_ligne, resa.cd_cli)
# creer une notification de annulation
insert_email_resa(request, resa.ref, resa.cd_cli, "d'heure "+resa.ref[2:], resa.date, resa.noplan, "ANNULEE")
# ------------ HEURES PLANNING A
elif resa.ref in ["TA", "TB","HCA3", "HCA4"] :
# date de la leçon antérieure à aujourd'hui ? Oui, confirmer de suite
if TODAY > resa.date :
# confirmer le rdv
update_planning_A_confirm(request, "C", resa.no_ligne, resa.cd_cli)
else:
# controler le solde REEL de l'élève
eleve = get_solde_eleve(request, resa.cd_cli, resa.ref)
if eleve.solde + eleve.total_resa >= resa.debit :
# confirmer le rdv
update_planning_A_confirm(request, "C", resa.no_ligne, resa.cd_cli)
# creer une notification de confirmation
insert_email_resa(request, resa.ref, resa.cd_cli, "Moto", resa.date, 0, "CONFIRMEE")
else:
# date de fin de résa atteinte ?
if TODAY > resa.fin_reservation :
# annuler la résa
update_planning_A_confirm(request, 'A', resa.no_ligne, resa.cd_cli)
# creer une notification de annulation
insert_email_resa(request, resa.ref, resa.cd_cli, "Moto", resa.date, 0, "ANNULEE")
@view_config(route_name='batch_email')
def batch_email(request):
"""
Ce traitement est lancé chaque nuit, sur le serveur du site web, par une tâche planifiée :
- Executer en tant que : root
- Commande : wget http://localhost:6544/batch_email/JonSn0w
- Quand executer : chaque jour, à 03:00 et 04:30
Par sécurité, ce view ne peut être appelé qu'avec un paramètre secret 'JonSn0w'
A cause du quota d'envoi limité à 200 emails par heure imposé par OVH,
ce traitement envoie les notifications par email par lot de 150 emails.
Il sera lancé 2 fois par nuit, chaque fois espacé de 1h30
"""
# contrôle : paramètre correct ? non, terminer
par = request.matchdict['param']
if par != 'JonSn0w':
return Response('Erreur : paramètre incorrect')
# ----- envoyer les rappels
notifier_rappels(request)
@view_config(route_name='batch_test')
def batch_test(request):
"""
Ce traitement sert à tester une fonction particulière du batch de nuit ou de jour :
- Commande : http://localhost:6544/batch_test/JonSn0w
Par sécurité, ce view ne peut être appelé qu'avec un paramètre secret 'JonSn0w'
"""
# contrôle : paramètre correct ? non, terminer
par = request.matchdict['param']
if par != 'JonSn0w':
return Response('Erreur : paramètre incorrect')
# ----- CONFIRMATION DES RESERVATIONS HEURES
confirm_reservations(request)
@view_config(route_name='batch_justif')
def batch_justif(request):
"""
Ce traitement sert à tester une fonction particulière du batch de nuit ou de jour :
- Commande : http://localhost:6544/batch_justif/JonSn0w
Par sécurité, ce view ne peut être appelé qu'avec un paramètre secret 'JonSn0w'
"""
# contrôle : paramètre correct ? non, terminer
par = request.matchdict['param']
if par != 'JonSn0w':
return Response('Erreur : paramètre incorrect')
all_justifs = get_justify_not_found(request, datetime.strptime('2021-09-01', '%Y-%m-%d'))
base_folder = str(request.registry.settings['aem_gestion.justifs_dir'])
data_not_found = {}
for justif in all_justifs:
try:
justif_path = os.path.join(base_folder, str(justif.cd_cli), str(justif.nom_fic))
if not os.path.isfile(justif_path):
# update_justify_not_found(request,justif.no_ligne)
insert_log(request, 'justif_CKECK', str(justif.no_ligne) + '->' + justif_path)
data_not_found[str(justif.no_ligne)] = justif_path
except Exception as e:
pass
return Response(json.dumps(data_not_found))