Exemple #1
0
    def post(self) -> Response:
        email_pattern = self.request.POST["email_pattern"] or ""

        if not email_pattern:
            self.request.session.flash(
                Message(cls="error",
                        text="Veuillez saisir un courriel ou modèle."))
            return HTTPFound(location=self.request.resource_url(self.context))

        allowed_email_pattern = (
            DBSession.query(AllowedEmailPattern).filter_by(
                pattern=email_pattern).first())

        if allowed_email_pattern:
            self.request.session.flash(
                Message(cls="warning",
                        text="Cette adresse de courriel existe déjà."))
            return HTTPFound(location=self.request.resource_url(self.context))

        if User.email_is_allowed(email_pattern):
            self.request.session.flash(
                Message(cls="warning",
                        text="Cette adresse de courriel est déjà acceptée."))
            return HTTPFound(location=self.request.resource_url(self.context))

        WhitelistAdd.create(email_pattern=email_pattern,
                            comment=None,
                            request=self.request)

        self.request.session.flash(
            Message(
                cls="success",
                text=("Adresse de courriel ou modèle créé(e) avec succès."),
            ))
        return HTTPFound(location=self.request.resource_url(self.context))
Exemple #2
0
def upload_csv(context: LectureResource, request: Request) -> Response:

    lecture = context.model(
        subqueryload("amendements").options(
            load_only("num"),
            joinedload("user_content").load_only("avis", "objet", "reponse",
                                                 "comments"),
            joinedload("location").options(
                subqueryload("shared_table"),
                subqueryload("user_table"),
            ),
        ))

    next_url = request.resource_url(context["amendements"])

    # We cannot just do `if not POST["reponses"]`, as FieldStorage does not want
    # to be cast to a boolean.
    if request.POST["reponses"] == b"":
        request.session.flash(
            Message(cls="warning",
                    text="Veuillez d’abord sélectionner un fichier"))
        return HTTPFound(location=request.resource_url(context, "options"))

    try:
        counter = import_csv(
            request=request,
            reponses_file=request.POST["reponses"].file,
            lecture=lecture,
            amendements={
                amendement.num: amendement
                for amendement in lecture.amendements
            },
            team=context.dossier_resource.dossier.team,
        )
    except CSVImportError as exc:
        request.session.flash(Message(cls="danger", text=str(exc)))
        return HTTPFound(location=next_url)

    if counter["reponses"]:
        request.session.flash(
            Message(
                cls="success",
                text=f"{counter['reponses']} réponse(s) chargée(s) avec succès",
            ))
        ReponsesImportees.create(lecture=lecture, request=request)

    if counter["reponses_errors"]:
        request.session.flash(
            Message(
                cls="warning",
                text=
                (f"{counter['reponses_errors']} réponse(s) "
                 "n’ont pas pu être chargée(s). "
                 "Pour rappel, il faut que le fichier CSV contienne au moins "
                 "les noms de colonnes suivants « Num amdt », "
                 "« Avis du Gouvernement », « Objet amdt » et « Réponse »."),
            ))

    return HTTPFound(location=next_url)
Exemple #3
0
    def post(self) -> Response:
        avis = self.request.POST.get("avis", "")
        objet = clean_html(self.request.POST.get("objet", ""))
        reponse = clean_html(self.request.POST.get("reponse", ""))
        comments = clean_html(self.request.POST.get("comments", ""))

        avis_changed = avis != (self.amendement.user_content.avis or "")
        objet_changed = objet != (self.amendement.user_content.objet or "")
        reponse_changed = reponse != (self.amendement.user_content.reponse or "")
        comments_changed = comments != (self.amendement.user_content.comments or "")

        if not self.is_on_my_table:
            message = (
                "Les modifications n’ont PAS été enregistrées "
                "car l’amendement n’est plus sur votre table."
            )
            if self.amendement.user_table:
                message += (
                    f" Il est actuellement sur la table de "
                    f"{self.amendement.user_table.user}."
                )
            self.request.session.flash(Message(cls="danger", text=message))
            return HTTPFound(location=self.my_table_url)

        if avis_changed:
            AvisAmendementModifie.create(self.request, self.amendement, avis)

        if objet_changed:
            ObjetAmendementModifie.create(self.request, self.amendement, objet)

        if reponse_changed:
            ReponseAmendementModifiee.create(self.request, self.amendement, reponse)

        if comments_changed:
            CommentsAmendementModifie.create(self.request, self.amendement, comments)

        self.amendement.stop_editing()

        self.request.session.flash(
            Message(cls="success", text="Les modifications ont bien été enregistrées.")
        )
        if "save-and-transfer" in self.request.POST:
            return HTTPFound(
                location=self.request.resource_url(
                    self.context.parent.parent,
                    "transfer_amendements",
                    query={
                        "nums": self.amendement.num,
                        "from_save": 1,
                        "back": self.back_url,
                    },
                )
            )
        else:
            self.request.session["highlighted_amdt"] = self.amendement.slug
            return HTTPFound(location=self.back_url)
Exemple #4
0
def upload_json(context: LectureResource, request: Request) -> Response:

    lecture = context.model(joinedload("articles"))

    next_url = request.resource_url(context["amendements"])

    # We cannot just do `if not POST["backup"]`, as FieldStorage does not want
    # to be cast to a boolean.
    if request.POST["backup"] == b"":
        request.session.flash(
            Message(cls="warning",
                    text="Veuillez d’abord sélectionner un fichier"))
        return HTTPFound(location=request.resource_url(context, "options"))

    try:
        counter = import_json(
            request=request,
            backup_file=request.POST["backup"].file,
            lecture=lecture,
            amendements={
                amendement.num: amendement
                for amendement in lecture.amendements
            },
            articles={
                article.sort_key_as_str: article
                for article in lecture.articles
            },
            team=context.dossier_resource.dossier.team,
        )
    except ValueError as exc:
        request.session.flash(Message(cls="danger", text=str(exc)))
        return HTTPFound(location=next_url)

    if counter["reponses"] or counter["articles"]:
        if counter["reponses"]:
            message = f"{counter['reponses']} réponse(s) chargée(s) avec succès"
            if counter["articles"]:
                message += f", {counter['articles']} article(s) chargé(s) avec succès"
        elif counter["articles"]:
            message = f"{counter['articles']} article(s) chargé(s) avec succès"
        request.session.flash(Message(cls="success", text=message))
        ReponsesImporteesJSON.create(lecture=lecture, request=request)

    if counter["reponses_errors"] or counter["articles_errors"]:
        message = "Le fichier de sauvegarde n’a pas pu être chargé"
        if counter["reponses_errors"]:
            message += f" pour {counter['reponses_errors']} amendement(s)"
            if counter["articles_errors"]:
                message += f" et {counter['articles_errors']} article(s)"
        elif counter["articles_errors"]:
            message += f" pour {counter['articles_errors']} article(s)"
        request.session.flash(Message(cls="warning", text=message))

    return HTTPFound(location=next_url)
Exemple #5
0
 def post(self) -> Response:
     user_pk = self.request.POST["user_pk"]
     if str(self.request.user.pk) == user_pk:
         message = "Vous ne pouvez pas vous retirer du statut d’administrateur."
         self.request.session.flash(Message(cls="warning", text=message))
         return HTTPFound(location=self.request.resource_url(self.context))
     user = DBSession.query(User).filter_by(pk=user_pk).first()
     AdminRevoke.create(target=user, request=self.request)
     self.request.session.flash(
         Message(cls="success",
                 text=("Droits d’administration retirés avec succès.")))
     return HTTPFound(location=self.request.resource_url(self.context))
Exemple #6
0
 def post(self) -> Response:
     if self.request.user.can_delete_lecture:
         DBSession.delete(self.lecture)
         DBSession.flush()
         self.request.session.flash(
             Message(cls="success", text="Lecture supprimée avec succès."))
     else:
         self.request.session.flash(
             Message(
                 cls="warning",
                 text=
                 "Vous n’avez pas les droits pour supprimer une lecture.",
             ))
     return HTTPFound(
         location=self.request.resource_url(self.context.parent))
Exemple #7
0
    def post(self) -> Response:
        changed = False

        new_title = self.request.POST["title"]
        if new_title != self.article.user_content.title:
            TitreArticleModifie.create(
                request=self.request, article=self.article, title=new_title
            )
            changed = True

        new_presentation = clean_html(self.request.POST["presentation"])
        if new_presentation != self.article.user_content.presentation:
            PresentationArticleModifiee.create(
                request=self.request,
                article=self.article,
                presentation=new_presentation,
            )
            changed = True

        if changed:
            self.request.session.flash(
                Message(cls="success", text="Article mis à jour avec succès.")
            )

        return HTTPFound(location=self.next_url())
Exemple #8
0
 def post(self) -> Response:
     user_pk = self.request.POST["user_pk"]
     if not user_pk:
         self.request.session.flash(
             Message(
                 cls="warning",
                 text="Veuillez saisir une personne dans le menu déroulant.",
             ))
         return HTTPFound(
             location=self.request.resource_url(self.context, "add"))
     user = DBSession.query(User).filter_by(pk=user_pk).first()
     AdminGrant.create(target=user, request=self.request)
     self.request.session.flash(
         Message(cls="success",
                 text=("Droits d’administration ajoutés avec succès.")))
     return HTTPFound(location=self.request.resource_url(self.context))
Exemple #9
0
    def get(self) -> Any:

        if self.ip_limiter.exceeded(self.request.remote_addr):
            return HTTPTooManyRequests()

        if self.request.unauthenticated_userid:
            return HTTPFound(location=self.next_url)

        token = self.request.params.get("token")
        auth = repository.get_auth_token_data(token)
        if auth is None:
            self.log_failed_login_attempt(token)
            self.request.session.flash(
                Message(
                    cls="error",
                    text="Le lien est invalide ou a expiré. Merci de renouveler votre demande.",  # noqa
                )
            )
            raise HTTPFound(location=self.request.route_url("login"))

        # Delete token from repository after it's been used successfully
        repository.delete_auth_token(token)

        email = auth["email"]
        user, created = get_one_or_create(User, email=email)

        if created:
            DBSession.flush()  # so that the DB assigns a value to user.pk

        self.log_successful_login_attempt(email)

        user.last_login_at = datetime.utcnow()

        next_url = self.next_url
        if not user.name:
            next_url = self.request.route_url("welcome", _query={"source": next_url})

        # Compute response headers for the session cookie
        headers = remember(self.request, user.pk)

        app_name = self.request.registry.settings["zam.app_name"]
        self.request.session.flash(
            Message(cls="success", text=f"Bienvenue dans {app_name} !")
        )

        return HTTPFound(location=next_url, headers=headers)
Exemple #10
0
 def post(self) -> Response:
     titre: str = self.request.POST.get("titre")
     table, created = get_one_or_create(SharedTable,
                                        titre=titre,
                                        lecture=self.lecture)
     if created:
         SharedTableCreee.create(lecture=self.lecture,
                                 titre=titre,
                                 request=self.request)
         self.request.session.flash(
             Message(cls="success",
                     text=f"Boîte « {table.titre} » créée avec succès."))
     else:
         self.request.session.flash(
             Message(cls="warning",
                     text=f"La boîte « {table.titre} » existe déjà…"))
     return HTTPFound(location=self.request.resource_url(
         self.context.lecture_resource, "options", anchor="shared-tables"))
Exemple #11
0
def manual_refresh(context: LectureResource, request: Request) -> Response:
    lecture = context.model()
    amendements_collection = context["amendements"]
    if lecture.get_fetch_progress():
        request.session.flash(
            Message(cls="warning",
                    text="Rafraîchissement des amendements déjà en cours."))
        return HTTPFound(location=request.resource_url(amendements_collection))
    fetch_amendements(lecture.pk)
    # The progress is initialized even if the task is async for early feedback
    # to users and ability to disable the refresh button.
    # The total is doubled because we need to handle the dry_run.
    total = len(lecture.amendements) * 2 if lecture.amendements else 100
    lecture.set_fetch_progress(1, total)
    request.session.flash(
        Message(cls="success",
                text="Rafraîchissement des amendements en cours."))
    return HTTPFound(location=request.resource_url(amendements_collection))
Exemple #12
0
    def post(self) -> Response:
        dossier_slug = self._get_dossier_slug()

        if not dossier_slug:
            self.request.session.flash(
                Message(cls="error", text="Ce dossier n’existe pas."))
            return HTTPFound(location=self.request.resource_url(self.context))

        dossier = Dossier.get(slug=dossier_slug)

        if dossier is None:
            self.request.session.flash(
                Message(cls="error", text="Ce dossier n’existe pas."))
            return HTTPFound(location=self.request.resource_url(self.context))

        if dossier.team:
            self.request.session.flash(
                Message(cls="warning",
                        text="Ce dossier appartient à une autre équipe…"))
            return HTTPFound(location=self.request.resource_url(self.context))

        team = Team.create(name=dossier.slug)
        dossier.team = team
        for admin in DBSession.query(User).filter(
                User.admin_at.isnot(None)  # type: ignore
        ):
            admin.teams.append(team)

        # Enqueue task to asynchronously add the lectures
        create_missing_lectures(dossier_pk=dossier.pk,
                                user_pk=self.request.user.pk)

        DossierActive.create(dossier=dossier, request=self.request)

        self.request.session.flash(
            Message(
                cls="success",
                text=(
                    "Dossier créé avec succès, lectures en cours de création."
                ),
            ))
        return HTTPFound(
            location=self.request.resource_url(self.context[dossier.url_key]))
Exemple #13
0
def manual_refresh(context: LectureResource, request: Request) -> Response:
    lecture = context.model()
    fetch_amendements(lecture.pk)
    fetch_articles(lecture.pk)
    request.session.flash(
        Message(
            cls="success",
            text="Rafraichissement des amendements et des articles en cours.",
        ))
    return HTTPFound(location=request.resource_url(context, "amendements"))
Exemple #14
0
 def post(self) -> Response:
     DBSession.delete(self.dossier.team)
     for lecture in self.dossier.lectures:
         DBSession.delete(lecture)
     DBSession.flush()
     DossierDesactive.create(dossier=self.dossier, request=self.request)
     self.request.session.flash(
         Message(cls="success", text="Dossier supprimé avec succès."))
     return HTTPFound(
         location=self.request.resource_url(self.context.parent))
Exemple #15
0
    def post(self) -> Response:
        """
        Transfer amendement(s) from this table to another one, or back to the index
        """
        nums: List[int] = self.request.POST.getall("nums")
        if "submit-index" in self.request.POST:
            target = ""
        elif "submit-table" in self.request.POST:
            target = self.request.user.email
        else:
            target = self.request.POST.get("target")
            if not target:
                self.request.session.flash(
                    Message(cls="warning",
                            text="Veuillez sélectionner un·e destinataire."))
                return HTTPFound(location=self.request.resource_url(
                    self.context.lecture_resource,
                    "transfer_amendements",
                    query={"nums": nums},
                ))

        target_table: Optional[UserTable] = None
        if target:
            if target == self.request.user.email:
                target_user = self.request.user
            else:
                target_user = DBSession.query(User).filter(
                    User.email == target).one()
            if self.request.team and target_user not in self.request.team.users:
                raise HTTPForbidden("Transfert non autorisé")
            target_table = target_user.table_for(self.lecture)

        amendements = DBSession.query(Amendement).filter(
            Amendement.lecture == self.lecture,
            Amendement.num.in_(nums)  # type: ignore
        )

        for amendement in amendements:
            old = str(
                amendement.user_table.user) if amendement.user_table else ""
            new = str(target_table.user) if target_table else ""
            if amendement.user_table is target_table:
                continue
            amendement.user_table = target_table
            amendement.stop_editing()
            AmendementTransfere.create(self.request, amendement, old, new)

        if target != self.request.user.email and self.request.POST.get(
                "from_index"):
            next_location = self.request.resource_url(
                self.context.lecture_resource, "amendements")
        else:
            next_location = self.request.resource_url(self.context.parent,
                                                      self.owner.email)
        return HTTPFound(location=next_location)
Exemple #16
0
 def post(self) -> Response:
     titre = self.shared_table.titre
     DBSession.delete(self.shared_table)
     SharedTableSupprimee.create(lecture=self.lecture,
                                 titre=titre,
                                 request=self.request)
     self.request.session.flash(
         Message(cls="success",
                 text=f"Boîte « {titre} » supprimée avec succès."))
     return HTTPFound(location=self.request.resource_url(
         self.context.lecture_resource, "options", anchor="shared-tables"))
    def check_amendements_are_all_from_same_mission(
            self, amendements: List[Amendement]) -> None:
        first_mission_titre = amendements[0].mission_titre
        are_all_from_same_mission = all(
            amdt.mission_titre == first_mission_titre for amdt in amendements)
        if are_all_from_same_mission:
            return

        message = (
            "Tous les amendements doivent être relatifs à la même mission "
            "pour pouvoir être associés.")
        self.request.session.flash(Message(cls="danger", text=message))
        raise HTTPFound(location=self.my_table_url)
    def check_amendements_are_all_on_my_table(
            self, amendements: List[Amendement]) -> None:
        are_all_on_my_table = all(
            amendement.location.user_table.user ==
            self.request.user if amendement.location.user_table else False
            for amendement in amendements)
        if are_all_on_my_table:
            return

        message = ("Tous les amendements doivent être sur votre table "
                   "pour pouvoir les associer.")
        self.request.session.flash(Message(cls="danger", text=message))
        raise HTTPFound(location=self.my_table_url)
Exemple #19
0
    def post(self) -> Response:
        user_pk: str = self.request.POST.get("pk")
        user = DBSession.query(User).filter(User.pk == user_pk).one()
        target = str(user)

        self.dossier.team.users.remove(user)

        DossierRetrait.create(dossier=self.dossier, target=target, request=self.request)
        self.request.session.flash(
            Message(
                cls="success", text=(f"{target} a été retiré·e du dossier avec succès.")
            )
        )
        return HTTPFound(location=self.request.resource_url(self.context, "retrait"))
Exemple #20
0
 def post(self) -> Response:
     email_pattern_pk = self.request.POST["pk"]
     allowed_email_pattern = (
         DBSession.query(AllowedEmailPattern).filter_by(
             pk=email_pattern_pk).first())
     WhitelistRemove.create(allowed_email_pattern=allowed_email_pattern,
                            request=self.request)
     self.request.session.flash(
         Message(
             cls="success",
             text=(
                 "Adresse de courriel ou modèle supprimé(e) avec succès."),
         ))
     return HTTPFound(location=self.request.resource_url(self.context))
Exemple #21
0
def forbidden_view(request: Request) -> Any:

    # Redirect unauthenticated users to the login page
    if request.user is None:
        return HTTPFound(
            location=request.route_url("user_login", _query={"source": request.url})
        )

    # Redirect authenticated ones to the home page with an error message
    request.session.flash(
        Message(
            cls="warning",
            text="L’accès à cette lecture est réservé aux personnes autorisées.",
        )
    )
    return HTTPFound(location=request.resource_url(request.root))
Exemple #22
0
 def post(self) -> Response:
     old_titre = self.shared_table.titre
     titre: str = self.request.POST.get("titre")
     self.shared_table.titre = titre
     self.shared_table.slug = slugify(titre)
     SharedTableRenommee.create(
         lecture=self.lecture,
         old_titre=old_titre,
         new_titre=titre,
         request=self.request,
     )
     self.request.session.flash(
         Message(cls="success",
                 text=f"Boîte « {titre} » sauvegardée avec succès."))
     return HTTPFound(location=self.request.resource_url(
         self.context.lecture_resource, "options", anchor="shared-tables"))
    def check_amendements_have_all_same_reponse_or_empty(
            self, amendements: List[Amendement]) -> None:
        reponses = (amendement.user_content.as_tuple()
                    for amendement in amendements)
        non_empty_reponses = (reponse for reponse in reponses
                              if not reponse.is_empty)

        if len(set(
                non_empty_reponses)) <= 1:  # all the same (1) or all empty (0)
            return

        message = (
            "Tous les amendements doivent avoir les mêmes réponses et commentaires "
            "avant de pouvoir être associés.")
        self.request.session.flash(Message(cls="danger", text=message))
        raise HTTPFound(location=self.my_table_url)
Exemple #24
0
def forbidden_view(exception: HTTPForbidden, request: Request) -> Any:

    # Redirect unauthenticated users to the login page
    if request.user is None:
        return HTTPFound(
            location=request.route_url("login", _query={"source": request.url})
        )

    # Default
    message = "L’accès à ce dossier est réservé aux personnes autorisées."
    next_resource = request.root

    if isinstance(exception.result, ACLDenied):
        acl_denied: ACLDenied = exception.result
        if acl_denied.permission == "delete":
            message = "Vous n’êtes pas autorisé à supprimer ce dossier."
            next_resource = request.context
        elif acl_denied.permission == "retrait":
            message = "Vous n’êtes pas autorisé à retirer une personne de ce dossier."
            next_resource = request.context

    request.session.flash(Message(cls="warning", text=message))
    return HTTPFound(location=request.resource_url(next_resource))
Exemple #25
0
    def post(self) -> Response:
        dossier_ref = self._get_dossier_ref()
        lecture_ref = self._get_lecture_ref(dossier_ref)

        chambre = lecture_ref.chambre.value
        titre = lecture_ref.titre
        organe = lecture_ref.organe
        partie = lecture_ref.partie

        session = lecture_ref.get_session()
        texte = lecture_ref.texte

        assert texte.date_depot is not None

        texte_model = get_one_or_create(
            TexteModel,
            uid=texte.uid,
            type_=texte.type_,
            chambre=Chambre.AN
            if lecture_ref.chambre.value == "an" else Chambre.SENAT,
            legislature=int(session) if chambre == "an" else None,
            session=int(session.split("-")[0]) if chambre == "senat" else None,
            numero=texte.numero,
            titre_long=texte.titre_long,
            titre_court=texte.titre_court,
            date_depot=texte.date_depot,
        )[0]

        dossier_model = get_one_or_create(DossierModel,
                                          uid=dossier_ref.uid,
                                          titre=dossier_ref.titre)[0]

        if LectureModel.exists(chambre, session, texte_model, partie, organe):
            self.request.session.flash(
                Message(cls="warning", text="Cette lecture existe déjà…"))
            return HTTPFound(location=self.request.resource_url(self.context))

        lecture_model: LectureModel = LectureModel.create(
            owned_by_team=self.request.team,
            chambre=chambre,
            session=session,
            texte=texte_model,
            partie=partie,
            titre=titre,
            organe=organe,
            dossier=dossier_model,
        )
        get_articles(lecture_model)
        LectureCreee.create(self.request, lecture=lecture_model)
        ArticlesRecuperes.create(request=None, lecture=lecture_model)
        # Call to fetch_* tasks below being asynchronous, we need to make
        # sure the lecture_model already exists once and for all in the database
        # for future access. Otherwise, it may create many instances and
        # thus many objects within the database.
        transaction.commit()
        fetch_amendements(lecture_model.pk)
        self.request.session.flash(
            Message(
                cls="success",
                text=
                ("Lecture créée avec succès, amendements en cours de récupération."
                 ),
            ))
        return HTTPFound(location=self.request.resource_url(
            self.context[lecture_model.url_key], "amendements"))
Exemple #26
0
def manual_refresh(context: DossierResource, request: Request) -> Response:
    dossier = context.dossier
    update_dossier(dossier.pk, force=True)
    request.session.flash(
        Message(cls="success", text="Rafraîchissement des lectures en cours."))
    return HTTPFound(location=request.resource_url(context))
Exemple #27
0
    def post(self) -> Response:

        # Only consider well formed addresses from allowed domains
        emails = self._extract_emails(self.request.POST.get("emails"))
        bad_emails, clean_emails = self._clean_emails(emails)

        # Create user accounts if needed
        new_users, existing_users = self._find_or_create_users(clean_emails)

        team = self.dossier.team

        # Users that already have a Zam account, but are not yet members
        new_members, existing_members = self._identify_members(existing_users, team)

        users_to_invite = new_users + new_members

        team.add_members(users_to_invite)

        invitations_sent = 0
        if new_users:
            invitations_sent += self._send_new_users_invitations(new_users)
        if new_members:
            invitations_sent += self._send_existing_users_invitations(new_members)

        for user in users_to_invite:
            InvitationEnvoyee.create(
                dossier=self.dossier, email=user.email, request=self.request
            )

        if invitations_sent:
            if invitations_sent > 1:
                message = "Invitations envoyées avec succès."
            else:
                message = "Invitation envoyée avec succès."
            cls = "success"
        else:
            message = "Aucune invitation n’a été envoyée."
            cls = "warning"

        if existing_members:
            existing_emails = [user.email for user in existing_members]
            message += "<br><br>"
            if len(existing_emails) > 1:
                message += (
                    f"Les adresses courriel {enumeration(existing_emails)} "
                    "avaient déjà été invitées au dossier précédemment."
                )
            else:
                message += (
                    f"L’adresse courriel {existing_emails[0]} "
                    "avait déjà été invitée au dossier précédemment."
                )

        if bad_emails:
            message += "<br><br>"
            if len(bad_emails) > 1:
                message += (
                    f"Les adresses courriel {enumeration(bad_emails)} "
                    "sont mal formées ou non autorisées et n’ont pas été invitées."
                )
            else:
                message += (
                    f"L’adresse courriel {bad_emails[0]} "
                    "est mal formée ou non autorisée et n’a pas été invitée."
                )

        self.request.session.flash(Message(cls=cls, text=message))

        return HTTPFound(location=self.request.resource_url(self.context))
def upload_liasse_xml(context: LectureResource, request: Request) -> Response:
    try:
        liasse_field = request.POST["liasse"]
    except KeyError:
        request.session.flash(
            Message(cls="warning",
                    text="Veuillez d’abord sélectionner un fichier"))
        return HTTPFound(location=request.resource_url(context, "options"))

    if liasse_field == b"":
        request.session.flash(
            Message(cls="warning",
                    text="Veuillez d’abord sélectionner un fichier"))
        return HTTPFound(location=request.resource_url(context, "options"))

    # Backup uploaded file to make troubleshooting easier
    backup_path = get_backup_path(request)
    if backup_path is not None:
        save_uploaded_file(liasse_field, backup_path)

    lecture = context.model()

    try:
        amendements, errors = import_liasse_xml(liasse_field.file, lecture)
    except ValueError:
        logger.exception("Erreur d'import de la liasse XML")
        request.session.flash(
            Message(cls="danger",
                    text="Le format du fichier n’est pas valide."))
        return HTTPFound(location=request.resource_url(context, "options"))
    except LectureDoesNotMatch as exc:
        request.session.flash(
            Message(
                cls="danger",
                text=
                f"La liasse correspond à une autre lecture ({exc.lecture_fmt}).",
            ))
        return HTTPFound(location=request.resource_url(context, "options"))

    if errors:
        if len(errors) == 1:
            what = "l'amendement"
        else:
            what = "les amendements"
        uids = ", ".join(uid for uid, cause in errors)
        request.session.flash(
            Message(cls="warning",
                    text=f"Impossible d'importer {what} {uids}."))

    if len(amendements) == 0:
        request.session.flash(
            Message(
                cls="warning",
                text="Aucun amendement valide n’a été trouvé dans ce fichier.",
            ))
        return HTTPFound(location=request.resource_url(context, "options"))

    if len(amendements) == 1:
        message = "1 nouvel amendement récupéré (import liasse XML)."
    else:
        message = (
            f"{len(amendements)} nouveaux amendements récupérés (import liasse XML)."
        )
    request.session.flash(Message(cls="success", text=message))
    AmendementsRecuperesLiasse.create(request, lecture, count=len(amendements))
    DBSession.add(lecture)
    return HTTPFound(location=request.resource_url(context, "amendements"))
Exemple #29
0
    def post(self) -> Response:
        """
        Transfer amendement(s) from this table to another one, or back to the index
        """
        nums: List[str] = self.get_nums()
        if "submit-index" in self.request.POST:
            target = ""
        elif "submit-table" in self.request.POST:
            target = self.request.user.email
        else:
            target = self.request.POST.get("target")
            if not target:
                self.request.session.flash(
                    Message(cls="warning",
                            text="Veuillez sélectionner un·e destinataire."))
                return HTTPFound(location=self.request.resource_url(
                    self.context.lecture_resource,
                    "transfer_amendements",
                    query={"n": nums},
                ))

        target_user_table = self.get_target_user_table(target)
        target_shared_table = self.get_target_shared_table(target)

        amendements = DBSession.query(Amendement).filter(
            Amendement.lecture == self.lecture,
            Amendement.num.in_(nums)  # type: ignore
        )

        for amendement in Batch.expanded_batches(amendements):
            old = amendement.table_name_with_email
            if target_shared_table:
                if target and amendement.location.shared_table is target_shared_table:
                    continue
                new = target_shared_table.titre
                amendement.location.shared_table = target_shared_table
                amendement.location.user_table = None
            else:
                if target and amendement.location.user_table is target_user_table:
                    continue
                new = str(target_user_table.user) if target_user_table else ""
                amendement.location.user_table = target_user_table
                amendement.location.shared_table = None
            amendement.stop_editing()
            AmendementTransfere.create(
                amendement=amendement,
                old_value=old,
                new_value=new,
                request=self.request,
            )

        if target != self.request.user.email and self.request.POST.get(
                "from_index"):
            amendements_collection = self.context.lecture_resource[
                "amendements"]
            next_location = self.request.resource_url(amendements_collection)
        else:
            table = self.context.model()
            table_resource = self.context.parent[table.user.email]
            next_location = self.request.resource_url(table_resource)
        return HTTPFound(location=next_location)