def __repr__(self): if self.focus_cle and self.focus_cle != '': return _('<Recherche Information Balisée \'{expr}\' DANS "{loc}">' ).format(expr=self.expression_recherchee, loc=self.focus_cle) return _('<Recherche Information Balisée \'{expr}\' PARTOUT>').format( expr=self.expression_recherchee)
class RechercheInteretView(BaseAdminView): column_editable_list = ['est_obligatoire'] column_filters = ['est_obligatoire'] column_exclude_list = [ 'detecteurs', 'createur', 'responsable_derniere_modification' ] can_export = True can_view_details = False can_create = False can_edit = False can_delete = True edit_modal = False create_modal = False details_modal = True column_labels = dict(mapped_class_child='Type', friendly_name='Résultat dans variable', focus_cle='Recherche ciblée') column_formatters = dict(mapped_class_child=lambda a, b, c, d: str(c)) column_display_pk = False column_descriptions = { 'friendly_name': _('Si vous souhaitez conserver et exploiter le résultat de la règle dans votre automate, ' 'spécifiez un nom simple ici'), 'mapped_class_child': _('Nom du type de la règle'), 'focus_cle': _('Force le moteur à effectuer la recherche sur une partie restreinte de la source, laissez vide ' 'pour laisser le moteur rechercher PARTOUT.') }
def __repr__(self): if self.focus_cle and self.focus_cle != '': return _( '<Recherche Exactement \'{expr}\' DANS \'{loc}\'>').format( expr=self.expression_cle, loc=self.focus_cle) return _('<Recherche Exactement \'{expr}\' PARTOUT>').format( expr=self.expression_cle)
def __repr__(self): if self.focus_cle and self.focus_cle != '': return _( '<Recherche Identifiant Préfixé \'{identifiant_prefixe}\' DANS \'{focus_cle}\'>' ).format(identifiant_prefixe=self.prefixe, focus_cle=self.focus_cle) return _( '<Recherche Identifiant Préfixé \'{identifiant_prefixe}\' PARTOUT>' ).format(identifiant_prefixe=self.prefixe)
def supprimer_action(automate_id, action_noeud_id): automate = db.session.query(Automate).get(automate_id) # type: Automate action_noeud = db.session.query(ActionNoeud).filter_by( automate_id=automate_id, id=action_noeud_id).one() # type: ActionNoeud if automate is None: return jsonify({ 'message': _('Aucun automate ne correspond à ID {id}').format(id=automate_id) }), 404 if action_noeud is None: return jsonify({ 'message': _('Aucun action noeud ne correspond à ID {action_id} pour l\'automate ID {automate_id}' ).format(action_id=action_noeud_id, automate_id=automate_id) }), 404 cascade_delete = request.form.get('cascade', default=0, type=int) if automate.action_racine_id == action_noeud.id: automate.action_racine_id = None automate.action_racine = None if cascade_delete > 0: db.session.delete(action_noeud) else: try: noeud_a_mettre_niveau_r = db.session.query(ActionNoeud).filter_by( automate_id=automate_id, action_reussite=action_noeud).one() # type: ActionNoeud except NoResultFound: noeud_a_mettre_niveau_r = None try: noeud_a_mettre_niveau_f = db.session.query(ActionNoeud).filter_by( automate_id=automate_id, action_echec=action_noeud).one() # type: ActionNoeud except NoResultFound: noeud_a_mettre_niveau_f = None if noeud_a_mettre_niveau_r is not None: noeud_a_mettre_niveau_r.action_reussite = action_noeud.action_reussite action_noeud.action_reussite = None if noeud_a_mettre_niveau_f is not None: noeud_a_mettre_niveau_f.action_echec = action_noeud.action_echec action_noeud.action_echec = None if noeud_a_mettre_niveau_r is None and noeud_a_mettre_niveau_f is None: automate.action_racine = action_noeud.action_reussite or action_noeud.action_echec db.session.delete(action_noeud) db.session.commit() db.session.flush() return jsonify({}), 204
def __repr__(self): if self.expression_gauche and self.expression_droite and self.expression_gauche != '' and self.expression_droite != '': return _('<Recherche Expression ENTRE \'{expr_gauche}\' ET \'{expr_droite}\' {loc}>').format(expr_gauche=self.expression_gauche, expr_droite=self.expression_droite, loc=_('PARTOUT') if self.focus_cle and self.focus_cle != '' else _('DANS \'{}\'').format(self.focus_cle)) if self.expression_gauche and self.expression_gauche != '': return _('<Recherche Expression À DROITE DE \'{expr_gauche}\' {loc}>').format(expr_gauche=self.expression_gauche, loc=_('PARTOUT') if self.focus_cle and self.focus_cle != '' else _('DANS \'{}\'').format(self.focus_cle)) if self.expression_droite and self.expression_droite != '': return _('<Recherche Expression À GAUCHE DE \'{expr_droite}\' {loc}>').format(expr_droite=self.expression_droite, loc=_('PARTOUT') if self.focus_cle and self.focus_cle != '' else _('DANS \'{}\'').format(self.focus_cle)) return _('<Recherche Expression ENTRE \'{expr_gauche}\' ET \'{expr_droite}\' {loc}>').format(expr_gauche=self.expression_gauche, expr_droite=self.expression_droite, loc=_('PARTOUT') if self.focus_cle and self.focus_cle != '' else _('DANS \'{}\'').format(self.focus_cle))
class AutomateView(BaseAdminView): column_editable_list = ['production', 'notifiable'] column_searchable_list = ['designation', 'production'] column_exclude_list = [ 'action_racine', 'date_creation', 'createur', 'responsable_derniere_modification' ] column_details_exclude_list = None column_filters = ['designation'] form_excluded_columns = [ 'actions', 'action_racine', 'createur', 'date_creation', 'date_modification', 'responsable_derniere_modification' ] can_export = True can_view_details = False can_create = True can_edit = True can_delete = True edit_modal = True create_modal = True details_modal = True column_descriptions = { 'detecteur': _('Associe un détecteur, qui si résolu avec une source permet de lancer votre suite d\'action' ), 'designation': _('Description courte de ce que réalise votre automate, un objectif'), 'production': _('Si cette case est cochée, votre automate sera executé en production' ), 'priorite': _('Un entier à partir de 0 (Zéro) permettant de priviligier une execution ' 'd\'automate par rapport à un autre sur une source. De plus la priorité est proche de 0 (Zéro), ' 'de plus il est prioritaire'), 'notifiable': _('Active les notifications en cas d\'échec d\'au moins une des actions de votre automate' ) } def on_model_change(self, form, model, is_created): """ :param form: :param hermes_ui.models.automate.Automate model: :param bool is_created: :return: """ if is_created is True: model.createur = current_user model.date_creation = datetime.now() model.date_modification = datetime.now() model.responsable_derniere_modification = current_user
def simulation_detecteur(): detecteur_id = request.form.get('detecteur_id', type=int, default=None) sujet = request.form.get('sujet', type=str, default=None) corps = request.form.get('corps', type=str, default=None) if sujet is None: return jsonify({ 'message': _('Formulaire incomplet, manque le sujet de la source') }), 400 if corps is None: return jsonify({ 'message': _('Formulaire incomplet, manque le corps de la source') }), 400 if detecteur_id is None: return jsonify({ 'message': _('Formulaire incomplet, manque l\'identifiant du detecteur cible à tester' ) }), 400 detecteur = db.session.query(Detecteur).get( detecteur_id) # type: Detecteur if detecteur is None: return jsonify({ 'message': _('Impossible de trouver le detecteur n°{n}').format( n=str(detecteur_id)) }), 404 from hermes.source import Source as SourceNatif k = detecteur.transcription() d = SourceNatif(sujet, corps) k.lance_toi(d) return jsonify({ 'explications': k.explain(), 'interets': d.extraction_interet.interets }), 200 if k.est_accomplis is True else 409
def lecture_automate(automate_id): automate = db.session.query(Automate).get(automate_id) # type: Automate if automate is None: return jsonify({ 'message': _('Aucun automate ne correspond à ID {id}').format(id=automate_id) }), 404 return AutomateSchema().jsonify(automate), 200
class ExpressionDansCleRechercheInteretView(BaseAdminView): column_editable_list = ['est_obligatoire'] column_searchable_list = [ 'designation', 'expression_recherchee', 'est_obligatoire' ] column_exclude_list = ['mapped_class_child', 'focus_cle'] column_details_exclude_list = None column_filters = [ 'designation', 'expression_recherchee', 'est_obligatoire' ] form_excluded_columns = [ 'createur', 'date_creation', 'focus_cle', 'date_modification', 'responsable_derniere_modification', 'mapped_class_child' ] can_export = True can_view_details = False can_create = True can_edit = True can_delete = True edit_modal = True create_modal = True details_modal = True column_labels = dict(friendly_name=_('Résultat dans variable')) column_descriptions = { 'friendly_name': _('Si vous souhaitez conserver et exploiter le résultat de la règle dans votre automate, ' 'spécifiez un nom simple ici') } def on_model_change(self, form, model, is_created): """ :param form: :param hermes_ui.models.configuration.Configuration model: :param bool is_created: :return: """ if is_created is True: model.createur = current_user model.date_creation = datetime.now() model.mapped_class_child = str(model.__class__) model.date_modification = datetime.now() model.responsable_derniere_modification = current_user
def simulation_extraction_interet(): sujet = request.form.get('sujet', type=str, default=None) corps = request.form.get('corps', type=str, default=None) if sujet is None: return jsonify({ 'message': _('Formulaire incomplet, manque le sujet de la source') }), 409 if corps is None: return jsonify({ 'message': _('Formulaire incomplet, manque le corps de la source') }), 409 mon_extraction_interet = ExtractionInteret(sujet, corps) return jsonify(mon_extraction_interet.interets), 201
def creation_test_service(): automate_id = request.form.get('automate_id', type=int, default=None) if automate_id is None: return jsonify({}), 400 if InstanceInteroperabilite.current_thread is not None: return jsonify({ 'message': _("L'environnement de test nécessite que le traitement des flux soit désactivé." ) }), 409 automate = db.session.query(Automate).get(automate_id) # type: Automate if automate is None: return jsonify({ 'message': _('Aucun automate ne correspond à ID {id}').format(id=automate_id) }), 404 if automate.production is True: return jsonify({ 'message': _('Votre automate ne doit pas être en mode production.') }), 409 InstanceInteroperabilite.liste_attente_test_lock.acquire(blocking=True) if automate_id in InstanceInteroperabilite.liste_attente_test: InstanceInteroperabilite.liste_attente_test_lock.release() return jsonify({ 'message': _("Votre automate est toujours en attente d'être testé. Veuillez patienter." ) }), 409 InstanceInteroperabilite.liste_attente_test.append(automate_id) InstanceInteroperabilite.liste_attente_test_lock.release() demarrage = InstanceInteroperabilite.demarrer() return jsonify({}), 201 if demarrage is True else 409
def lecture_action_automate(automate_id, action_noeud_id): action = db.session.query(ActionNoeud).filter_by( automate_id=automate_id, id=action_noeud_id).one() # type: ActionNoeud if action is None: return jsonify({}), 404 decompose_type_action = action.mapped_class_child.split( "'") # type: list[str] if len(decompose_type_action) != 3 or not decompose_type_action[ -2].startswith('hermes_ui.models.'): return jsonify({ 'message': _('Type d\'action illégale à la création: "{action_type}"').format( action_type=decompose_type_action[-2]) }), 409 target_module = modules['.'.join( decompose_type_action[-2].split('.')[0:-1])] try: target_model_class = getattr(target_module, decompose_type_action[-2].split('.')[-1]) except AttributeError as e: return jsonify({ 'message': _('Le type d\'action demandé à la création est inexistant: {action_type}' ).format(action_type=decompose_type_action[-2].split('.')[-1]) }), 400 target_sub_action = db.session.query(target_model_class).filter_by( automate_id=automate_id, id=action_noeud_id).one() # type: ActionNoeud for schema_action_noeud_class in ActionNoeudSchema.__subclasses__(): if str(schema_action_noeud_class).split('.')[-1][0:-2].startswith( str(target_model_class).split('.')[-1][0:-2]): return schema_action_noeud_class().jsonify(target_sub_action), 200 return ActionNoeudSchema().jsonify(target_sub_action), 200
def assistance_saisie_automate(automate_id): automate = db.session.query(Automate).get(automate_id) if automate is None: return jsonify({ 'message': _('Impossible de proposer la liste des variables disponibles pour un automate inexistant' ) }) propositions = list() for el in automate.detecteur.regles: # type: RechercheInteret if el.friendly_name is not None and el.friendly_name != '': propositions.append('{{' + el.friendly_name + '}}') for el in automate.actions: # type: ActionNoeud if el.friendly_name is not None and el.friendly_name != '': propositions.append('{{' + el.friendly_name + '}}') return jsonify(propositions), 200
class InformationRechercheInteretView(BaseAdminView): column_editable_list = ['est_obligatoire'] column_searchable_list = [ 'designation', 'information_cible', 'est_obligatoire' ] column_exclude_list = ['mapped_class_child'] column_details_exclude_list = None column_filters = ['designation', 'information_cible', 'est_obligatoire'] form_excluded_columns = [ 'createur', 'date_creation', 'date_modification', 'responsable_derniere_modification', 'mapped_class_child' ] can_export = True can_view_details = False can_create = True can_edit = True can_delete = True edit_modal = True create_modal = True details_modal = True column_labels = dict(friendly_name=_('Résultat dans variable'), focus_cle=_('Recherche ciblée')) column_descriptions = { 'friendly_name': _('Si vous souhaitez conserver et exploiter le résultat de la règle dans votre automate, ' 'spécifiez un nom simple ici'), 'focus_cle': _('Force le moteur à effectuer la recherche sur une partie restreinte de la source, laissez vide ' 'pour laisser le moteur rechercher PARTOUT.') } form_choices = { 'focus_cle': [('titre', _('Uniquement dans le TITRE')), ('corpus', _('Uniquement dans le CORPS')), ('expediteur', _('Dans le champ expéditeur du message')), ('destinataire', _('Dans le champ destinataire du message')), ('hyperliens', _('Dans la liste des URLs découvertes')), ('pieces-jointes', _('Dans la liste des noms des pièces jointes')), ('pieces-jointes-types', _('Dans la liste des formats MIME des pièces jointes'))], } def on_model_change(self, form, model, is_created): """ :param form: :param hermes_ui.models.configuration.Configuration model: :param bool is_created: :return: """ if is_created is True: model.createur = current_user model.date_creation = datetime.now() model.mapped_class_child = str(model.__class__) model.date_modification = datetime.now() model.responsable_derniere_modification = current_user
def simulation_detecteur_fichier(): if 'file' not in request.files: return jsonify({'message': 'Aucun fichier envoyé'}), 400 mon_fichier = request.files['file'] # type: FileStorage if (mon_fichier.content_type != 'application/octet-stream' and mon_fichier.content_type != 'message/rfc822') or ( mon_fichier.filename.endswith('.eml') is False and mon_fichier.filename.endswith('.msg') is False): return jsonify({ 'message': _('Fichier message invalide, fichier binaire *.EML ou *.MSG requis !' ) }), 400 if mon_fichier.filename.endswith('.eml') is True: mon_message = Mail.from_eml(mon_fichier.stream.read()) else: mon_message = Mail.from_msg(mon_fichier.stream.read()) detecteurs = db.session.query(Detecteur).all() ob_detecteurs = list() ma_reponse_html = str() ma_reponse_html += """ <div class="panel box box-warning"> <div class="box-header with-border"> <h4 class="box-title"> <a data-toggle="collapse" data-parent="#accordion" href="#collapse-x" aria-expanded="false" class=""> Ce que le moteur perçoit </a> </h4> </div> <div id="collapse-x" class="panel-collapse collapse" aria-expanded="false" style=""> <div class="box-body"> <pre> <code class="json"> {perception_moteur} </code> </pre> </div> </div> </div>""".format(perception_moteur=dumps( mon_message.extraction_interet.interets, indent=4, ensure_ascii=False)) for detecteur, i in zip(detecteurs, range(0, len(detecteurs))): ob_detecteurs.append(detecteur.transcription()) try: ob_detecteurs[-1].lance_toi(mon_message) except AucuneObligationInteretException as e: continue ma_reponse_html += """ <div class="panel box {box_color}"> <div class="box-header with-border"> <h4 class="box-title"> <a data-toggle="collapse" data-parent="#accordion" href="#collapse{i_row}" aria-expanded="false" class=""> {detecteur_res} </a> </h4> </div> <div id="collapse{i_row}" class="panel-collapse collapse" aria-expanded="false" style=""> <div class="box-body"> <pre> <code> {detecteur_explications} </code> </pre> </div> </div> </div>""".format( detecteur_res=str(ob_detecteurs[-1]), detecteur_explications=ob_detecteurs[-1].explain(), box_color="box-success" if ob_detecteurs[-1].est_accomplis else 'box-danger', i_row=str(i), ) return Response(ma_reponse_html, status=200, content_type='text/html')
def export_automates(): if 'file' not in request.files: return jsonify({'message': 'Aucun fichier envoyé'}), 400 mon_fichier = request.files['file'] # type: FileStorage if mon_fichier.content_type != 'application/json' or mon_fichier.filename.endswith( '.json') is False: return jsonify( {'message': _('Fichier message invalide, fichier JSON requis !')}), 400 def rec_(at, act): """ :param Automate at: :param ActionNoeud act: :return: """ act.automate_id = at.id if act.action_reussite is not None: rec_(at, act.action_reussite) if act.action_echec is not None: rec_(at, act.action_echec) db.session.query(ActionNoeudExecution).delete() db.session.query(RechercheInteretExecution).delete() db.session.query(AutomateExecution).delete() for sb in ActionNoeud.__subclasses__(): db.session.query(sb).delete() db.session.query(ActionNoeud).delete() db.session.query(Automate).delete() db.session.query(LienDetecteurRechercheInteret).delete() db.session.query(LienSousRegleOperationLogique).delete() for sb in RechercheInteret.__subclasses__(): db.session.query(sb).delete() db.session.query(RechercheInteret).delete() db.session.query(Detecteur).delete() db.session.commit() db.session.flush() try: from json import loads automates = AutomateLegacySchema(many=True).load( loads(mon_fichier.stream.read().decode( 'ascii'))) # type: list[Automate] except MarshmallowError as e: return jsonify({ 'message': _("Impossible d'importer votre fichier '{fname}' car votre fichier ne respecte pas la structure JSON obligatoire ! '{msg_err}'." ).format(fname=mon_fichier.filename, msg_err=str(e)) }), 409 for automate in automates: try: act_r = deepcopy(automate.action_racine) automate.action_racine = None automate.createur = current_user automate.responsable_derniere_modification = current_user automate.detecteur.createur = current_user automate.detecteur.responsable_derniere_modification = current_user for rg in automate.detecteur.regles: ri_ex = db.session.query(RechercheInteret).filter_by( designation=rg.designation, mapped_class_child=rg.mapped_class_child).first() if ri_ex is not None: automate.detecteur.regles[automate.detecteur.regles.index( rg)] = ri_ex db.session.add(automate) db.session.commit() if act_r is not None: rec_(automate, act_r) automate.action_racine = act_r db.session.commit() except SQLAlchemyError as e: logger.error(_( "Impossible d'importer votre automate '{automate_nom}' car une erreur de transposition en base de données est survenue '{msg_err}'." ), automate_nom=automate.designation, msg_err=str(e)) continue try: db.session.flush() except SQLAlchemyError as e: logger.warning(_("Erreur SQL '{err_msg}'."), err_msg=str(e)) return jsonify({ 'message': _('Erreur de transaction SQL : {err_msg}').format(err_msg=str(e)) }), 409 return jsonify({}), 204
def thread(): logger.info(_("Chargement des variables globales passées en base de données")) configurations = db.session.query(Configuration).all() # type: list[Configuration] for configuration in configurations: logger.info(_("Chargement de la configuration <'{conf_nom}'::{conf_format}>"), conf_nom=configuration.designation, conf_format=configuration.format) try: Session.charger_input( configuration.designation, configuration.valeur, configuration.format ) except TypeError as e: logger.error(_("Impossible de charger la configuration <'{conf_nom}'::{conf_format}> car '{error_msg}'"), conf_nom=configuration.designation, conf_format=configuration.format, error_msg=str(e)) logger.info(_("Démarrage de la boucle de surveillance des automates sur les boîtes IMAP4")) InstanceInteroperabilite.liste_attente_test_lock.acquire() est_une_sequence_test = len(InstanceInteroperabilite.liste_attente_test) > 0 if not est_une_sequence_test: InstanceInteroperabilite.liste_attente_test_lock.release() while InstanceInteroperabilite.stop_instruction is None: boites_aux_lettres = db.session.query(BoiteAuxLettresImap).all() # type: list[BoiteAuxLettresImap] mail_factories = [] for el in boites_aux_lettres: try: mail_factories.append( el.get_mailtoolbox() ) except LoginError as e: logger.error(_("Impossible de démarrer l'usine " "à production de source '{designation}' car '{msg_err}'"), designation=el.designation, msg_err=str(e)) except Exception as e: logger.error(_("Impossible de démarrer l'usine " "à production de source '{designation}' car '{msg_err}'"), designation=el.designation, msg_err=str(e)) if len(mail_factories) == 0: logger.warning(_("Aucune usine à production de source n'est active, impossible de continuer")) break logger.debug(_("{n} usine(s) à production de source sont actives"), n=len(mail_factories)) if est_une_sequence_test: models_automates = db.session.query(Automate).filter_by(production=False).all() # type: list[Automate] else: models_automates = db.session.query(Automate).filter_by(production=True).all() # type: list[Automate] models_automates.sort(key=lambda x: x.priorite, reverse=True) if len(models_automates) == 0: logger.warning( _("Aucune automate à traitement de source n'est actif, impossible de continuer")) break logger.debug(_("{n} automates en production sont actifs"), n=len(models_automates)) for mail_factory in mail_factories: logger.debug(_("Ouverture de la BAL '{nom_utilisateur}'@'{hote_imap}'"), nom_utilisateur=mail_factory.nom_utilisateur, hote_imap=mail_factory.hote_imap) if InstanceInteroperabilite.stop_instruction is not None: logger.info(_("Arrêt de la surveillance continue des BAL")) return sources = mail_factory.extraire() logger.debug(_("{n} sources ont été extraites de l'usine à production '{usine}'"), n=len(sources), usine=str(mail_factory)) for source in sources: logger.debug(_("Vérification du message électronique '{source_nom}'"), source_nom=source.titre) for model in models_automates: automate = model.transcription() if InstanceInteroperabilite.stop_instruction is not None: logger.info(_("Arrêt de la surveillance continue des BAL")) return if est_une_sequence_test and model.id not in InstanceInteroperabilite.liste_attente_test: logger.debug(_("Séquence de test ne conserne pas '{automate_nom}'."), automate_nom=model.designation) continue date_depart_automate = datetime.now() # On vérifie les conditions pour anti-spam nb_execution_heure = db.session.query(AutomateExecution).filter( AutomateExecution.automate == model, AutomateExecution.sujet == source.titre, AutomateExecution.corps == source.corps, # AutomateExecution.validation_automate == False, AutomateExecution.date_creation >= (date_depart_automate - timedelta(hours=1)) ).count() if nb_execution_heure >= (model.limite_par_heure if model.limite_par_heure is not None else 100): logger.warning( _("L'automate '{automate_nom}' ne va pas traiter la source '{source_nom}' car celle ci " "dépasse la limite de {n} lancement(s) par heure."), automate_nom=automate.designation, source_nom=source.titre, n=(model.limite_par_heure if model.limite_par_heure is not None else 100) ) continue nb_execution_echec_heure = db.session.query(AutomateExecution).filter( AutomateExecution.automate == model, AutomateExecution.sujet == source.titre, AutomateExecution.corps == source.corps, AutomateExecution.validation_automate == False, AutomateExecution.date_creation >= (date_depart_automate - timedelta(hours=1)) ).count() if nb_execution_echec_heure >= (model.limite_echec_par_heure if model.limite_echec_par_heure is not None else 10): logger.warning( _("L'automate '{automate_nom}' ne va pas traiter la source '{source_nom}' car celle ci " "dépasse la limite en échec de {n} lancement(s) par heure."), automate_nom=automate.designation, source_nom=source.titre, n=(model.limite_echec_par_heure if model.limite_echec_par_heure is not None else 10) ) continue try: etat_final_automate = automate.lance_toi(source) if etat_final_automate is True: logger.info( _("L'automate '{automate_nom}' vient de traiter avec succès la source '{source_nom}'"), automate_nom=automate.designation, source_nom=source.titre ) elif etat_final_automate is False and automate.detecteur.est_accomplis is True: logger.warning( _("L'automate '{automate_nom}' vient de traiter avec au moins une erreur la source '{source_nom}'"), automate_nom=automate.designation, source_nom=source.titre ) if model.notifiable is True: NotificationIncident.prevenir( model, source, _("L'automate '{automate_nom}' n'a pas réussi à aboutir, au moins une action est en échec").format(automate_nom=automate.designation), _("Vous recevez ce message car votre automate '{automate_nom}' est configurée " "pour émettre une notification dans ce cas. \n\n" "En PJ les élements nécessaires à l'analyse des évènements. " "L'automate est toujours actif. \n\n").format( automate_nom=automate.designation, ) ) except AucuneObligationInteretException as e: logger.error( _("L'automate '{automate_nom}' ne dispose pas d'un détecteur contraignant, " "il est nécessaire d'avoir au moins une règle avec obligation. " "Désactivation de l'automate."), automate_nom=automate.designation ) model.production = False db.session.commit() db.session.flush() continue except KeyError as e: logger.error( _("L'automate '{automate_nom}' est en erreur grave, " "une variable est non résolue: '{err_msg}'"), automate_nom=automate.designation, err_msg=str(e) ) NotificationIncident.prevenir( model, source, _("L'automate '{automate_nom}' est en erreur grave, une variable est non résolue !").format( automate_nom=automate.designation), _("Vous recevez ce message car votre automate '{automate_nom}' n'est pas en mesure d'aboutir. \n\n" "Une variable est non résolue. Veuillez revenir en conception. " "L'automate <b>a été désactivé</b> par précaution. \n\n" "Information technique: \n\n" "<pre class='code code-html'><label></label><code>{msg_err}</code></pre>").format( automate_nom=automate.designation, msg_err=str(e) ) ) model.production = False db.session.commit() db.session.flush() continue except Exception as e: from sys import exc_info from os import path import traceback exc_type, exc_obj, exc_tb = exc_info() logger.critical( _("L'automate '{automate_nom}' est en erreur critique, " "une exception est soulevée: '{msg_err}'"), automate_nom=automate.designation, msg_err=str(e) ) fname = path.split(exc_tb.tb_frame.f_code.co_filename)[1] logger.critical( _("Informations complémentaires '{exc_type}', '{fname}' à la ligne {lineno}."), exc_type=str(exc_type), fname=str(fname), lineno=str(exc_tb.tb_lineno) ) logger.critical( traceback.format_exc() ) NotificationIncident.prevenir( model, source, _("L'automate '{automate_nom}' est en erreur critique, une exception est soulevée !").format(automate_nom=automate.designation), _("Vous recevez ce message car votre automate '{automate_nom}' n'est pas en mesure d'aboutir. \n\n" "Une exception est non résolue. Veuillez revenir en conception. " "L'automate a été désactivé par précaution. \n\n" "Information technique: \n\n" "<pre class='code code-html'><label>Logs</label><code>{msg_err}</code></pre>").format(automate_nom=automate.designation, msg_err=traceback.format_exc()) ) model.production = False db.session.commit() db.session.flush() continue if automate.detecteur.est_accomplis is True: automate_execution = AutomateExecution( automate=model, sujet=source.titre if len(source.titre) < 255 else source.titre[1:250]+'..', corps=source.corps, date_creation=date_depart_automate, detecteur=model.detecteur, validation_detecteur=automate.detecteur.est_accomplis, validation_automate=etat_final_automate, explications_detecteur=automate.detecteur.explain(), date_finalisation=datetime.now(), logs=automate.logs ) for action_lancee in automate.actions_lancees: action_noeud_execution = ActionNoeudExecution( automate_execution=automate_execution, action_noeud=db.session.query(ActionNoeud).filter_by(designation=action_lancee.designation, automate_id=model.id).one(), validation_action_noeud=action_lancee.est_reussite, payload=str(action_lancee.payload), args_payload=dumps(action_lancee.snapshot) ) db.session.add(action_noeud_execution) db.session.add(automate_execution) db.session.commit() break # La source a été traitée. Pas besoin d'y appliquer un autre automate. db.session.flush() if est_une_sequence_test: InstanceInteroperabilite.liste_attente_test.clear() InstanceInteroperabilite.liste_attente_test_lock.release() break sleep(1 if len(mail_factories) > 0 else 10) logger.info(_("Fin de surveillance continue des automates sur les boîtes IMAP4")) InstanceInteroperabilite.current_thread = None
def __repr__(self): if self.focus_cle and self.focus_cle != '': return _('<Recherche REGEX \'{expr}\' DANS \'{loc}\'>').format(expr=self.expression_reguliere, loc=self.focus_cle) return _('<Recherche REGEX \'{expr}\' PARTOUT>').format(expr=self.expression_reguliere)
babel = Babel(app) project = WebpackTemplateProject( __name__, project_folder='assets', config_path='assets/config.json', ) app.config.update(dict(WEBPACKEXT_PROJECT=project, )) # Initialize extension FlaskWebpackExt(app) admin = AdminLte(app, skin='green-light', name=_('Hermes - Automates à réaction aux échanges IMAP'), short_name="<b>H</b><sup>ermes</sup>", long_name="<b>Hermes</b>", index_view=AdminIndexView(name=_("Éditeur d'Automate"), menu_icon_value='fa-pencil', menu_icon_type='fa')) db.init_app(app) migrate = Migrate(app, db) ma = Marshmallow(app) NotificationIncident.init_app(app) admin.add_view( BoiteAuxLettresImapView(BoiteAuxLettresImap, db.session, name=_("Boite aux lettres (IMAP)"),
def __repr__(self): return _('<Recherche DE \'{nom}\'>').format(nom=self.designation)
def __repr__(self): return _('<Opération sur Critère(s) {operande} "{nom}">').format(operande=self.operande, nom=self.designation)
def __repr__(self): return _('<Recherche Clé Auto-Découverte \'{loc}\'>').format(loc=self.cle_recherchee)
def __repr__(self): return _('<Détection DE \'{detecteur_nom}\'>').format(detecteur_nom=self.designation)
def modification_action(automate_id, action_noeud_id): automate = db.session.query(Automate).get(automate_id) # type: Automate action_noeud = db.session.query(ActionNoeud).filter_by( automate_id=automate_id, id=action_noeud_id).first() # type: ActionNoeud if not request.is_json: return jsonify( {'message': _('Aucun corps JSON présent dans la requête HTTP')}), 400 if automate is None: return jsonify({ 'message': _('Aucun automate ne correspond à ID {id}').format(id=automate_id) }), 404 if action_noeud is None: return jsonify({ 'message': _('Aucun action noeud ne correspond à ID {action_id} pour l\'automate ID {automate_id}' ).format(action_id=action_noeud_id, automate_id=automate_id) }), 404 payload = request.json # type: dict if 'type' not in payload.keys() or 'formulaire' not in payload.keys(): return jsonify( {'message': _('Le JSON présent dans la requête est invalide')}), 400 type_action = payload['type'] formulaire = payload['formulaire'] # type: dict decompose_type_action = type_action.split("'") # type: list[str] target_module = modules['.'.join( decompose_type_action[-2].split('.')[0:-1])] target_model_class = getattr(target_module, decompose_type_action[-2].split('.')[-1]) target_model_instance = db.session.query(target_model_class).get( action_noeud.id) # type: ActionNoeud for key_attr in formulaire.keys(): try: getattr(target_model_instance, key_attr) except AttributeError as e: return jsonify({'message': str(e)}), 409 if key_attr in target_model_class.PARAMETRES.keys( ) and target_model_class.PARAMETRES[key_attr]['format'] == 'CHECKBOX': formulaire[key_attr] = True if formulaire[key_attr] == 1 else False if isinstance(formulaire[key_attr], str) and len( formulaire[key_attr].strip() ) == 0 and key_attr in target_model_class.PARAMETRES.keys( ) and target_model_class.PARAMETRES[key_attr]['required'] is False: formulaire[key_attr] = None setattr(target_model_instance, key_attr, formulaire[key_attr]) try: db.session.commit() db.session.flush() except IntegrityError as e: return jsonify({'message': str(e)}), 409 return jsonify({}), 200
def __repr__(self): if self.focus_cle and self.focus_cle != '': return _('<Recherche Date \'{prefixe}\' DANS \'{loc}\'>').format(prefixe=self.prefixe, loc=self.focus_cle) return _('<Recherche Date \'{prefixe}\' PARTOUT>').format(prefixe=self.prefixe)
def __repr__(self): return _('<Recherche XPath \'{expr_xpath}\' DANS CORPS HTML>').format(expr_xpath=self.expression_xpath)
def creation_action(automate_id): automate = db.session.query(Automate).get(automate_id) # type: Automate if not request.is_json: return jsonify( {'message': _('Aucun corps JSON présent dans la requête HTTP')}), 400 if automate is None: return jsonify({ 'message': _('Aucun automate ne correspond à ID {id}').format(id=automate_id) }), 404 payload = request.json # type: dict if 'type' not in payload or ( 'parent' not in payload and 'remplacement' not in payload) or 'formulaire' not in payload: return jsonify( {'message': _('Le JSON présent dans la requête est invalide')}), 400 type_action = payload['type'] parent_information = payload['parent'] if 'parent' in payload else None remplacement_action_noeud = payload[ 'remplacement'] if 'remplacement' in payload else None formulaire = payload['formulaire'] decompose_type_action = type_action.split("'") # type: list[str] if len(decompose_type_action) != 3 or not decompose_type_action[ -2].startswith('hermes_ui.models.'): return jsonify({ 'message': _('Type d\'action illégale à la création: "{action_type}"').format( action_type=decompose_type_action[-2]) }), 409 target_module = modules['.'.join( decompose_type_action[-2].split('.')[0:-1])] try: target_model_class = getattr(target_module, decompose_type_action[-2].split( '.')[-1]) # type: type(ActionNoeud) except AttributeError as e: return jsonify({ 'message': _('Le type d\'action demandé à la création est inexistant: {action_type}' ).format(action_type=decompose_type_action[-2].split('.')[-1]) }), 400 for key_form in formulaire.keys(): if isinstance(formulaire[key_form], str) and len( formulaire[key_form].strip() ) == 0 and key_form in target_model_class.PARAMETRES.keys( ) and target_model_class.PARAMETRES[key_form]['required'] is False: formulaire[key_form] = None try: target_model_instance = target_model_class( **formulaire) # type: ActionNoeud except AttributeError as e: return jsonify({ 'message': _('Le formulaire de création est invalide, pour cause de "{msg_err}"' ).format(msg_err=str(e)) }), 400 target_model_instance.mapped_class_child = str( target_model_instance.__class__) target_model_instance.automate = automate target_model_instance.automate_id = automate.id target_model_instance.createur = current_user target_model_instance.responsable_derniere_modification = current_user target_model_instance.date_creation = datetime.datetime.now() target_model_instance.date_modification = datetime.datetime.now() try: db.session.add(target_model_instance) db.session.commit() except IntegrityError as e: return jsonify({'message': str(e)}), 409 if remplacement_action_noeud is None: if parent_information is None: automate.action_racine = target_model_instance else: if len(parent_information) != 2: return jsonify({ 'message': _('Les informations de votre action parente sont malformés' ) }), 400 action_parente_id, etat_reussite = tuple(parent_information) action_noeud_parente = db.session.query(ActionNoeud).filter_by( automate_id=automate_id, id=int(action_parente_id)).one() # type: ActionNoeud if action_noeud_parente is None: return jsonify( {'message': _('L\'action parente n\'existe pas !')}), 404 if 'ECHEC' in parent_information: # cas simple de non ajout if action_noeud_parente.action_echec is None: target_model_instance.action_echec_id = action_noeud_parente.id else: # cas insertion dans arbre action_deplacement = action_noeud_parente.action_echec action_noeud_parente.action_echec = target_model_instance target_model_instance.action_echec = action_deplacement elif 'REUSSITE' in parent_information: if action_noeud_parente.action_reussite is None: target_model_instance.action_reussite_id = action_noeud_parente.id else: # cas insertion dans arbre action_deplacement = action_noeud_parente.action_reussite action_noeud_parente.action_reussite = target_model_instance target_model_instance.action_reussite = action_deplacement else: noeud_a_remplacer = db.session.query(ActionNoeud).filter_by( automate_id=automate_id, id=int(remplacement_action_noeud)).one() # type: ActionNoeud if noeud_a_remplacer is None: return jsonify({ 'message': _("Le noeud que vous souhaitez remplacer est inexistant") }), 404 target_model_instance.action_echec = noeud_a_remplacer.action_echec target_model_instance.action_reussite = noeud_a_remplacer.action_reussite noeud_a_remplacer.action_echec = None noeud_a_remplacer.action_reussite = None try: noeud_a_mettre_niveau_r = db.session.query(ActionNoeud).filter_by( automate_id=automate_id, action_reussite=noeud_a_remplacer).one() # type: ActionNoeud except NoResultFound: noeud_a_mettre_niveau_r = None try: noeud_a_mettre_niveau_f = db.session.query(ActionNoeud).filter_by( automate_id=automate_id, action_echec=noeud_a_remplacer).one() # type: ActionNoeud except NoResultFound: noeud_a_mettre_niveau_f = None if noeud_a_mettre_niveau_r is not None: noeud_a_mettre_niveau_r.action_reussite = target_model_instance if noeud_a_mettre_niveau_f is not None: noeud_a_mettre_niveau_f.action_echec = target_model_instance if noeud_a_mettre_niveau_r is None and noeud_a_mettre_niveau_f is None: automate.action_racine = target_model_instance db.session.delete(noeud_a_remplacer) try: db.session.commit() db.session.flush() except IntegrityError as e: return jsonify({'message': str(e)}), 409 return jsonify({}), 201
def __repr__(self): return _('<Recherche Exactement \'{expr}\' DANS Clé Auto-Découverte "{loc}">').format(expr=self.expression_recherchee, loc=self.cle_recherchee)