Skip to content

Commit

Permalink
feat(import): dynamic form on default values
Browse files Browse the repository at this point in the history
  • Loading branch information
20cents committed Dec 11, 2024
1 parent 26b1f4f commit ce364f3
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 75 deletions.
1 change: 1 addition & 0 deletions backend/geonature/core/imports/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ class BibFields(db.Model):
fr_label = db.Column(db.Unicode, nullable=False)
eng_label = db.Column(db.Unicode, nullable=True)
type_field = db.Column(db.Unicode, nullable=True)
type_field_params = db.Column(MutableDict.as_mutable(JSON))
mandatory = db.Column(db.Boolean, nullable=False)
autogenerated = db.Column(db.Boolean, nullable=False)
mnemonique = db.Column(db.Unicode, db.ForeignKey(BibNomenclaturesTypes.mnemonique))
Expand Down
2 changes: 2 additions & 0 deletions backend/geonature/core/imports/routes/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ def get_fields(scope, destination):
fields=[
"id_field",
"name_field",
"type_field",
"type_field_params",
"fr_label",
"eng_label",
"mnemonique",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""bib_field.type_field conforms to dynamic_form.type_widget
Revision ID: a94bea44ab56
Revises: e43b01a18850
Create Date: 2024-12-11 15:44:52.912515
"""

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "a94bea44ab56"
down_revision = "e43b01a18850"
branch_labels = None
depends_on = None


def upgrade():
op.execute(
"""
ALTER TABLE gn_imports.bib_fields ADD type_field_params jsonb NULL;
"""
)
op.execute(
"""
UPDATE gn_imports.bib_fields
SET type_field =
case
-- mnemonique is handled front side
WHEN mnemonique IS NOT NULL AND mnemonique != '' THEN NULL
-- multi is handled front side
WHEN multi = true THEN null
WHEN type_field IN ('integer', 'real') THEN 'number'
WHEN type_field IN ('geometry', 'jsonb', 'json', 'wkt') THEN 'textarea'
WHEN type_field LIKE 'timestamp%' THEN 'date'
WHEN type_field ~ '^character varying\((\d+)\)$'
AND COALESCE(substring(type_field FROM '\d+')::int, 0) > 68 THEN 'textarea'
-- Default: garder la valeur actuelle.
ELSE NULL
END;
"""
)


def downgrade():
op.execute(
"""
ALTER TABLE gn_imports.bib_fields DROP COLUMN type_field_params;
"""
)
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@
[cdNomenclatures]="formDefComp['cd_nomenclatures']"
[regne]="formDefComp['regne']"
[group2Inpn]="formDefComp['group2Inpn']"
[bindAllItem]="formDefComp['bind_all_item']"
></pnx-nomenclature>

<pnx-taxonomy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ export class TaxonomyComponent implements OnInit, OnChanges {
if (!this.apiEndPoint) {
this.setApiEndPoint(this.idList);
}
// Use "!= null" instead of "!== null" to also match undefined values.
this.parentFormControl.valueChanges
.pipe(filter((value) => value !== null && value.length === 0))
.pipe(filter((value) => value != null && value.length === 0))
.subscribe((value) => {
this.onDelete.emit();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { ImportDataService } from '../../../services/data.service';
import { FieldMappingService } from '@geonature/modules/imports/services/mappings/field-mapping.service';
import { FieldMappingModalComponent } from './field-mapping-modal/field-mapping-modal.component';
import { Cruved, toBooleanCruved } from '@geonature/modules/imports/models/cruved.model';
import { Step } from '@geonature/modules/imports/models/enums.model';
import { ActivatedRoute } from '@angular/router';
import { ImportProcessService } from '../import-process.service';
import { CruvedStoreService } from '@geonature_common/service/cruved-store.service';
import { concatMap, finalize, first, flatMap, skip, take } from 'rxjs/operators';
import { Observable, Subscription, of } from 'rxjs';
import { concatMap, flatMap, skip, take } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Import } from '@geonature/modules/imports/models/import.model';
import {
Expand All @@ -33,6 +32,7 @@ export class FieldsMappingStepComponent implements OnInit {
public updateAvailable: boolean = false;
public step: Step;
public modalCreateMappingForm = new FormControl('');
public defaultValueFormDefs: any = {};

constructor(
public _fieldMappingService: FieldMappingService,
Expand All @@ -53,6 +53,7 @@ export class FieldsMappingStepComponent implements OnInit {
if (!fieldMappings) return;
this._fieldMappingService.parseData({ fieldMappings, targetFields, sourceFields });
this.targetFields = this._fieldMappingService.getTargetFieldsData();
this.defaultValueFormDefs = this._fieldMappingService.getDefaultValueFormDefs();
this.sourceFields = this._fieldMappingService.getSourceFieldsData();
this._fieldMappingService.initForm();
this._fieldMappingService.populateMappingForm();
Expand Down Expand Up @@ -115,21 +116,23 @@ export class FieldsMappingStepComponent implements OnInit {

getFieldMappingValues(): FieldMappingValues {
const values: FieldMappingValues = {};
this._fieldMappingService.flattenTargetFieldData(this.targetFields).forEach((field) => {
const column_src = this._fieldMappingService.mappingFormGroup.get(field.name_field)?.value;
const default_value = this._fieldMappingService.mappingFormGroup.get(
`${field.name_field}_default_value`
)?.value;
if (column_src || default_value) {
values[field.name_field] = {
column_src: column_src || undefined,
// Using the nomenclature's label instead of the ID allows us to avoid modifying the content mapping step.
default_value: default_value?.id_nomenclature
? default_value.label_default
: (default_value ?? undefined),
};
}
});
this._fieldMappingService
.flattenTargetFieldData(this.targetFields)
.forEach(({ name_field }) => {
const column_src = this._fieldMappingService.mappingFormGroup.get(name_field)?.value;
const default_value = this._fieldMappingService.mappingFormGroup.get(
`${name_field}_default_value`
)?.value;
if (column_src || default_value) {
values[name_field] = {
column_src: column_src || undefined,
default_value: this._fieldMappingService.getFieldDefaultValue(
name_field,
default_value
),
};
}
});
return values;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,41 +87,11 @@
{{ _fm.mappingFormGroup.controls[field.name_field].getError('conflict') }}
</div>

<div>
<small>Valeur par défaut :</small>
<ng-container [ngSwitch]="getDefaultValueWidget(field)">
<pnx-nomenclature
*ngSwitchCase="'nomenclature'"
[bindAllItem]="true"
[codeNomenclatureType]="field.mnemonique"
[parentFormControl]="
_fm.mappingFormGroup.get(field.name_field + '_default_value')
"
></pnx-nomenclature>
<textarea
*ngSwitchCase="'textarea'"
rows="4"
class="form-control form-control-sm"
formControlName="{{ field.name_field }}_default_value"
></textarea>
<input
*ngSwitchDefault
type="text"
class="form-control form-control-sm"
formControlName="{{ field.name_field }}_default_value"
/>
</ng-container>
<div
*ngIf="
_fm.mappingFormGroup.controls[field.name_field + '_default_value'].hasError(
'invalidJSON'
)
"
class="invalid-feedback d-block"
>
JSON non valide
</div>
</div>
<pnx-dynamic-form
class="dynamic-form padding-sm"
[formDef]="defaultValueFormDefs[field.name_field]"
[form]="_fm.mappingFormGroup"
></pnx-dynamic-form>
</div>
<div
*ngIf="field.autogenerated"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Field } from '@geonature/modules/imports/models/mapping.model';
import { FieldMappingService } from '@geonature/modules/imports/services/mappings/field-mapping.service';

@Component({
Expand All @@ -12,9 +10,13 @@ export class MappingThemeComponent implements OnInit {
@Input() themeData;
@Input() sourceFields: Array<string>;

defaultValueFormDefs: any = {};

constructor(public _fm: FieldMappingService) {}

ngOnInit() {}
ngOnInit() {
this.defaultValueFormDefs = this._fm.getDefaultValueFormDefs();
}

isMapped(keySource: string) {
return this._fm.checkTargetFieldStatus('mapped', keySource);
Expand All @@ -31,14 +33,4 @@ export class MappingThemeComponent implements OnInit {
return this.themeData.fields.find((field) => field.name_field === label)?.fr_label;
});
}

public getDefaultValueWidget(field: any) {
if (field.mnemonique) {
return 'nomenclature';
} else if (field.multi) {
return 'textarea';
}

return 'text';
}
}
11 changes: 11 additions & 0 deletions frontend/src/app/modules/imports/models/mapping.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ interface Mapping {
cruved: Cruved;
}

export interface FormDef {
type_widget: string;
[propName: string]: any;
}

export interface Field {
id_field: number;
autogenerated: boolean;
Expand All @@ -21,6 +26,12 @@ export interface Field {
multi: boolean;
name_field: string;
mapping_condition: string;
entity: any;
type_field: string;
type_field_params: any;
mnemonique: string;
mandatory_conditions: string[];
optional_conditions: string[];
}

export interface FieldMappingValues {
Expand Down
Loading

0 comments on commit ce364f3

Please sign in to comment.