Files
aem_moniteurs/aem_gestion/views/utils.py
2023-06-22 10:34:18 +02:00

415 lines
16 KiB
Python

# -*- 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))