Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions backend/geonature/core/gn_meta/models/aframework.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ def filter_by_params(cls, params={}, *, _ds_search=True, query=None):
name = params.get("name")
date = params.get("date")
is_parent = params.get("is_parent")
opened = params.get("opened")
query = (
query.where(
TAcquisitionFramework.unique_acquisition_framework_id == uuid if uuid else True
Expand All @@ -284,6 +285,7 @@ def filter_by_params(cls, params={}, *, _ds_search=True, query=None):
)
.where(TAcquisitionFramework.acquisition_framework_start_date == date if date else True)
.where(TAcquisitionFramework.is_parent == is_parent if is_parent is not None else True)
.where(TAcquisitionFramework.opened == opened if opened is not None else True)
)

actors = []
Expand Down
1 change: 1 addition & 0 deletions backend/geonature/core/gn_meta/models/commons.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class Meta:
areas = ma.fields.List(ma.fields.Integer())
search = ma.fields.String()
is_parent = ma.fields.Boolean(allow_none=True)
opened = ma.fields.Boolean(allow_none=True)

@ma.post_load(pass_collection=False)
def convert_date(self, data, **kwargs):
Expand Down
28 changes: 26 additions & 2 deletions backend/geonature/core/gn_meta/schemas.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from sqlalchemy import inspect
from marshmallow import pre_load, post_dump, fields, EXCLUDE
from marshmallow import pre_load, post_dump, fields, EXCLUDE, validates_schema, ValidationError
from flask import g

from .models import (
Expand All @@ -9,7 +9,7 @@
CorDatasetActor,
TBibliographicReference,
)
from geonature.utils.env import MA
from geonature.utils.env import MA, db
from geonature.utils.schema import CruvedSchemaMixin
from geonature.core.gn_commons.models import TModules
from geonature.core.gn_commons.schemas import ModuleSchema
Expand Down Expand Up @@ -72,6 +72,30 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.mobile_app = kwargs.get("mobile_app", False)

@validates_schema
def validate_acquisition_framework_opened(self, data, **kwargs):
"""Check if the acquisition framework is opened before creating or updating active status on a dataset."""
af_id = data.get("id_acquisition_framework")

if not af_id and self.instance:
af = self.instance.acquisition_framework
else:
af = db.session.get(TAcquisitionFramework, af_id)
is_creation = self.instance is None or self.instance.id_dataset is None

# If we try to create a dataset on a closed acquisition framework, raise an error
if not af.opened and is_creation:
raise ValidationError(
"Can't create a dataset on a closed acquisition framework.",
field_name="id_acquisition_framework",
)

if not af.opened and data.get("active", self.instance.active) != self.instance.active:
raise ValidationError(
"Can't change the active status of a dataset if the acquisition framework is not opened.",
field_name="active",
)

@post_dump(pass_collection=False, pass_original=True)
def module_input(self, item, original, many, **kwargs):
if "modules" in item:
Expand Down
47 changes: 47 additions & 0 deletions backend/geonature/tests/test_gn_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,17 @@ def test_list_datasets_mobile(self, users, datasets, acquisition_frameworks):

assert set(response.json.keys()) == {"data"}

def get_test_dataset_json(self, id_acquisition_framework):
return {
"id_acquisition_framework": id_acquisition_framework,
"dataset_name": "test",
"dataset_shortname": "test",
"dataset_desc": "test",
"terrestrial_domain": True,
"marine_domain": False,
"unique_dataset_id": None,
}

def test_create_dataset(self, users, datasets):
response = self.client.post(url_for("gn_meta.create_dataset"))
assert response.status_code == Unauthorized.code
Expand All @@ -820,6 +831,42 @@ def test_create_dataset(self, users, datasets):
ds["id_dataset"] = "takeonme"
response = self.client.post(url_for("gn_meta.create_dataset"), json=ds)
assert response.status_code == BadRequest.code
ds_json = self.get_test_dataset_json(datasets["own_dataset"].id_acquisition_framework)
response = self.client.post(
url_for("gn_meta.create_dataset"),
json=ds_json,
)
assert response.status_code == 200

def test_dataset_with_closed_af(self, users, datasets):
set_logged_user(self.client, users["admin_user"])
datasets["own_dataset"].acquisition_framework.opened = False
db.session.flush()
ds_json = self.get_test_dataset_json(datasets["own_dataset"].id_acquisition_framework)
response = self.client.post(
url_for("gn_meta.create_dataset"),
json=ds_json,
)
assert response.status_code == 400
# We check if error is linked to the acquisition framework
assert response.json["description"].get("id_acquisition_framework")

# Now post the dataset with af opened, close it and try to update its active status
datasets["own_dataset"].acquisition_framework.opened = True
db.session.flush()
ds_json["active"] = False
response = self.client.post(url_for("gn_meta.create_dataset"), json=ds_json)
assert response.status_code == 200
id_dataset = response.json["id_dataset"]

datasets["own_dataset"].acquisition_framework.opened = False
db.session.flush()
ds_json["active"] = True
response = self.client.post(
url_for("gn_meta.update_dataset", id_dataset=id_dataset), json=ds_json
)
assert response.status_code == 400
assert response.json["description"].get("active")

def test_get_dataset(self, users, datasets):
ds = datasets["own_dataset"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,37 @@ <h3 class="main-color">{{ 'Dataset' | translate }}</h3>
<div class="card-body">
<div>
<small>{{ 'AcquisitionFramework' | translate }}</small>
<ng-container *ngIf="acquisitionFrameworks | async as frameworks">
<div
*ngIf="!frameworks"
class="d-flex justify-content-center"
>
<mat-spinner diameter="20"></mat-spinner>
</div>

<div
*ngIf="!(acquisitionFrameworks | async)"
class="d-flex justify-content-center"
>
<mat-spinner diameter="20"></mat-spinner>
</div>

<ng-select
*ngIf="acquisitionFrameworks | async as frameworks"
[formControl]="form.get('id_acquisition_framework')"
[items]="frameworks"
bindLabel="acquisition_framework_name"
bindValue="id_acquisition_framework"
[disabled]="true"
data-qa="pnx-dataset-form-select-cadre-acq"
>
<ng-template
ng-option-tmp
let-item="item"
let-index="index"
let-search="searchTerm"
<ng-select
*ngIf="frameworks"
[formControl]="form.get('id_acquisition_framework')"
[items]="frameworks"
bindLabel="acquisition_framework_name"
bindValue="id_acquisition_framework"
data-qa="pnx-dataset-form-select-cadre-acq"
>
<span
class="pre-wrap"
[attr.data-qa]="'pnx-metadata-jdd-' + item.acquisition_framework_name"
<ng-template
ng-option-tmp
let-item="item"
let-index="index"
let-search="searchTerm"
>
{{ item.acquisition_framework_name }}
<span *ngIf="!item.opened">[Cloturé]</span>
</span>
</ng-template>
</ng-select>
<span
class="pre-wrap"
[attr.data-qa]="'pnx-metadata-jdd-' + item.acquisition_framework_name"
>
{{ item.acquisition_framework_name }}
</span>
</ng-template>
</ng-select>
</ng-container>
</div>

<div>
Expand Down
38 changes: 30 additions & 8 deletions frontend/src/app/metadataModule/datasets/dataset-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class DatasetFormComponent implements OnInit {
public taxaBibList: number;
public uuidEditionEnabled: boolean = true;
public entityLabel: string;
public isAcquisitionFrameworkOpened: boolean = true;

constructor(
private _route: ActivatedRoute,
Expand All @@ -47,22 +48,43 @@ export class DatasetFormComponent implements OnInit {
this._route.params
.pipe(
switchMap((params) => {
return params['id'] ? this._dfs.getDataset(params['id']) : of(null);
if (params['id']) {
// Update
return this._dfs.getDataset(params['id']);
}
// Creation
return of(null);
})
)
.subscribe((dataset) => this.datasetFormS.dataset.next(dataset));

.subscribe((dataset) => {
if (dataset) {
this.datasetFormS.dataset.next(dataset);
this.updateFormControlsState(dataset);
}
if (dataset?.acquisition_framework?.opened === false) {
// If AF closed, we only get the AF of the current dataset
this.acquisitionFrameworks = of([dataset.acquisition_framework]);
} else {
// If AF opened or it is a creation, we get the list of AF
this.acquisitionFrameworks = this._dfs
.getAcquisitionFrameworksList({ opened: true }, {}, 1, -1)
.pipe(map((response) => response.items));
}
});
this.form = this.datasetFormS.form;

this._dfs.getTaxaBibList().subscribe((d) => (this.taxaBibList = d));

this.acquisitionFrameworks = this._dfs
.getAcquisitionFrameworksList({}, {}, 1, -1)
.pipe(map((response) => response.items));
this.uuidEditionEnabled = this._config.METADATA.ENABLE_UUID_EDITION_FIELD;
this.entityLabel = this.translation_service.instant('Dataset');
}

updateFormControlsState(dataset: any): void {
// if the af is closed, we disable the acquisition_framework selection and active fields
if (dataset && dataset.acquisition_framework?.opened === false) {
this.form.get('active')?.disable();
this.form.get('id_acquisition_framework')?.disable();
}
}

genericActorFormSubmit(result) {
if (result) {
// TODO
Expand Down
Loading