示例#1
0
def lecture_options(context: LectureResource, request: Request) -> Response:
    lecture = context.model()
    return {
        "lecture": lecture,
        "lecture_resource": context,
        "current_tab": "options"
    }
示例#2
0
文件: download.py 项目: betagouv/zam
def download_amendements(context: LectureResource, request: Request) -> Response:
    fmt: str = request.params.get("format", "")
    if fmt not in DOWNLOAD_FORMATS.keys():
        raise HTTPBadRequest(f'Invalid value "{fmt}" for "format" param')

    if fmt == "pdf":
        options = PDF_OPTIONS
    else:
        options = EXPORT_OPTIONS

    lecture = context.model(*options)

    with NamedTemporaryFile() as file_:

        tmp_file_path = os.path.abspath(file_.name)

        write_func, content_type = DOWNLOAD_FORMATS[fmt]
        write_func(lecture, tmp_file_path, request)  # type: ignore

        response = FileResponse(tmp_file_path)
        attach_name = (
            f"lecture-{lecture.chambre}-{lecture.texte.numero}-"
            f"{lecture.organe}.{fmt}"
        )
        response.content_type = content_type
        response.headers["Content-Disposition"] = f"attachment; filename={attach_name}"
        return response
示例#3
0
def fetch_amendements(context: LectureResource, request: Request) -> Response:
    lecture = context.model()

    amendements, errored = get_amendements(
        chambre=lecture.chambre,
        session=lecture.session,
        texte=lecture.num_texte,
        organe=lecture.organe,
    )

    if errored:
        request.session.flash((
            "warning",
            f"Les amendements {', '.join(errored)} n’ont pu être récupérés.",
        ))

    if amendements:
        added, updated, unchanged = _add_or_update_amendements(amendements)
        assert added + updated + unchanged == len(amendements)
        _set_flash_messages(request, added, updated, unchanged)
    else:
        request.session.flash(
            ("danger", "Aucun amendement n’a pu être trouvé."))

    return HTTPFound(location=request.resource_url(context))
示例#4
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)
示例#5
0
def _do_upload_liasse_xml(context: LectureResource,
                          request: Request) -> Response:
    try:
        liasse_field = request.POST["liasse"]
    except KeyError:
        request.session.flash(
            ("warning", "Veuillez d’abord sélectionner un fichier"))
        return

    if liasse_field == b"":
        request.session.flash(
            ("warning", "Veuillez d’abord sélectionner un fichier"))
        return

    try:
        amendements = import_liasse_xml(liasse_field.file)
    except ValueError:
        request.session.flash(
            ("danger", "Le format du fichier n’est pas valide."))
        return

    if len(amendements) == 0:
        request.session.flash(
            ("warning", "Aucun amendement n’a été trouvé dans ce fichier."))
        return

    lecture = context.model()
    filtered_amendements = [
        amendement for amendement in amendements
        if amendement.chambre == lecture.chambre and amendement.session ==
        lecture.session and amendement.num_texte == lecture.num_texte
        and amendement.organe == lecture.organe
    ]
    ignored = len(amendements) - len(filtered_amendements)

    if len(filtered_amendements) == 0:
        amendement = amendements[0]
        other_lecture = Lecture(
            chambre=amendement.chambre,
            session=amendement.session,
            num_texte=amendement.num_texte,
            organe=amendement.organe,
        )
        request.session.flash(
            ("danger",
             f"La liasse correspond à une autre lecture ({other_lecture})."))
        return

    if ignored > 0:
        request.session.flash(
            ("warning",
             f"{ignored} amendements ignorés car non liés à cette lecture."))

    added, updated, unchanged = _add_or_update_amendements(
        filtered_amendements)
    assert added + updated + unchanged == len(filtered_amendements)
    _set_flash_messages(request, added, updated, unchanged)
示例#6
0
 def __init__(self, context: LectureResource, request: Request) -> None:
     self.context = context
     self.request = request
     self.lecture = context.model()
     self.amendements_query = DBSession.query(Amendement).filter(
         Amendement.chambre == self.lecture.chambre,
         Amendement.session == self.lecture.session,
         Amendement.num_texte == self.lecture.num_texte,
         Amendement.organe == self.lecture.organe,
     )
示例#7
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"))
示例#8
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)
示例#9
0
文件: lecture.py 项目: betagouv/zam
def lecture_options(context: LectureResource, request: Request) -> Response:
    lecture = context.model(noload("amendements"))
    shared_tables = (DBSession.query(SharedTable).filter(
        SharedTable.lecture_pk == lecture.pk).options(
            load_only("lecture_pk", "nb_amendements", "slug", "titre"))).all()
    return {
        "lecture": lecture,
        "dossier_resource": context.dossier_resource,
        "lecture_resource": context,
        "current_tab": "options",
        "shared_tables": shared_tables,
    }
示例#10
0
文件: download.py 项目: betagouv/zam
def export_pdf(context: LectureResource, request: Request) -> Response:
    lecture = context.model(
        noload("amendements"),
        DOSSIER_OPTIONS,
        subqueryload("articles").options(joinedload("user_content")),
    )
    nums, article_param = parse_params(request, lecture=lecture)
    if article_param == "all":
        article_amendements = (
            DBSession.query(Amendement)
            .join(Article)
            .filter(Amendement.lecture == lecture,)
            .options(USER_CONTENT_OPTIONS, LOCATION_OPTIONS)
        )
    else:
        article_type, article_num, article_mult, article_pos = article_param.split(".")
        article_amendements = (
            DBSession.query(Amendement)
            .filter(
                Article.pk == Amendement.article_pk,
                Amendement.lecture == lecture,
                Article.type == article_type,
                Article.num == article_num,
                Article.mult == article_mult,
                Article.pos == article_pos,
            )
            .options(USER_CONTENT_OPTIONS, LOCATION_OPTIONS,)
        )

    amendements = [
        amendement for amendement in article_amendements if amendement.num in nums
    ]
    expanded_amendements = list(Batch.expanded_batches(amendements))

    with NamedTemporaryFile() as file_:
        tmp_file_path = os.path.abspath(file_.name)
        write_pdf_multiple(
            lecture=lecture,
            amendements=amendements,
            article_amendements=AmendementList(article_amendements),
            filename=tmp_file_path,
            request=request,
        )
        return write_response(
            tmp_file_path=tmp_file_path,
            fmt="pdf",
            lecture=lecture,
            article_param=article_param,
            amendements=expanded_amendements,
        )
示例#11
0
文件: lecture.py 项目: betagouv/zam
def lecture_journal(context: LectureResource, request: Request) -> Response:
    lecture = context.model(noload("amendements"))
    settings = request.registry.settings
    refreshable = lecture.refreshable_for("articles",
                                          settings) or lecture.refreshable_for(
                                              "amendements", settings)
    can_refresh = request.has_permission("refresh", context)
    refreshing = lecture.get_fetch_progress()
    allowed_to_refresh = refreshable and can_refresh and not refreshing
    return {
        "lecture": lecture,
        "dossier_resource": context.dossier_resource,
        "lecture_resource": context,
        "current_tab": "journal",
        "today": date.today(),
        "allowed_to_refresh": allowed_to_refresh,
    }
示例#12
0
文件: lecture.py 项目: betagouv/zam
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))
示例#13
0
文件: download.py 项目: betagouv/zam
def export_xlsx(context: LectureResource, request: Request) -> Response:
    lecture = context.model(noload("amendements"))
    nums, article_param = parse_params(request, lecture=lecture)
    if article_param == "all":
        amendements = (
            DBSession.query(Amendement)
            .join(Article)
            .filter(
                Amendement.lecture == lecture, Amendement.num.in_(nums),  # type: ignore
            )
            .options(USER_CONTENT_OPTIONS, LOCATION_OPTIONS)
        )
    else:
        article_type, article_num, article_mult, article_pos = article_param.split(".")
        amendements = (
            DBSession.query(Amendement)
            .filter(
                Article.pk == Amendement.article_pk,
                Amendement.lecture == lecture,
                Article.type == article_type,
                Article.num == article_num,
                Article.mult == article_mult,
                Article.pos == article_pos,
                Amendement.num.in_(nums),  # type: ignore
            )
            .options(USER_CONTENT_OPTIONS, LOCATION_OPTIONS)
        )

    expanded_amendements = list(Batch.expanded_batches(amendements))

    with NamedTemporaryFile() as file_:
        tmp_file_path = os.path.abspath(file_.name)
        write_xlsx(lecture, tmp_file_path, request, amendements=expanded_amendements)
        return write_response(
            tmp_file_path=tmp_file_path,
            fmt="xlsx",
            lecture=lecture,
            article_param=article_param,
            amendements=expanded_amendements,
        )
示例#14
0
def download_amendements(context: LectureResource, request: Request) -> Response:
    lecture = context.model()

    amendements = (
        DBSession.query(Amendement)
        .filter(
            Amendement.chambre == lecture.chambre,
            Amendement.session == lecture.session,
            Amendement.num_texte == lecture.num_texte,
            Amendement.organe == lecture.organe,
        )
        .order_by(
            case([(Amendement.position.is_(None), 1)], else_=0),  # type: ignore
            Amendement.position,
            Amendement.num,
        )
        .all()
    )

    fmt: str = request.params.get("format", "")
    if fmt not in ("csv", "xlsx", "pdf"):
        raise HTTPBadRequest(f'Invalid value "{fmt}" for "format" param')

    with NamedTemporaryFile() as file_:

        tmp_file_path = os.path.abspath(file_.name)

        write_func, content_type = DOWNLOAD_FORMATS[fmt]

        write_func(lecture, amendements, tmp_file_path, request)

        response = FileResponse(tmp_file_path)
        attach_name = (
            f"amendements-{lecture.chambre}-{lecture.session}-{lecture.num_texte}-"
            f"{lecture.organe}.{fmt}"
        )
        response.content_type = content_type
        response.headers["Content-Disposition"] = f"attachment; filename={attach_name}"
        return response
示例#15
0
def export_pdf(context: LectureResource, request: Request) -> Response:

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

    try:
        nums: List[int] = [int(num) for num in request.params.getall("nums")]
    except ValueError:
        raise HTTPBadRequest()

    amendements = [
        amendement
        for amendement in (lecture.find_amendement(num) for num in nums)
        if amendement is not None
    ]

    with NamedTemporaryFile() as file_:

        tmp_file_path = os.path.abspath(file_.name)

        write_pdf_multiple(
            lecture=lecture,
            amendements=amendements,
            filename=tmp_file_path,
            request=request,
        )

        response = FileResponse(tmp_file_path)
        attach_name = (
            f"amendement{'s' if len(nums) > 1 else ''}-"
            f"{','.join(str(num) for num in nums)}-"
            f"{lecture.chambre}-{lecture.session}-{lecture.texte.numero}-"
            f"{lecture.organe}.pdf")
        response.content_type = "application/pdf"
        response.headers[
            "Content-Disposition"] = f"attachment; filename={attach_name}"
        return response
示例#16
0
文件: lecture.py 项目: betagouv/zam
def search_amendement(context: LectureResource, request: Request) -> dict:
    lecture = context.model(noload("amendements"))

    try:
        num_param: str = request.params.get("num", "")
        num: int = int(num_param)
    except ValueError:
        raise HTTPBadRequest()

    amendement = (DBSession.query(Amendement).filter(
        Amendement.lecture == lecture, Amendement.num == num).first())
    if amendement is None:
        raise HTTPBadRequest()

    total_count_amendements = lecture.nb_amendements
    max_amendements_for_full_index = int(
        request.registry.settings.get(
            "zam.limits.max_amendements_for_full_index", 1000))
    too_many_amendements = total_count_amendements > max_amendements_for_full_index

    result = {
        "index":
        request.resource_url(
            context["amendements"],
            query={
                "article":
                amendement.article.url_key if too_many_amendements else "all"
            },
            anchor=amendement.slug,
        ),
    }
    if amendement.is_displayable:
        result["visionneuse"] = request.resource_url(
            context["articles"][amendement.article.url_key],
            "reponses",
            anchor=amendement.slug,
        )
    return result
示例#17
0
def list_reponses(context: LectureResource, request: Request) -> Response:
    lecture = context.model()
    amendements = (
        DBSession.query(Amendement).filter(
            Amendement.chambre == lecture.chambre,
            Amendement.session == lecture.session,
            Amendement.num_texte == lecture.num_texte,
            Amendement.organe == lecture.organe,
        ).order_by(
            case([(Amendement.position.is_(None), 1)],
                 else_=0),  # type: ignore
            Amendement.position,
            Amendement.num,
        ).all())
    articles = build_tree(amendements)
    check_url = request.resource_path(context, "check")
    return {
        "dossier_legislatif": lecture.dossier_legislatif,
        "lecture": str(lecture),
        "articles": articles,
        "timestamp": lecture.modified_at_timestamp,
        "check_url": check_url,
    }
示例#18
0
def lecture_check(context: LectureResource, request: Request) -> dict:
    lecture = context.model()
    return {"modified_at": lecture.modified_at_timestamp}
示例#19
0
def fetch_articles(context: LectureResource, request: Request) -> Response:
    lecture = context.model()
    get_articles(lecture)
    request.session.flash(("success", f"Articles récupérés"))
    return HTTPFound(location=request.resource_url(context))
示例#20
0
def lecture_journal(context: LectureResource, request: Request) -> Response:
    lecture = context.model()
    return {"lecture": lecture, "today": date.today()}
示例#21
0
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"))
示例#22
0
文件: lecture.py 项目: betagouv/zam
def progress_status(context: LectureResource, request: Request) -> dict:
    lecture = context.model(noload("amendements"))
    return lecture.get_fetch_progress() or {}
示例#23
0
 def __init__(self, context: LectureResource, request: Request) -> None:
     self.context = context
     self.request = request
     self.lecture = context.model()