Adding support for nested types, array and dates!

This commit is contained in:
2023-01-19 18:39:51 +01:00
parent aa4399ea20
commit 70d5a6e752
9 changed files with 246 additions and 23 deletions

View File

@@ -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
}

View File

@@ -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')

View File

@@ -1,11 +1,11 @@
<div>
<form [formGroup]="form" (ngSubmit)="onSubmit(model)">
<form cForm [formGroup]="form" (ngSubmit)="onSubmit(model)">
<span class="col col-form-label" *ngIf="loading$ | async">Loading...</span>
<formly-form [form]="form" [fields]="fields" [model]="model"></formly-form>
<button color="primary" type="submit" class="btn btn-default">
<button cButton color="primary">
{{ this.isCreateForm() ? "Create" : "Update" }}
</button>
<button *ngIf="!this.isCreateForm()" (click)="onDelete()" color="danger" type="" class="btn btn-default">
<button cButton *ngIf="!this.isCreateForm()" (click)="onDelete()" color="danger">
Delete
</button>
</form>

View File

@@ -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 { 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

View File

@@ -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<Schema> {
return new Observable<Schema>((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);
}

View File

@@ -0,0 +1,36 @@
import { Component } from '@angular/core';
import { FieldArrayType } from '@ngx-formly/core';
@Component({
selector: 'formly-array-type',
template: `
<div class="mb-3">
<label *ngIf="props.label" class="form-label">{{ props.label }}</label>
<p *ngIf="props.description">{{ props.description }}</p>
<div class="d-flex flex-row-reverse">
<button class="btn btn-primary" type="button" (click)="add()">+</button>
</div>
<div class="alert alert-danger" role="alert" *ngIf="showError && formControl.errors">
<formly-validation-message [field]="field"></formly-validation-message>
</div>
<div *ngFor="let field of field.fieldGroup; let i = index" class="row align-items-start">
<formly-field class="col" [field]="field"></formly-field>
<div *ngIf="field.props!['removable'] !== false" class="col-2 text-right">
<button class="btn btn-danger" type="button" (click)="remove(i)">-</button>
</div>
</div>
</div>
`,
})
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 */

View File

@@ -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: `
<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>
<div class="input-group" *ngIf="! this.field.props.readonly">
<input type="text"
[formControl]="formControl"
[formlyAttributes]="field"
[class.is-invalid]="showError"
/>
<input
class="form-control"
placeholder="yyyy-mm-dd"
name="dp"
[(ngModel)]="date"
(ngModelChange)="changeDatetime($event)"
ngbDatepicker
#d="ngbDatepicker"
/>
<button class="btn btn-outline-secondary bi bi-calendar3" (click)="d.toggle()" type="button"></button>
<ngb-timepicker
(ngModelChange)="changeDatetime($event)"
[(ngModel)]="time"
[seconds]="true">
</ngb-timepicker>
</div>
<div class="input-group" *ngIf="this.field.props.readonly">
<input class="form-control" value="{{ this.datetime.toLocaleString() }}" disabled=""/>
</div>
`,
})
export class DatetimeTypeComponent extends FieldType<FieldTypeConfig> 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')
)
}
}

View File

@@ -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: `
<div class="card mb-3">
<div class="card-body">
<label *ngIf="props.label" class="form-label">{{ props.label }}</label>
<p *ngIf="props.description">{{ props.description }}</p>
<div class="alert alert-danger" role="alert" *ngIf="showError && formControl.errors">
<formly-validation-message [field]="field"></formly-validation-message>
</div>
<formly-field *ngFor="let f of field.fieldGroup" [field]="f"></formly-field>
</div>
</div>
`,
})
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 */