Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/home/display latest discussions #3154

Merged
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ffae686
feat(wip): discussion on page home
andriacap Jul 25, 2024
aea50e1
feat(wip): column responsibility changed + orderby
andriacap Jul 26, 2024
7e30aa8
feat(back): add test for route list_reports
andriacap Jul 26, 2024
fe879b6
fix: sort dir / orderby and pages
andriacap Jul 29, 2024
32aaa8d
feat: add config for LATEST_DISCUSSIONS
andriacap Jul 29, 2024
6eaa269
feat(front): add column mode force
andriacap Aug 19, 2024
cf3cf24
style(backend): apply linter black
andriacap Aug 19, 2024
783b9d9
style(frontend): apply prettier
andriacap Aug 19, 2024
17f6284
refactor: change checkbox to mat-slide-toggle
andriacap Aug 19, 2024
d0205c0
refactor: split code in components
andriacap Aug 20, 2024
ccbafc4
refact(frontend): remove generic table
andriacap Aug 21, 2024
48ca4f6
refactor(frontend): use standalone
andriacap Aug 21, 2024
2e2537d
refactor(frontend): make selected row works
andriacap Aug 21, 2024
eb04139
refactor(frontend): add condition to display/hide
andriacap Aug 21, 2024
2da923f
refactor: move config LATEST_DISCUSSION sample
andriacap Aug 28, 2024
de66c74
feat(frontend): use same css from other datatable
andriacap Aug 28, 2024
a79b282
refactore: set default True for LATEST_DISCUSSION
andriacap Aug 28, 2024
a111dd8
fix(frontend): undefined pageChanged Method
andriacap Aug 28, 2024
97cdc93
feat(frontend): add information icon and click evt
andriacap Aug 28, 2024
8a0d65a
style: remove console.log
andriacap Aug 28, 2024
ce49260
style(frontend): apply prettier
andriacap Aug 28, 2024
515a769
feat: add filtered count result to route /reports
andriacap Aug 28, 2024
50cc23e
styl(frontend): apply linter prettier
andriacap Aug 28, 2024
b9e6d01
fix: style and comment
andriacap Sep 3, 2024
e04c8e8
fix: orderby, joinedload, split routes
andriacap Sep 3, 2024
b84a0e9
fix(test): wip - split test related reports routes
andriacap Sep 4, 2024
18ee0ad
fix: change way to read permission synthese
andriacap Sep 16, 2024
d38eea7
feat: add missing test for pin case in list_reports
edelclaux Sep 17, 2024
edfec67
feat: add missing test for unknow type in list_reports
edelclaux Sep 18, 2024
9575545
test: dummy commit to trigger ci pipeline
edelclaux Sep 18, 2024
7333851
test: add undefined test in lsit_all_reports
edelclaux Sep 30, 2024
efd9d4e
test: adjust phrasing
edelclaux Sep 30, 2024
7d768a9
test: add nortigjtuser scenario in list_reports
edelclaux Sep 30, 2024
4803996
feat(routes, report) : translate to sqla2.0
jacquesfize Oct 4, 2024
8346915
fix(test) : sorted does not work exactly like psql sort
jacquesfize Oct 4, 2024
0306bf0
fix(blueprint,report): change query to feat flasksqlalchemy paginate(…
jacquesfize Oct 4, 2024
8c2b874
fix(latest_discussions): fix number of page
jacquesfize Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 38 additions & 49 deletions backend/geonature/core/gn_synthese/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
jsonify,
g,
)
from geonature.core.gn_synthese.schemas import SyntheseSchema
from geonature.core.gn_synthese.schemas import ReportSchema, SyntheseSchema
from geonature.core.gn_synthese.synthese_config import MANDATORY_COLUMNS
from pypnusershub.db.models import User
from pypnnomenclature.models import BibNomenclaturesTypes, TNomenclatures
Expand Down Expand Up @@ -1491,6 +1491,7 @@ def update_content_report(id_report):
@routes.route("/reports", methods=["GET"])
@permissions_required("R", module_code="SYNTHESE")
def list_all_reports(permissions):
# Parameters
type_name = request.args.get("type")
orderby = request.args.get("orderby", "creation_date")
sort = request.args.get("sort")
Expand All @@ -1499,9 +1500,8 @@ def list_all_reports(permissions):
my_reports = request.args.get("my_reports", "false").lower() == "true"

# Start query
req = (
db.session.query(TReport, User.nom_complet)
.select_from(TReport)
query = (
sa.select(TReport, User.nom_complet)
.join(User, TReport.id_role == User.id_role)
.options(
joinedload(TReport.report_type).load_only(
Expand All @@ -1516,26 +1516,25 @@ def list_all_reports(permissions):
),
)
)

# Verify and filter by type
if type_name:
type_exists = BibReportsTypes.query.filter_by(type=type_name).one_or_none()
type_exists = db.session.scalar(
sa.exists(BibReportsTypes).where(BibReportsTypes.type == type_name).select()
)
if not type_exists:
raise BadRequest("This report type does not exist")
req = req.where(TReport.report_type.has(BibReportsTypes.type == type_name))
query = query.where(TReport.report_type.has(BibReportsTypes.type == type_name))

# Filter by id_role for 'pin' type only or if my_reports is true
if type_name == "pin" or my_reports:
req = req.where(TReport.id_role == g.current_user.id_role)
query = query.where(TReport.id_role == g.current_user.id_role)

# On vérifie les permissions en lecture sur la synthese
query = select(Synthese.id_synthese).select_from(Synthese)
synthese_query_obj = SyntheseQuery(Synthese, query, {})
synthese_query = select(Synthese.id_synthese).select_from(Synthese)
synthese_query_obj = SyntheseQuery(Synthese, synthese_query, {})
synthese_query_obj.filter_query_with_cruved(g.current_user, permissions)
res_ids_synthese = db.session.execute(synthese_query_obj.query)
result = res_ids_synthese.fetchall()
ids_synthese = [row[0] for row in result]
req = req.filter(TReport.id_synthese.in_(ids_synthese))
ids_synthese = db.session.scalars(synthese_query_obj.query).all()
query = query.where(TReport.id_synthese.in_(ids_synthese))

SORT_COLUMNS = {
"user.nom_complet": User.nom_complet,
Expand All @@ -1547,18 +1546,23 @@ def list_all_reports(permissions):
if orderby in SORT_COLUMNS:
sort_column = SORT_COLUMNS[orderby]
if sort == "desc":
req = req.order_by(desc(sort_column))
query = query.order_by(desc(sort_column))
else:
req = req.order_by(asc(sort_column))
query = query.order_by(asc(sort_column))
else:
raise BadRequest("Bad orderby")

total = TReport.query.count()
paginated_results = req.paginate(page=page, per_page=per_page, error_out=False)
# Pagination
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C'est une bonne pratique ça de faire la pagination à la mano ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dans GeoNature, c'est flask-sqlalchemy qui fait la pagination. Le soucis, c'est qu'ils utilisent la méthode scalars() lors de l'exécution d'une requête construite avec un objet Select (remplace Query dans SQLA 2.0) (https://github.com/pallets-eco/flask-sqlalchemy/blob/main/src/flask_sqlalchemy/pagination.py#L328-L364). Ici, c'est pas ce que l'on souhaite. SI on veut que la pagination flask-sqlalchemy fonctionne ici, il faudrait passer par une requête construite avec un object Query mais qui n'est plus recommandé dans SQLA 2.0 😕

Du coup, je propose de faire la pagination "à la main" en s'appuyant sur quasi le même fonctionnement de flask-sqlalchemy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On peut aussi modifier la requête, récupérer le nom_complet de l'utilisateur dans un joinedload et repasser sur la pagination flask-sqlalchemy, ça devrait marcher 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c'est fait :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Top, merci !

total = db.session.scalar(select(func.count("*")).select_from(query))
nb_pages = total // per_page
offset = per_page * (page - 1)
query = query.limit(per_page).offset(offset)

paginated_results = db.session.execute(query).all()

result = []

for report, nom_complet in paginated_results.items:
for report, nom_complet in paginated_results:
report_dict = {
"id_report": report.id_report,
"id_synthese": report.id_synthese,
Expand All @@ -1582,11 +1586,11 @@ def list_all_reports(permissions):
result.append(report_dict)

response = {
"total_filtered": paginated_results.total,
"total_filtered": len(paginated_results),
"total": total,
"pages": paginated_results.pages,
"current_page": paginated_results.page,
"per_page": paginated_results.per_page,
"pages": nb_pages,
"current_page": page,
"per_page": per_page,
"items": result,
}
return jsonify(response)
Expand All @@ -1596,50 +1600,35 @@ def list_all_reports(permissions):
@permissions_required("R", module_code="SYNTHESE")
def list_reports(permissions, id_synthese):
type_name = request.args.get("type")
# Start query
req = TReport.query

synthese = db.get_or_404(Synthese, id_synthese)
if not synthese.has_instance_permission(permissions):
raise Forbidden
req = req.where(TReport.id_synthese == id_synthese)

query = sa.select(TReport).where(TReport.id_synthese == id_synthese)

# Verify and filter by type
if type_name:
type_exists = BibReportsTypes.query.filter_by(type=type_name).one_or_none()
type_exists = db.session.scalar(
sa.exists(BibReportsTypes).where(BibReportsTypes.type == type_name).select()
)
if not type_exists:
raise BadRequest("This report type does not exist")
req = req.where(TReport.report_type.has(BibReportsTypes.type == type_name))
query = query.where(TReport.report_type.has(BibReportsTypes.type == type_name))

# Filter by id_role for 'pin' type only
if type_name == "pin":
req = req.where(TReport.id_role == g.current_user.id_role)
query = query.where(TReport.id_role == g.current_user.id_role)

# Join the User table
req = req.options(
query = query.options(
joinedload(TReport.user).load_only(User.nom_role, User.prenom_role),
joinedload(TReport.report_type),
)

response = [
report.as_dict(
fields=[
"id_report",
"id_synthese",
"id_role",
"report_type.type",
"report_type.id_type",
"content",
"deleted",
"creation_date",
"user.nom_role",
"user.prenom_role",
]
)
for report in req.all()
]

return jsonify(response)
return ReportSchema(many=True, only=["+user.nom_role", "+user.prenom_role"]).dump(
db.session.scalars(query).all()
)


@routes.route("/reports/<int:id_report>", methods=["DELETE"])
Expand Down