From 70d5a6e75213c2f2ee7befa393f21d6f99ad4d7e Mon Sep 17 00:00:00 2001 From: ewandor Date: Thu, 19 Jan 2023 18:39:51 +0100 Subject: [PATCH] Adding support for nested types, array and dates! --- back/app/entity/models.py | 42 +++++++-- back/app/entity/schemas.py | 7 +- .../src/common/crud/card/card.component.html | 6 +- front/app/src/common/crud/crud.module.ts | 21 +++-- .../src/common/crud/jsonschemas.service.ts | 30 ++++++- front/app/src/common/crud/types/array.type.ts | 36 ++++++++ .../src/common/crud/types/datetime.type.ts | 89 +++++++++++++++++++ .../src/common/crud/types/multischema.type.ts | 38 ++++++++ .../common/crud/{ => types}/object.type.ts | 0 9 files changed, 246 insertions(+), 23 deletions(-) create mode 100644 front/app/src/common/crud/types/array.type.ts create mode 100644 front/app/src/common/crud/types/datetime.type.ts create mode 100644 front/app/src/common/crud/types/multischema.type.ts rename front/app/src/common/crud/{ => types}/object.type.ts (100%) diff --git a/back/app/entity/models.py b/back/app/entity/models.py index c5f3c0c7..8ac90301 100644 --- a/back/app/entity/models.py +++ b/back/app/entity/models.py @@ -1,8 +1,9 @@ from enum import Enum -from datetime import datetime -from pydantic import Field +from datetime import datetime, date +from typing import List, Literal -from beanie import Document +from pydantic import Field, BaseModel +from beanie import Document, Link class EntityType(str, Enum): @@ -11,13 +12,40 @@ class EntityType(str, Enum): institution = 'institution' +class Individual(BaseModel): + type: Literal['individual'] = 'individual' + firstname: str + middlenames: List[str] = Field(default=[]) + lastname: str + surnames: List[str] = Field(default=[]) + day_of_birth: date + job: str + employer: str + + +class Employee(BaseModel): + entity_id: str + role: str + + +class Corporation(BaseModel): + type: Literal['corporation'] = 'corporation' + title: str + activity: str + employees: List[Employee] = Field(default=[]) + + class Entity(Document): _id: str - type: EntityType name: str address: str + entity_data: Individual | Corporation = Field(..., discriminator='type') + created_at: datetime = Field(default=datetime.utcnow(), nullable=False) updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) - # - # class Settings: - # name = "entities" + + class Settings: + bson_encoders = { + date: lambda dt: datetime(year=dt.year, month=dt.month, day=dt.day, hour=0, minute=0, second=0) \ + if not hasattr(dt, 'hour') else dt + } diff --git a/back/app/entity/schemas.py b/back/app/entity/schemas.py index c8fbf997..645b347f 100644 --- a/back/app/entity/schemas.py +++ b/back/app/entity/schemas.py @@ -1,9 +1,9 @@ import uuid from datetime import datetime -from pydantic import BaseModel +from pydantic import BaseModel, Field -from .models import Entity, EntityType +from .models import Entity, EntityType, Individual, Corporation from ..core.schemas import Writer @@ -12,11 +12,12 @@ class EntityRead(Entity): class EntityCreate(Writer): - type: EntityType name: str address: str + entity_data: Individual | Corporation = Field(..., discriminator='type') class EntityUpdate(BaseModel): name: str address: str + entity_data: Individual | Corporation = Field(..., discriminator='type') diff --git a/front/app/src/common/crud/card/card.component.html b/front/app/src/common/crud/card/card.component.html index 1c4aea56..2f4c0a3d 100644 --- a/front/app/src/common/crud/card/card.component.html +++ b/front/app/src/common/crud/card/card.component.html @@ -1,11 +1,11 @@
-
+ Loading... - -
diff --git a/front/app/src/common/crud/crud.module.ts b/front/app/src/common/crud/crud.module.ts index 91b61043..bfa5d5fa 100644 --- a/front/app/src/common/crud/crud.module.ts +++ b/front/app/src/common/crud/crud.module.ts @@ -1,8 +1,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -//import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; - import { HttpClientModule } from "@angular/common/http"; import { FormsModule, ReactiveFormsModule} from '@angular/forms'; @@ -11,17 +9,23 @@ import { FormlyBootstrapModule } from '@ngx-formly/bootstrap'; import { CardComponent } from './card/card.component'; import { ListComponent } from './list/list.component'; -import { ObjectTypeComponent } from "./object.type"; -import {ApiService, CrudService} from "./crud.service"; -import {NgbModule} from "@ng-bootstrap/ng-bootstrap"; -import {JsonschemasService} from "./jsonschemas.service"; +import { ArrayTypeComponent } from "./types/array.type"; +import { ObjectTypeComponent } from "./types/object.type"; +import { DatetimeTypeComponent } from "./types/datetime.type"; +import { ApiService, CrudService } from "./crud.service"; +import { NgbModule} from "@ng-bootstrap/ng-bootstrap"; +import { JsonschemasService } from "./jsonschemas.service"; +import { MultiSchemaTypeComponent } from "./types/multischema.type"; @NgModule({ declarations: [ CardComponent, ListComponent, - ObjectTypeComponent + ObjectTypeComponent, + DatetimeTypeComponent, + ArrayTypeComponent, + MultiSchemaTypeComponent ], providers: [ JsonschemasService, ApiService, CrudService ], imports: [ @@ -34,6 +38,9 @@ import {JsonschemasService} from "./jsonschemas.service"; FormlyModule.forRoot({ types: [ { name: 'object', component: ObjectTypeComponent }, + { name: 'datetime', component: DatetimeTypeComponent }, + { name: 'array', component: ArrayTypeComponent }, + { name: 'multischema', component: MultiSchemaTypeComponent }, ] }), FormlyBootstrapModule diff --git a/front/app/src/common/crud/jsonschemas.service.ts b/front/app/src/common/crud/jsonschemas.service.ts index e9ae9c3c..e35d6021 100644 --- a/front/app/src/common/crud/jsonschemas.service.ts +++ b/front/app/src/common/crud/jsonschemas.service.ts @@ -32,15 +32,35 @@ export class JsonschemasService { for (let prop_name in resource.properties) { let prop = resource.properties[prop_name]; - if (this.is_reference(prop)) { - let subresourceName = this.get_reference_name(prop); - resource.components.schemas[subresourceName] = this.buildResource(subresourceName); + if (prop_name === '_id') { + delete resource.properties[prop_name] + } else if (this.is_reference(prop)) { + this.resolveReference(resource, prop); + } else if (prop.hasOwnProperty('oneOf')) { + for (let i in prop.oneOf) { + this.resolveReference(resource, prop.oneOf[i]); + } + } else if (prop.hasOwnProperty('items') && this.is_reference(prop.items)) { + this.resolveReference(resource, prop.items); + } else if (prop.format === 'date-time') { + prop.type = "datetime"; } } return resource; } + resolveReference(resource: any, prop_reference: any) { + let subresourceName = this.get_reference_name(prop_reference); + let subresource = this.buildResource(subresourceName); + resource.components.schemas[subresourceName] = subresource; + for (let subsubresourceName in subresource.components.schemas) { + if (! resource.components.schemas.hasOwnProperty(subsubresourceName)) { + resource.components.schemas[subsubresourceName] = subresource.components.schemas[subsubresourceName]; + } + } + } + getCreateResource(resourceName: string): Observable { return new Observable((observer) => { this.getSchemas().subscribe(() => { @@ -75,6 +95,10 @@ export class JsonschemasService { return prop.hasOwnProperty('$ref'); } + private is_union(prop: any) { + return prop.hasOwnProperty('oneOf'); + } + private get_reference_name(prop: any) { return prop['$ref'].substring(prop['$ref'].lastIndexOf('/')+1); } diff --git a/front/app/src/common/crud/types/array.type.ts b/front/app/src/common/crud/types/array.type.ts new file mode 100644 index 00000000..5b10ac60 --- /dev/null +++ b/front/app/src/common/crud/types/array.type.ts @@ -0,0 +1,36 @@ +import { Component } from '@angular/core'; +import { FieldArrayType } from '@ngx-formly/core'; + +@Component({ + selector: 'formly-array-type', + template: ` +
+ +

{{ props.description }}

+
+ +
+ + + +
+ +
+ +
+
+
+ `, +}) +export class ArrayTypeComponent extends FieldArrayType { + constructor() { + super(); + } +} + + +/** Copyright 2021 Formly. All Rights Reserved. + Use of this source code is governed by an MIT-style license that + can be found in the LICENSE file at https://github.com/ngx-formly/ngx-formly/blob/main/LICENSE */ \ No newline at end of file diff --git a/front/app/src/common/crud/types/datetime.type.ts b/front/app/src/common/crud/types/datetime.type.ts new file mode 100644 index 00000000..b7900879 --- /dev/null +++ b/front/app/src/common/crud/types/datetime.type.ts @@ -0,0 +1,89 @@ + +import { Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import { formatDate } from "@angular/common"; +import { NgbDateStruct, NgbTimeStruct } from '@ng-bootstrap/ng-bootstrap'; +import { FieldType, FieldTypeConfig } from '@ngx-formly/core'; + + +@Component({ + selector: 'app-form-datepicker-type', + template: ` + +
+ + + + + +
+
+ +
+ `, +}) +export class DatetimeTypeComponent extends FieldType implements OnInit +{ + public time : NgbTimeStruct = { hour: 12, minute: 0, second: 0 } + public date : NgbDateStruct = { year: 2023, month: 1, day: 9 } + public datetime : Date = new Date() + + + constructor() { + super(); + } + + ngOnInit() { + this.formControl.valueChanges.subscribe(value => { + this.datetime = new Date(value) + this.date = this.getDateStruct(this.datetime); + this.time = this.getTimeStruct(this.datetime); + }) + } + + getDateStruct(d: Date) { + return { + year: d.getFullYear(), + month: d.getMonth() + 1, + day: d.getDate(), + } + } + + getTimeStruct(d: Date) { + return { + hour: d.getHours(), + minute: d.getMinutes(), + second: d.getSeconds(), + } + } + + changeDatetime(event: any) { + this.datetime.setFullYear(this.date.year) + this.datetime.setMonth(this.date.month - 1) + this.datetime.setDate(this.date.day) + this.datetime.setHours(this.time.hour) + this.datetime.setMinutes(this.time.minute) + this.datetime.setSeconds(this.time.second) + + this.formControl.setValue( + formatDate(this.datetime, 'YYYY-MM-ddTHH:mm:ss.SSS', 'EN_US', 'CET') + ) + } +} diff --git a/front/app/src/common/crud/types/multischema.type.ts b/front/app/src/common/crud/types/multischema.type.ts new file mode 100644 index 00000000..7dcae663 --- /dev/null +++ b/front/app/src/common/crud/types/multischema.type.ts @@ -0,0 +1,38 @@ +import {Component, OnInit} from '@angular/core'; +import { FieldType } from '@ngx-formly/core'; +import {Observable} from "rxjs"; + +@Component({ + selector: 'formly-multi-schema-type', + template: ` +
+
+ +

{{ props.description }}

+ + +
+
+ `, +}) +export class MultiSchemaTypeComponent extends FieldType implements OnInit { + ngOnInit() { + let f = this.field + f.fieldGroup![0].props!.options!.forEach(function (option) { + option.label = f.fieldGroup![1].fieldGroup![option.value].props!.label; + f.fieldGroup![1].fieldGroup![option.value].props!.label = ""; + }); + + f.fieldGroup![1].fieldGroup!.forEach(function (field) { + //field.fieldGroup![0].hide = true; + }); + + } +} + + +/** Copyright 2021 Formly. All Rights Reserved. + Use of this source code is governed by an MIT-style license that + can be found in the LICENSE file at https://github.com/ngx-formly/ngx-formly/blob/main/LICENSE */ diff --git a/front/app/src/common/crud/object.type.ts b/front/app/src/common/crud/types/object.type.ts similarity index 100% rename from front/app/src/common/crud/object.type.ts rename to front/app/src/common/crud/types/object.type.ts