# -*- 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 = "

=============================================

" corps += "

Rapport du traitement de nuit du " + NOW.strftime('%d/%m/%Y - %H:%M') + "

" corps += "

=============================================

" # 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 + "
" corps += "

=============================================

" 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 + " (Navette pour Vaugneray)" # lire le message selon la référence du tarif message = get_message(request, rappel.ref) if message: szBody = message.rappel_html else: szBody = "
Rappel de rendez-vous " + rappel.ref + "
" # 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 = "

Nous vous informons que votre inscription au stage %s du %s a été %s.

" % (item.ref, date_heure, confirm) elif item.resa_type == "WEB_48H.": # heure annulée en moins 48 heures sur le WEB remplacée message = "

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.

" % (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 = "

Nous vous informons que votre inscription au stage %s du %s a été %s.

" % (item.ref, date_heure, confirm) else: # résa d'heure A ou B effectuée en agence message = "

Nous vous informons qu'une réservation de leçon a été " + confirm + ".

" szBody = "

Bonjour " + item.nompren + ",

" + message + \ "

Merci de consulter votre carnet de rendez-vous remis à jour sur votre espace : " + \ "monespace.marietton.com

" + \ "

Nous vous rappelons votre code élève pour vous connecter : " + str(item.cd_cli) + "

" + \ "

Veuillez agréer nos sincères salutations.

" + \ "

Votre équipe Marietton

" 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))