Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
086736f
add python 3.12 to tests
hypsug0 Apr 8, 2024
fd9927b
WIP fix py3.12 issue
hypsug0 Apr 8, 2024
b420239
WIP fix py3.12 issue
hypsug0 Apr 8, 2024
89c03d6
add id_obs in admin - fix #425
hypsug0 Nov 20, 2024
c75160e
update changelog
hypsug0 Nov 20, 2024
69c242d
Merge pull request #428 from hypsug0/admin_id_observation
hypsug0 Nov 20, 2024
6f075d9
fix #420, avoid scratching password from backoffice
hypsug0 Nov 20, 2024
eac44ea
Merge pull request #429 from hypsug0/fix_420_avoid_scratch_passwd
hypsug0 Nov 20, 2024
82acdae
[doc] fix clicnat-citizen url
PaulLabruyere Jan 14, 2025
244c486
fix: url redirection backoffice admin
andriacap Jan 15, 2025
7d0b83d
feat: add translation deutsch and missing ones
andriacap Feb 14, 2025
fba48e4
feat: add missing config for "de" translation
Feb 17, 2025
735d568
Merge pull request #433 from PaulLabruyere/update/fix_clicnat_url_in_…
camillemonchicourt Feb 26, 2025
a7b68a6
Merge pull request #436 from naturalsolutions/feat/add-translation-de…
lpofredc Mar 20, 2025
af22661
Merge pull request #435 from naturalsolutions/fix/problem-redirection…
lpofredc Mar 20, 2025
6ed5169
feat: add stats home for nb site
andriacap Feb 14, 2025
6e5b077
fix: add missing translation for Sites
andriacap Mar 20, 2025
f11440c
Fix and improve #438
lpofredc Mar 21, 2025
c3facec
this.http.get
lpofredc Mar 21, 2025
4b0e8ff
Merge branch 'naturalsolutions-feat/add-stats-nb-site-home' into dev
lpofredc Mar 21, 2025
241356d
thumb label now clickable, fix #348"
lpofredc Mar 24, 2025
4d09e77
Merge pull request #443 from hypsug0/fix_348
hypsug0 Mar 24, 2025
92fcf8a
update deps & fix docs build
lpofredc Mar 24, 2025
67c3548
update deps & fix docs build
lpofredc Mar 24, 2025
ee09832
Merge remote-tracking branch 'upstream/master' into dev
lpofredc Mar 24, 2025
045349f
Merge pull request #444 from hypsug0/dev
hypsug0 Mar 24, 2025
d7bdb41
Merge branch 'dev' into test_python_3.12
lpofredc Mar 24, 2025
d3fd0ac
Support python 3.12
lpofredc Mar 24, 2025
800f794
fix missing updated requirements
lpofredc Mar 24, 2025
551d2bb
Merge pull request #406 from hypsug0/test_python_3.12
hypsug0 Mar 24, 2025
fb0ebb2
fix hashed password save
lpofredc Mar 25, 2025
d989fab
Merge pull request #446 from hypsug0/fix_445
lpofredc Mar 25, 2025
1021320
Update Changelog & version
lpofredc Mar 25, 2025
9829192
Merge pull request #447 from hypsug0/dev
lpofredc Mar 25, 2025
4ed99f6
update doc
lpofredc Mar 25, 2025
648cde2
Update changelog
lpofredc Mar 25, 2025
8ae2eaf
update requirements
lpofredc Mar 25, 2025
6821a64
Merge pull request #448 from hypsug0/dev
lpofredc Mar 25, 2025
e7dec3b
Review changelog 1.2.0
camillemonchicourt Mar 25, 2025
2a153fc
changelog 1.2.0 / Fix typo
camillemonchicourt Mar 25, 2025
fdf9730
Merge pull request #449 from PnX-SI/review-changelog-120
lpofredc Mar 25, 2025
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
4 changes: 2 additions & 2 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ concurrency:
cancel-in-progress: true

env:
PYTHON_VERSION: 3.11
PYTHON_VERSION: 3.12

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
Expand All @@ -49,7 +49,7 @@ jobs:
- name: Install dependencies
run: |
cd backend
poetry install --only=docs
poetry install --only=docs --no-root

- name: Build doc using Sphinx
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
env:
BACKEND_FOLDER: "backend"
FRONTEND_FOLDER: "frontend"
PYTHON_VERSION: 3.9
PYTHON_VERSION: 3.12

jobs:
lint-py:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
matrix:
os: ["ubuntu-latest"]
node-version: ["14.21.3"]
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12"]
# The type of runner that the job will run on
runs-on: ${{ matrix.os }}

Expand Down
194 changes: 107 additions & 87 deletions docs/CHANGELOG.md → CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Documentation : https://geonature-citizen.readthedocs.io
- https://enquetes.lashf.org
- https://observatoire-biodiversite.parc-du-vercors.fr
- https://enquetes-biodivrennes.fr
- https://enquetes.clicnat.fr
- https://citizen.clicnat.fr
- https://obs-citoyenne.pyrenees-parcnational.fr
- https://observation.lehavre.fr
- https://enquetes.cbiodiv.org
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.0
1.2.0
9 changes: 5 additions & 4 deletions backend/gncitizen/core/commons/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,11 @@ def get_modules():
def get_stat():
try:
stats = {}
stats["nb_obs"] = ObservationModel.query.count()
stats["nb_user"] = UserModel.query.count()
stats["nb_program"] = ProgramsModel.query.filter(ProgramsModel.is_active).count()
stats["nb_espece"] = ObservationModel.query.distinct(ObservationModel.cd_nom).count()
stats["count_obs"] = ObservationModel.query.count()
stats["count_users"] = UserModel.query.count()
stats["count_programs"] = ProgramsModel.query.filter(ProgramsModel.is_active).count()
stats["count_taxa"] = ObservationModel.query.distinct(ObservationModel.cd_nom).count()
stats["count_sites"] = SiteModel.query.count()
return (stats, 200)
except Exception as e:
current_app.logger.critical("[get_observations] Error: %s", str(e))
Expand Down
6 changes: 4 additions & 2 deletions backend/gncitizen/core/observations/admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from flask_admin.actions import action
from wtforms import SelectField

from gncitizen.utils.admin import CustomJSONField, CustomTileView, json_formatter
from server import db
from wtforms import SelectField

from .models import ObservationModel, ValidationStatus

Expand All @@ -15,6 +14,7 @@ def enum_formatter(view, context, model, name):
class ObservationView(CustomTileView):
can_export = True
# column_exclude_list = ["geom"]
column_display_pk = True
form_overrides = {"json_schema": CustomJSONField, "validation_status": SelectField}
form_args = {
"validation_status": {
Expand All @@ -27,6 +27,7 @@ class ObservationView(CustomTileView):
"validation_status": enum_formatter,
}
column_filters = (
"id_observation",
"email",
"cd_nom",
"date",
Expand All @@ -45,6 +46,7 @@ class ObservationView(CustomTileView):
"municipality",
"obs_txt",
"email",
"uuid_sinp",
)
can_create = False

Expand Down
65 changes: 21 additions & 44 deletions backend/gncitizen/core/observations/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@
from flask_jwt_extended import jwt_required
from geoalchemy2.shape import from_shape
from geojson import FeatureCollection
from shapely.geometry import Point, asShape
from sqlalchemy import desc
from utils_flask_sqla.response import json_resp

from gncitizen.core.commons.models import MediaModel, ProgramsModel
from gncitizen.utils.env import admin
from gncitizen.utils.errors import GeonatureApiError
Expand All @@ -23,6 +19,9 @@
from gncitizen.utils.media import save_upload_files
from gncitizen.utils.taxonomy import get_specie_from_cd_nom
from server import db
from shapely.geometry import Point, shape
from sqlalchemy import desc
from utils_flask_sqla.response import json_resp

from .admin import ObservationView
from .models import (
Expand Down Expand Up @@ -187,11 +186,12 @@ def post_observation():
raise GeonatureApiError(e)

try:
_coordinates = json.loads(request_datas["geometry"])
_point = Point(_coordinates["x"], _coordinates["y"])
_shape = asShape(_point)
newobs.geom = from_shape(Point(_shape), srid=4326)
geometry = json.loads(request_datas["geometry"])
shape_geometry = shape(geometry)
newobs.geom = from_shape(shape_geometry, srid=4326)
current_app.logger.debug("[post_observation] newobs geom ", newobs.geom)
if not newobs.municipality:
newobs.municipality = get_municipality_id_from_wkb(shape_geometry)
except Exception as e:
current_app.logger.warning("[post_observation] coords ", e)
raise GeonatureApiError(e) from e
Expand All @@ -217,8 +217,6 @@ def post_observation():
newobs.obs_txt = "Anonyme"

# If municipality is not provided: call API_CITY
if not newobs.municipality:
newobs.municipality = get_municipality_id_from_wkb(_coordinates)

# If taxon name is not provided: call taxhub
if not newobs.name:
Expand All @@ -243,15 +241,11 @@ def post_observation():
newobs.id_observation,
ObservationMediaModel,
)
current_app.logger.debug(
"[post_observation] ObsTax UPLOAD FILE {}".format(file)
)
current_app.logger.debug("[post_observation] ObsTax UPLOAD FILE {}".format(file))
features["properties"]["images"] = file

except Exception as e:
current_app.logger.warning(
"[post_observation] ObsTax ERROR ON FILE SAVING", str(e)
)
current_app.logger.warning("[post_observation] ObsTax ERROR ON FILE SAVING", str(e))
# raise GeonatureApiError(e)

return (
Expand Down Expand Up @@ -371,10 +365,7 @@ def update_observation():
observation_to_update = ObservationModel.query.filter_by(
id_observation=request.form.get("id_observation")
)
if (
observation_to_update.one().id_role != current_user.id_user
and not current_user.validator
):
if observation_to_update.one().id_role != current_user.id_user and not current_user.validator:
abort(403, "unauthorized")

try:
Expand All @@ -393,14 +384,11 @@ def update_observation():
update_obs[prop] = update_data[prop]
if "geometry" in update_data:
try:
_coordinates = json.loads(update_data["geometry"])
_point = Point(_coordinates["x"], _coordinates["y"])
_shape = asShape(_point)
update_obs["geom"] = from_shape(Point(_shape), srid=4326)
geometry = json.loads(update_data["geometry"])
shape_geometry = shape(geometry)
update_obs["geom"] = from_shape(shape_geometry, srid=4326)
if not update_obs["municipality"]:
update_obs["municipality"] = get_municipality_id_from_wkb(
_coordinates
)
update_obs["municipality"] = get_municipality_id_from_wkb(shape_geometry)
except Exception as e:
current_app.logger.warning("[post_observation] coords ", e)
raise GeonatureApiError(e)
Expand All @@ -421,8 +409,7 @@ def update_observation():
if len(id_media_to_delete):
db.session.query(ObservationMediaModel).filter(
ObservationMediaModel.id_media.in_(tuple(id_media_to_delete)),
ObservationMediaModel.id_data_source
== update_data.get("id_observation"),
ObservationMediaModel.id_data_source == update_data.get("id_observation"),
).delete(synchronize_session="fetch")
db.session.query(MediaModel).filter(
MediaModel.id_media.in_(tuple(id_media_to_delete))
Expand All @@ -439,14 +426,10 @@ def update_observation():
update_data.get("id_observation"),
ObservationMediaModel,
)
current_app.logger.debug(
"[post_observation] ObsTax UPLOAD FILE {}".format(file)
)
current_app.logger.debug("[post_observation] ObsTax UPLOAD FILE {}".format(file))

except Exception as e:
current_app.logger.warning(
"[post_observation] ObsTax ERROR ON FILE SAVING", str(e)
)
current_app.logger.warning("[post_observation] ObsTax ERROR ON FILE SAVING", str(e))
# raise GeonatureApiError(e)
obs_validation = (
"non_validatable_status" in update_data
Expand All @@ -463,9 +446,7 @@ def update_observation():
new_validation_status = ValidationStatus.VALIDATED
if non_validatable_status:
status = [
s
for s in INVALIDATION_STATUSES
if s["value"] == non_validatable_status
s for s in INVALIDATION_STATUSES if s["value"] == non_validatable_status
][0]
new_validation_status = ValidationStatus[status["link"]]

Expand Down Expand Up @@ -500,12 +481,8 @@ def update_observation():
),
)
except Exception as e:
current_app.logger.warning(
"send validation_email failed. %s", str(e)
)
return {
"message": f"""send validation_email failed: "{str(e)}" """
}, 400
current_app.logger.warning("send validation_email failed. %s", str(e))
return {"message": f"""send validation_email failed: "{str(e)}" """}, 400

return ("observation updated successfully"), 200
except Exception as e:
Expand Down
5 changes: 2 additions & 3 deletions backend/gncitizen/core/sites/admin.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"""Flask-Admin views for site module"""

from flask_admin.contrib.sqla.view import ModelView
from markupsafe import Markup

from gncitizen.utils.admin import CustomJSONField, CustomTileView, json_formatter
from markupsafe import Markup

from .models import VisitModel

Expand All @@ -20,9 +19,9 @@ class SiteView(CustomTileView):
VisitModel,
]
column_formatters = {"visits": _visits_list_formatter}
# column_exclude_list = ["geom"]
column_list = [
"id_site",
"uuid_sinp",
"name",
"visits",
"site_type.type",
Expand Down
23 changes: 10 additions & 13 deletions backend/gncitizen/core/sites/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,17 @@
from flask_jwt_extended import jwt_required
from geoalchemy2.shape import from_shape
from geojson import FeatureCollection
from shapely.geometry import Point, asShape
from sqlalchemy import or_
from utils_flask_sqla.response import json_resp
from utils_flask_sqla_geo.generic import get_geojson_feature

from gncitizen.core.commons.models import MediaModel, ProgramsModel
from gncitizen.core.users.models import UserModel
from gncitizen.utils.env import admin
from gncitizen.utils.errors import GeonatureApiError
from gncitizen.utils.jwt import get_id_role_if_exists, get_user_if_exists
from gncitizen.utils.media import save_upload_files
from server import db
from shapely.geometry import Point, shape
from sqlalchemy import or_
from utils_flask_sqla.response import json_resp
from utils_flask_sqla_geo.generic import get_geojson_feature

from .admin import SiteView, VisitView
from .models import MediaOnVisitModel, SiteModel, SiteTypeModel, VisitModel
Expand Down Expand Up @@ -53,7 +52,7 @@ def get_types():
@json_resp
def get_site(pk):
"""Get a site by id
---
---asShape
tags:
- Sites (External module)
parameters:
Expand Down Expand Up @@ -319,8 +318,8 @@ def post_site():
raise GeonatureApiError(e)

try:
shape = asShape(request_data["geometry"])
newsite.geom = from_shape(Point(shape), srid=4326)
shape_geometry = shape(request_data["geometry"])
newsite.geom = from_shape(shape_geometry, srid=4326)
except Exception as e:
current_app.logger.debug(e)
raise GeonatureApiError(e)
Expand Down Expand Up @@ -365,8 +364,8 @@ def update_site():
for prop in ["name", "id_type"]:
update_site[prop] = update_data[prop]
try:
shape = asShape(update_data["geometry"])
update_site["geom"] = from_shape(Point(shape), srid=4326)
shape_geometry = shape(update_data["geometry"])
update_site["geom"] = from_shape(shape_geometry, srid=4326)
except Exception as e:
current_app.logger.warning("[update_site] coords ", e)
raise GeonatureApiError(e)
Expand Down Expand Up @@ -588,9 +587,7 @@ def export_sites_xls(user_id):
xls_file = io.BytesIO()
wb.save(xls_file)
output = make_response(xls_file.getvalue())
output.headers["Content-Disposition"] = (
"attachment; filename=" + "export_sites.xls"
)
output.headers["Content-Disposition"] = "attachment; filename=" + "export_sites.xls"
output.headers["Content-type"] = "application/xls"
return output
except Exception as e:
Expand Down
18 changes: 3 additions & 15 deletions backend/gncitizen/core/users/models.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#!/usr/bin/env python3

from flask import current_app
from gncitizen.core.commons.models import ProgramsModel, TimestampMixinModel, TModules
from passlib.hash import pbkdf2_sha256 as sha256
from server import db
from sqlalchemy import event
from sqlalchemy.ext.declarative import declared_attr
from utils_flask_sqla_geo.serializers import serializable

from gncitizen.core.commons.models import ProgramsModel, TimestampMixinModel, TModules
from server import db

logger = current_app.logger


Expand Down Expand Up @@ -116,24 +115,13 @@ def to_json(x):
def __repr__(self):
return f"{self.username} <{self.id_user}>"

# @classmethod
# def delete_all(cls):
# try:
# num_rows_deleted = db.session.query(cls).delete()
# db.session.commit()
# return {'message': '{} row(s) deleted'.format(num_rows_deleted)}
# except:
# return {'message': 'Something went wrong'}


@event.listens_for(UserModel.password, "set", retval=True)
def hash_user_password(_target, value, oldvalue, _initiator):
"""Evenement qui hash le mot de passe systèmatiquement"""
logger.debug(f"<hash_user_password> OLD PWD {oldvalue} / NEW PWD {value != ''}")
if value != "" and not sha256.identify(value):
logger.debug("<hash_user_password> Update new password")
return UserModel.generate_hash(value)
return value
return oldvalue


class GroupsModel(db.Model):
Expand Down
Loading