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
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
10 changes: 3 additions & 7 deletions frontend/src/app/metadataModule/metadata-dataset.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
<td>
<span class="float-left">
<!-- Button to switch dataset active/inactive -->
<ng-container *ngIf="dataset.cruved.U; else noSwitchButton">
<ng-container *ngIf="isSwitchable(); else noSwitchButton">
<mat-slide-toggle
class="custom-toggle"
*ngIf="!stateChangeSaving"
[checked]="dataset.active"
[matTooltip]="
dataset.active ? 'Le jeu de données est actif' : 'Le jeu de données est inactif'
"
[matTooltip]="getSwitchTooltip()"
(change)="switchDatasetState($event)"
[attr.data-qa]="'pnx-metadata-jdd-actif-' + dataset.unique_dataset_id"
></mat-slide-toggle>
Expand All @@ -26,9 +24,7 @@
[checked]="dataset.active"
[disabled]="true"
color="primary"
[matTooltip]="
dataset.active ? 'Le jeu de données est actif' : 'Le jeu de données est inactif'
"
[matTooltip]="getSwitchTooltip()"
[attr.data-qa]="'pnx-metadata-jdd-inactif-' + dataset.unique_dataset_id"
></mat-slide-toggle>
</ng-template>
Expand Down
35 changes: 34 additions & 1 deletion frontend/src/app/metadataModule/metadata-dataset.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ConfirmationDialog } from '@geonature_common/others/modal-confirmation/
import { MetadataService } from './services/metadata.service';
import { MetadataDataService } from './services/metadata-data.service';
import { CommonService } from '@geonature_common/service/common.service';
import { ActionService } from '@geonature/services/action.service';

@Component({
selector: '[pnx-metadata-dataset]',
Expand All @@ -18,6 +19,7 @@ import { CommonService } from '@geonature_common/service/common.service';
})
export class MetadataDatasetComponent implements OnInit {
@Input() dataset: any;
@Input() acquisitionFramework: any;
stateChangeSaving: boolean = false;

constructor(
Expand All @@ -27,11 +29,42 @@ export class MetadataDatasetComponent implements OnInit {
public moduleService: ModuleService,
public metadataS: MetadataService,
public metadataDataS: MetadataDataService,
private _commonService: CommonService
private _commonService: CommonService,
private actionService: ActionService
) {}

ngOnInit() {}

getSwitchTooltip() {
if (
!this.actionService.isActionAllowed(
this.dataset.cruved,
this.acquisitionFramework.opened,
'U'
)
) {
return this.actionService.getActionTooltip(
this.dataset.cruved,
this.acquisitionFramework.opened,
'U',
'MetaData'
);
}
if (this.dataset.active) {
return this.translate.instant('MetaData.Tooltips.DatasetActive');
} else {
return this.translate.instant('MetaData.Tooltips.DatasetInactive');
}
}

isSwitchable() {
return this.actionService.isActionAllowed(
this.dataset.cruved,
this.acquisitionFramework.opened,
'U'
);
}

switchDatasetState(event) {
this.stateChangeSaving = true;
this.metadataDataS
Expand Down
1 change: 1 addition & 0 deletions frontend/src/app/metadataModule/metadata.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ <h3 class="main-color">{{ 'MetaData.Catalog' | translate }}</h3>
[attr.data-qa]="'pnx-metadata-dataset-name-' + dataset.dataset_name"
pnx-metadata-dataset
[dataset]="dataset"
[acquisitionFramework]="af"
></tr>
</tbody>
</table>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@
"SeeAFSheet": "View the acquisition framework sheet",
"FillOwnUUID": "I indicate my own SINP UUID"
},
"Tooltips": {
"DatasetActive": "The dataset is active",
"DatasetInactive": "The dataset is inactive"
},
"UUIDField": "UUID ID",
"AdvancedSearch": "Advanced search",
"AFAndDatasetsList": "List of acquisition frameworks and associated datasets",
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@
"SeeAFSheet": "Voir la fiche du cadre d'acquisition",
"FillOwnUUID": "Saisie manuelle de l'UUID"
},
"Tooltips": {
"DatasetActive": "Le jeu de données est actif",
"DatasetInactive": "Le jeu de données est inactif"
},
"UUIDField": "UUID du {{ entity }}",
"AdvancedSearch": "Recherche avancée",
"AFAndDatasetsList": "Liste des cadres d'acquisition et des jeux de données associés",
Expand Down
Loading