Coupled paraterized dictionary

This commit is contained in:
2023-02-06 17:44:03 +01:00
parent d12e35cc10
commit 920bb39ca1
8 changed files with 188 additions and 18 deletions

View File

@@ -40,7 +40,8 @@ class ProvisionTemplateReference(BaseModel):
"schema": "ProvisionTemplate", "schema": "ProvisionTemplate",
"displayedFields": ['title', 'body'] "displayedFields": ['title', 'body']
}, },
} },
props={"parametrized": True}
) )
@@ -56,6 +57,9 @@ class ContractTemplate(CrudDocument):
default=[], default=[],
props={"items-per-row": "1", "numbered": True} props={"items-per-row": "1", "numbered": True}
) )
variables: List[DictionaryEntry] = Field(default=[], format="dictionary") variables: List[DictionaryEntry] = Field(
default=[],
format="dictionary",
)

View File

@@ -1,7 +1,7 @@
from pydantic import BaseModel from pydantic import BaseModel, Field
from typing import List from typing import List
from .models import ContractTemplate, ProvisionTemplate, PartyTemplate, ProvisionTemplateReference from .models import ContractTemplate, ProvisionTemplate, PartyTemplate, ProvisionTemplateReference, DictionaryEntry
from ..core.schemas import Writer from ..core.schemas import Writer
from ..core.models import text_area from ..core.models import text_area
@@ -17,6 +17,11 @@ class ContractTemplateCreate(Writer):
class ContractTemplateUpdate(BaseModel): class ContractTemplateUpdate(BaseModel):
name: str name: str
parties: List[PartyTemplate] = [] parties: List[PartyTemplate] = []
variables: List[DictionaryEntry] = Field(
default=[],
format="dictionary",
props={"required": False}
)
provisions: List[ProvisionTemplateReference] = [] provisions: List[ProvisionTemplateReference] = []

View File

@@ -19,6 +19,7 @@ export class CardComponent implements OnInit {
@Input() is_modal: Boolean = false; @Input() is_modal: Boolean = false;
@Output() resourceCreated: EventEmitter<string> = new EventEmitter(); @Output() resourceCreated: EventEmitter<string> = new EventEmitter();
@Output() resourceUpdated: EventEmitter<string> = new EventEmitter();
@Output() resourceDeleted: EventEmitter<string> = new EventEmitter(); @Output() resourceDeleted: EventEmitter<string> = new EventEmitter();
@@ -81,6 +82,7 @@ export class CardComponent implements OnInit {
this.crudService.update(this.resource!, model).subscribe((model: any) => { this.crudService.update(this.resource!, model).subscribe((model: any) => {
this.model = model; this.model = model;
this._modelLoading$.next(false); this._modelLoading$.next(false);
this.resourceUpdated.emit(model._id)
}); });
} }
} }

View File

@@ -45,6 +45,8 @@ export class CrudFormlyJsonschemaOptions implements FormlyJsonschemaOptions {
field.type = "date"; field.type = "date";
} else if (schema.hasOwnProperty('enum') && schema.enum.length == 1 && schema.enum[0] == schema.default ) { } else if (schema.hasOwnProperty('enum') && schema.enum.length == 1 && schema.enum[0] == schema.default ) {
field.type = "hidden"; field.type = "hidden";
} else if (schema.type == "array" && schema.format == "dictionary") {
field.type = "dictionary";
} }
if (schema.hasOwnProperty('props')) { if (schema.hasOwnProperty('props')) {

View File

@@ -20,6 +20,8 @@ import { JsonschemasService } from "./jsonschemas.service";
import { MultiSchemaTypeComponent } from "./types/multischema.type"; import { MultiSchemaTypeComponent } from "./types/multischema.type";
import { ForeignkeyTypeComponent } from "./types/foreignkey.type"; import { ForeignkeyTypeComponent } from "./types/foreignkey.type";
import {HiddenTypeComponent} from "./types/hidden.type"; import {HiddenTypeComponent} from "./types/hidden.type";
import {DictionaryTypeComponent} from "./types/dictionary.type";
import {DictionaryService} from "./types/dictionary.service";
@NgModule({ @NgModule({
@@ -33,8 +35,15 @@ import {HiddenTypeComponent} from "./types/hidden.type";
MultiSchemaTypeComponent, MultiSchemaTypeComponent,
ForeignkeyTypeComponent, ForeignkeyTypeComponent,
HiddenTypeComponent, HiddenTypeComponent,
DictionaryTypeComponent
],
providers: [
JsonschemasService,
ApiService,
CrudService,
CrudFormlyJsonschemaService,
DictionaryService
], ],
providers: [ JsonschemasService, ApiService, CrudService, CrudFormlyJsonschemaService ],
imports: [ imports: [
CommonModule, CommonModule,
HttpClientModule, HttpClientModule,
@@ -51,6 +60,7 @@ import {HiddenTypeComponent} from "./types/hidden.type";
{ name: 'multischema', component: MultiSchemaTypeComponent }, { name: 'multischema', component: MultiSchemaTypeComponent },
{ name: 'foreign-key', component: ForeignkeyTypeComponent }, { name: 'foreign-key', component: ForeignkeyTypeComponent },
{ name: 'hidden', component: HiddenTypeComponent }, { name: 'hidden', component: HiddenTypeComponent },
{ name: 'dictionary', component: DictionaryTypeComponent },
] ]
}), }),
FormlyBootstrapModule FormlyBootstrapModule

View File

@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import {Observable, Subject} from "rxjs";
@Injectable()
export class DictionaryService {
fieldsParameters: {[field_id: string]: string[]} = {}
parameters: string[] = []
parameters$: Subject<string[]> = new Subject<string[]>()
updateParameters(field_id: string, newParameters: string[]) {
this.fieldsParameters[field_id] = newParameters;
this.parameters = [].concat();
for (const field of Object.values(this.fieldsParameters)) {
this.parameters = this.parameters.concat(field);
}
this.parameters = this.parameters.filter(
(item,index) => this.parameters.indexOf(item) === index
);
this.parameters$.next(this.parameters)
}
}

View File

@@ -0,0 +1,97 @@
import { Component, OnInit } from '@angular/core';
import {FieldType, FieldTypeConfig, FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core';
import {FormControl, FormGroup, FormArray} from "@angular/forms";
import {DictionaryService} from "./dictionary.service";
import {JSONSchema7} from "json-schema";
import {FormlyJsonschema} from "@ngx-formly/core/json-schema";
@Component({
selector: 'formly-dictionary-type',
template: `
<div class="mb-3">
<ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-0">
<ngb-panel>
<ng-template ngbPanelTitle>
<label *ngIf="props.label && props['hideLabel'] !== true" [attr.for]="id"
class="form-label">{{ props.label }}
<span *ngIf="props.required && props['hideRequiredMarker'] !== true" aria-hidden="true">*</span>
</label>
</ng-template>
<ng-template ngbPanelContent>
<div class="alert alert-danger" role="alert" *ngIf="showError && formControl.errors">
<formly-validation-message [field]="field"></formly-validation-message>
</div>
<formly-form [model]="parameterModel" [fields]="parameterFields" [form]="parameterForm"></formly-form>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div>
`,
})
export class DictionaryTypeComponent extends FieldType<FieldTypeConfig> implements OnInit
{
public errorMsg: string = "";
parameterForm: FormGroup = new FormGroup({});
parameterList: string[] = [];
parameterModel: {[field_id: string]: string} = {};
parameterFields: FormlyFieldConfig[] = [];
constructor(protected service: DictionaryService,
private formlyJsonschema: FormlyJsonschema) {
super();
}
ngOnInit() {
for (const entry of this.formControl.value) {
this.parameterModel[entry.key] = entry.value;
}
this.service.parameters$.subscribe((parameters) => {
this.parameterList = parameters;
for (const pname of parameters) {
if (Object.keys(this.parameterModel).indexOf(pname) == -1) {
this.parameterModel[pname] = "";
}
}
this.parameterFields = [this.formlyJsonschema.toFieldConfig(
this.getFormSchema(parameters)
)];
this.parameterForm.setValue(this.parameterModel, {emitEvent: false});
});
this.parameterForm.valueChanges.subscribe((values) => {
this.parameterModel = values;
const formValue: { key :string, value: string }[] = []
for (const [key, value] of Object.entries(this.parameterModel)) {
formValue.push({key, value});
}
this.formControl.setValue(formValue)
})
}
getFormSchema(properties: string[]) {
const schema: JSONSchema7 = {
type: "object",
properties: {
},
};
let required: string[] = []
for (const pname of properties) {
schema.properties![pname] = {
type: "string",
title: pname
}
if (this.field.props.required) {
required.push(pname)
}
}
schema.required = required
return schema
}
}

View File

@@ -7,6 +7,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { CrudService, Filters } from "../crud.service"; import { CrudService, Filters } from "../crud.service";
import { CrudFormlyJsonschemaService } from "../crud-formly-jsonschema.service"; import { CrudFormlyJsonschemaService } from "../crud-formly-jsonschema.service";
import { FormGroup } from "@angular/forms"; import { FormGroup } from "@angular/forms";
import {DictionaryService} from "./dictionary.service";
@Component({ @Component({
selector: 'formly-foreignkey-type', selector: 'formly-foreignkey-type',
@@ -61,13 +62,14 @@ import { FormGroup } from "@angular/forms";
[resource_id]="this.foreignModel._id" [resource_id]="this.foreignModel._id"
[schema]="this.foreignSchema" [schema]="this.foreignSchema"
[is_modal]="true" [is_modal]="true"
(resourceDeleted)="onResourceDeleted($event)"> (resourceDeleted)="onResourceDeleted($event)"
(resourceUpdated)="onResourceModified($event)">
</crud-card> </crud-card>
<crud-card *ngIf="! this.hasValue()" <crud-card *ngIf="! this.hasValue()"
[resource]="this.foreignResource" [resource]="this.foreignResource"
[schema]="this.foreignSchema" [schema]="this.foreignSchema"
[is_modal]="true" [is_modal]="true"
(resourceCreated)="onResourceCreated($event)"> (resourceCreated)="onResourceModified($event)">
</crud-card> </crud-card>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@@ -88,7 +90,8 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
constructor(private crudService: CrudService, constructor(private crudService: CrudService,
private formlyJsonschema: CrudFormlyJsonschemaService, private formlyJsonschema: CrudFormlyJsonschemaService,
private modalService: NgbModal private modalService: NgbModal,
protected service: DictionaryService
) { ) {
super(); super();
} }
@@ -114,18 +117,12 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
this.foreignModel = {}; this.foreignModel = {};
this.foreignLabel = ""; this.foreignLabel = "";
} else if (v != this.foreignModel._id) { } else if (v != this.foreignModel._id) {
this.getResource(v).pipe( this.loadModel(v);
map(v => { this.setModel(v); return v; }),
map( v => this.formControl.patchValue(v._id))
).subscribe()
} }
}) })
).subscribe(); ).subscribe();
if (this.hasValue()) { if (this.hasValue()) {
this.getResource(this.formControl.value).pipe( this.loadModel(this.formControl.value);
map( v => { this.setModel(v); return v; }),
map( v => this.formControl.patchValue(v._id))
).subscribe()
} }
} }
@@ -133,6 +130,13 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
return this.crudService.get(this.foreignResource, id); return this.crudService.get(this.foreignResource, id);
} }
loadModel(id:string) {
this.getResource(id).pipe(
map(v => { this.setModel(v); return v; }),
map( v => this.formControl.patchValue(v._id))
).subscribe()
}
search: OperatorFunction<string, readonly string[]> = (text$: Observable<string>) => { search: OperatorFunction<string, readonly string[]> = (text$: Observable<string>) => {
return text$.pipe( return text$.pipe(
debounceTime(200), debounceTime(200),
@@ -163,6 +167,7 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
this.foreignModel = model; this.foreignModel = model;
this.foreignLabel = model.label; this.foreignLabel = model.label;
this.loadDisplayedFields(); this.loadDisplayedFields();
this.service.updateParameters(this.field.id!, this.getParameters())
} }
hasValue() { hasValue() {
@@ -173,9 +178,9 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
this.modalService.open(modal, { size: 'xl' }); this.modalService.open(modal, { size: 'xl' });
} }
onResourceCreated(resource_id: string) { onResourceModified(resource_id: string) {
this.modalService.dismissAll(); this.modalService.dismissAll();
this.getResource(resource_id); this.loadModel(resource_id);
} }
see(modal: any) { see(modal: any) {
@@ -213,4 +218,26 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
}); });
} }
} }
getParameters() {
return this.extractParameters(this.foreignModel);
}
extractParameters(obj: any) {
let result: string[] = [];
for (const [k, v] of Object.entries(obj)) {
if (typeof(obj[k]) == "string") {
const matches = obj[k].match(/%[^\s.]+%/g);
if (matches) {
result = result.concat(matches.map((p: string | any[]) => p.slice(1,-1)) || []);
}
result = result.concat();
} else if (typeof(obj[k]) == "object") {
result = result.concat(this.extractParameters(obj[k]));
}
}
return result
}
} }