Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions backend/gn_module_monitoring/monitoring/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,14 +365,23 @@ class Meta:
model = TMonitoringIndividuals
include_fk = True
load_relationships = True
load_instance = True

medias = MA.Nested(MediaSchema, many=True)
id_individual = auto_field(required=False, allow_none=True)
uuid_individual = fields.String(required=False, allow_none=True)

pk = fields.Method("set_pk", dump_only=True)

def set_pk(self, obj):
return "id_individual"

@pre_load
def normalize(self, data, **kwargs):
data["medias"] = data.get("medias") or []

return data


class MonitoringIndividualsSchemaCruved(MonitoringCruvedSchemaMixin, MonitoringIndividualsSchema):
pass
73 changes: 72 additions & 1 deletion backend/gn_module_monitoring/routes/individuals.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from flask import g, request
from gn_module_monitoring.config.utils import get_specific_properties
from gn_module_monitoring.config.utils import get_module, get_specific_properties
from marshmallow import EXCLUDE
from sqlalchemy import select
from werkzeug.datastructures import MultiDict
from werkzeug.exceptions import Forbidden

from geonature.utils.env import db

from geonature.core.gn_permissions import decorators as permissions
from geonature.core.gn_permissions.decorators import check_cruved_scope
from gn_module_monitoring import MODULE_CODE
from gn_module_monitoring.blueprint import blueprint
Expand All @@ -23,10 +25,14 @@
get_sort,
paginate,
paginate_scope,
process_json_data_for_db_upsert,
sort,
sort_according_to_column_type_for_site,
)

default_route_object_type = "individual"
OBJECT_CODE = "MONITORINGS_INDIVIDUALS"


@blueprint.route("/refacto/individuals", methods=["GET"], defaults={"object_type": "individual"})
@blueprint.route(
Expand Down Expand Up @@ -73,6 +79,51 @@ def get_individuals(object_type, module_code=None):
)


@blueprint.route(
"/individuals/<string:module_code>/<int:id>",
methods=["GET"],
defaults={"object_type": default_route_object_type},
)
@permissions.check_cruved_scope("R", get_scope=True, object_code=OBJECT_CODE)
def get_individual_by_id(scope: int, module_code: str, id: int, object_type: str):
individual = db.get_or_404(TMonitoringIndividuals, id)
if not individual.has_instance_permission(scope=scope):
raise Forbidden(f"User {g.current_user} cannot read individual {individual.id_individual}")
data = MonitoringIndividualsSchema().dump(individual)

return data


@blueprint.route(
"/<string:module_code>/individuals",
methods=["POST"],
defaults={"object_type": default_route_object_type},
)
@permissions.check_cruved_scope("C", object_code=OBJECT_CODE)
def post_individual(object_type: str, module_code: str):
post_data = dict(request.get_json())
return create_or_update_individual(post_data, module_code=module_code)


@blueprint.route(
"/<string:module_code>/individuals/<int:_id>",
methods=["PATCH"],
defaults={"object_type": default_route_object_type},
)
@permissions.check_cruved_scope("U", get_scope=True, object_code=OBJECT_CODE)
def patch_individual(scope, object_type: str, module_code: str, _id: int):
individual = db.get_or_404(TMonitoringIndividuals, _id)
if not individual.has_instance_permission(scope=scope):
raise Forbidden(
f"User {g.current_user} cannot update individual {individual.id_individual}"
)
post_data = dict(request.get_json())
print(post_data)
if not "id_individual" in post_data:
post_data["id_individual"] = _id
return create_or_update_individual(post_data, module_code=module_code)


@blueprint.route(
"/individuals/<int:_id>", methods=["DELETE"], defaults={"object_type": "individual"}
)
Expand All @@ -86,3 +137,23 @@ def delete_individual(scope, _id: int, object_type: str):
db.session.delete(individual)
db.session.commit()
return {"success": "Item is successfully deleted"}, 200


def create_or_update_individual(post_data: dict, module_code: str = "generic"):
"""
Create or update an individual.

:param post_data: dict containing data to create or update an individual
:param module_code: str, module code, default is "generic"
:return: dict, serialized individual
"""
config = get_config(module_code, force=True)
process_data = process_json_data_for_db_upsert(config, post_data, default_route_object_type)
try:
individual = MonitoringIndividualsSchema(unknown=EXCLUDE).load(process_data)
except Exception as e:
raise Exception(f"create_or_update_individual : {str(e)}")
db.session.add(individual)
db.session.commit()

return MonitoringIndividualsSchema().dump(individual)
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ def test_monitoring_individuals_schema_deserialization(self, users):
deserialized_data = schema.load(input_data)

# Vérifications
assert deserialized_data["individual_name"] == input_data["individual_name"]
assert deserialized_data["cd_nom"] == input_data["cd_nom"]
assert deserialized_data["active"] == input_data["active"]
assert deserialized_data["id_digitiser"] == input_data["id_digitiser"]
assert deserialized_data.individual_name == input_data["individual_name"]
assert deserialized_data.cd_nom == input_data["cd_nom"]
assert deserialized_data.active == input_data["active"]
assert deserialized_data.id_digitiser == input_data["id_digitiser"]

def test_monitoring_individuals_schema_validation(self):
# Données d'entrée invalides
Expand Down
128 changes: 128 additions & 0 deletions backend/gn_module_monitoring/tests/test_routes/test_individuals.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

from pypnusershub.tests.utils import set_logged_user_cookie

from geonature.utils.env import db
from gn_module_monitoring.monitoring.models import TMonitoringIndividuals


@pytest.mark.usefixtures("client_class")
class TestIndividuals:
Expand Down Expand Up @@ -83,3 +86,128 @@ def test_get_individuals_scope_2_organism(self, install_module_test_indi, users)
response = self.client.get(url_for("monitorings.get_individuals", module_code="test_indi"))
assert response.status_code == 200
assert len(response.json["items"]) == 4

def test_get_individual_by_id(self, install_module_test_indi, users):
set_logged_user_cookie(self.client, users["admin_user"])
individual_instance = install_module_test_indi[0]

r = self.client.get(
url_for(
"monitorings.get_individual_by_id",
module_code="test_indi",
id=individual_instance.id_individual,
)
)

assert r.status_code == 200
assert r.json["id_individual"] == individual_instance.id_individual
assert r.json["individual_name"] == individual_instance.individual_name
assert r.json["cd_nom"] == individual_instance.cd_nom

def test_get_individual_by_id_not_found(self, users):
set_logged_user_cookie(self.client, users["admin_user"])

r = self.client.get(
url_for(
"monitorings.get_individual_by_id",
module_code="test_indi",
id=999999999,
)
)

assert r.status_code == 404

def test_post_individual(self, install_module_test_indi, users):
set_logged_user_cookie(self.client, users["admin_user"])

data = {
"individual_name": "new_individual",
"cd_nom": 79273,
"active": True,
"comment": "new_comment",
"id_digitiser": users["admin_user"].id_role,
}

r = self.client.post(
url_for(
"monitorings.post_individual",
module_code="test_indi",
),
json=data,
)
assert r.status_code == 200
assert r.json["individual_name"] == data["individual_name"]
assert r.json["cd_nom"] == data["cd_nom"]
assert r.json["comment"] == data["comment"]

def test_patch_individual(self, install_module_test_indi, users):
set_logged_user_cookie(self.client, users["admin_user"])
individual_instance = install_module_test_indi[0]

data = {
"id_individual": individual_instance.id_individual,
"individual_name": "updated_individual",
"cd_nom": individual_instance.cd_nom,
"id_digitiser": individual_instance.id_digitiser,
"comment": "updated_comment",
}

r = self.client.patch(
url_for(
"monitorings.patch_individual",
module_code="test_indi",
_id=individual_instance.id_individual,
),
json=data,
)
assert r.status_code == 200
assert r.json["id_individual"] == individual_instance.id_individual
assert r.json["individual_name"] == data["individual_name"]
assert r.json["comment"] == data["comment"]

def test_patch_individual_not_found(self, install_module_test_indi, users):
set_logged_user_cookie(self.client, users["admin_user"])

r = self.client.patch(
url_for(
"monitorings.patch_individual",
module_code="test_indi",
_id=999999999,
),
json={},
)

assert r.status_code == 404

def test_delete_individual(self, install_module_test_indi, users):
set_logged_user_cookie(self.client, users["admin_user"])
individual_instance = install_module_test_indi[-1]

r = self.client.delete(
url_for(
"monitorings.delete_individual",
_id=individual_instance.id_individual,
module_code="test_indi",
)
)

assert r.status_code == 200
assert r.json == {"success": "Item is successfully deleted"}

r = self.client.get(
url_for(
"monitorings.get_individual_by_id",
module_code="test_indi",
id=individual_instance.id_individual,
)
)
assert r.status_code == 404

def test_delete_individual_not_found(self, users):
set_logged_user_cookie(self.client, users["admin_user"])

r = self.client.delete(
url_for("monitorings.delete_individual", _id=999999999, module_code="test_indi")
)

assert r.status_code == 404
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.obj-title {
font-size: 1.75rem;
padding: 5px 15px;
}

:host ::ng-deep .obj-description {
font-size: 2rem;
font-weight: bold;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div>
<span class="obj-title">{{ 'Actions.Add' | translate }}</span>
</div>
<div>
<pnx-monitoring-form-g
[object]="individual"
objectType="individual"
[form]="form"
[apiService]="_individualsService"
[config]="moduleConfig['individual']"
[currentUser]="currentUser"
></pnx-monitoring-form-g>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { AuthService, User } from '@geonature/components/auth/auth.service';

import { ActivatedRoute } from '@angular/router';
import { FormService } from '../../services/form.service';
import { IndividualsService } from '../../services/api-geom.service';
import { ObjectService } from '../../services/object.service';
import { ConfigServiceG } from '../../services/config-g.service';
import { IIndividual } from '../../interfaces/individual';

@Component({
selector: 'monitoring-individuals-create',
templateUrl: './monitoring-individuals-create.component.html',
styleUrls: ['./monitoring-individuals-create.component.css'],
})
export class MonitoringIndividualsCreateComponent implements OnInit, OnDestroy {
currentUser: User;

public individual: IIndividual;

public moduleConfig;
public moduleCode;
public form: FormGroup;

constructor(
private _auth: AuthService,
private _formService: FormService,
private _formBuilder: FormBuilder,
private _objService: ObjectService,
public _individualsService: IndividualsService,
private _configServiceG: ConfigServiceG,
private _route: ActivatedRoute
) {}

ngOnInit() {
// Initialisation des variables
this.moduleConfig = this._configServiceG.config();
this.moduleCode = this._configServiceG.moduleCode();
this.form = this._formBuilder.group({});
this.currentUser = this._auth.getCurrentUser();
this._individualsService.initConfig();
this.individual = {} as IIndividual;

// breadcrumb
const queryParams = this._route.snapshot.queryParams;
this._objService.loadBreadCrumb(this.moduleCode, 'individual', null, queryParams);

// Passage en mode édition
this._formService.changeCurrentEditMode(true);
}

ngOnDestroy() {
this._formService.changeCurrentEditMode(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.obj-title {
font-size: 1.75rem;
padding: 5px 15px;
}

:host ::ng-deep .obj-description {
font-size: 2rem;
font-weight: bold;
}
Loading
Loading