Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix(ontology): support subproperty in ontology editor (DEV-332) (#643)
* fix(ontology): support subproperty in ontology editor (DEV-332)

* refactor(ontology): delete console.log

* fix(ontology): bug fix in part of prop

* feat(ontology): add unsupported prop types to list of default

* style(ontology): display hint in case of unsupported prop type

* feat(ontology): function to get super and default prop

* style(ontology): display hint in case of unsupported prop type

* refactor(ontology): clean up code

* feat(ontology): support subproperties (DEV-332)

* chore(deps): bump js-lib to latest

* refactor(ontology): clean up code

* test(ontology): fix tests

* refactor(ontology): clean up default props
  • Loading branch information
kilchenmann committed Feb 1, 2022
1 parent 84c3c85 commit c838043
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 272 deletions.
212 changes: 72 additions & 140 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -33,7 +33,7 @@
"@angular/platform-browser-dynamic": "^12.2.14",
"@angular/router": "^12.2.14",
"@ckeditor/ckeditor5-angular": "^2.0.2",
"@dasch-swiss/dsp-js": "^6.1.0",
"@dasch-swiss/dsp-js": "^6.2.0",
"@datadog/browser-rum": "^3.7.0",
"@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "5.0.0",
Expand Down
61 changes: 32 additions & 29 deletions src/app/project/ontology/default-data/default-properties.ts
Expand Up @@ -33,6 +33,18 @@ export interface DefaultProperty {
}

export class DefaultProperties {

public static unsupported: DefaultProperty = {
icon: 'warning_amber',
label: 'Unsupported property type',
description: 'This property type is not supported in the ontology editor',
subPropOf: undefined,
objectType: undefined,
guiEle: undefined,
group: 'Warning'
};


public static data: PropertyCategory[] = [
{
group: 'Text',
Expand All @@ -43,7 +55,7 @@ export class DefaultProperties {
description: 'Short text such as title or name',
subPropOf: Constants.HasValue,
objectType: Constants.TextValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'SimpleText', // better element would be: 'Input',
guiEle: Constants.GuiSimpleText, // better element would be: 'Input',
group: 'Text' // redundant information, but otherwise we don't get the main group name after selecting type
},
{
Expand All @@ -52,7 +64,7 @@ export class DefaultProperties {
description: 'Long text such as description; could have line breaks',
subPropOf: Constants.HasValue,
objectType: Constants.TextValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Textarea',
guiEle: Constants.GuiTextarea,
group: 'Text'
},
{
Expand All @@ -61,7 +73,7 @@ export class DefaultProperties {
description: 'A rich text editor with formatting options',
subPropOf: Constants.HasValue,
objectType: Constants.TextValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Richtext',
guiEle: Constants.GuiRichText,
group: 'Text'
}
]
Expand All @@ -75,7 +87,7 @@ export class DefaultProperties {
description: 'Dropdown menu with values from predefined list',
subPropOf: Constants.HasValue,
objectType: Constants.ListValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'List', // better element would be: 'Pulldown' or 'Select'
guiEle: Constants.GuiPulldown, // better element would be: 'Pulldown' or 'Select'
group: 'List'
}
]
Expand All @@ -89,7 +101,7 @@ export class DefaultProperties {
description: 'Yes or no, 1 or 0, true or false',
subPropOf: Constants.HasValue,
objectType: Constants.BooleanValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Checkbox', // should be 'Toggle' but it's not supported in DSP-Tangoh,
guiEle: Constants.GuiCheckbox, // should be 'Toggle' but it's not supported in DSP-Tangoh,
group: 'Boolean'
}
]
Expand All @@ -103,25 +115,16 @@ export class DefaultProperties {
description: 'A date field with day, month and year',
subPropOf: Constants.HasValue,
objectType: Constants.DateValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Date',
guiEle: Constants.GuiDatePicker,
group: 'Date / Time'
},
// {
// icon: 'date_range',
// label: 'Period',
// description: 'A period of time between two dates',
// subPropOf: Constants.HasValue,
// objectType: Constants.DateValue,
// guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Date',
// group: 'Date / Time'
// },
{
icon: 'access_time',
label: 'Timestamp',
description: 'A single timestamp on a timeline',
subPropOf: Constants.HasValue,
objectType: Constants.TimeValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'TimeStamp',
guiEle: Constants.GuiTimeStamp,
group: 'Date / Time'
},
{
Expand All @@ -130,7 +133,7 @@ export class DefaultProperties {
description: 'A time sequence with start and end point on a timeline',
subPropOf: Constants.HasValue,
objectType: Constants.IntervalValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Interval',
guiEle: Constants.GuiInterval,
group: 'Date / Time'
}
]
Expand All @@ -144,7 +147,7 @@ export class DefaultProperties {
description: 'Integer value',
subPropOf: Constants.HasValue,
objectType: Constants.IntValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Spinbox', // 'Number',
guiEle: Constants.GuiSpinbox, // 'Number',
group: 'Number'
},
{
Expand All @@ -153,16 +156,16 @@ export class DefaultProperties {
description: 'Decimal value',
subPropOf: Constants.HasValue,
objectType: Constants.DecimalValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Spinbox', // 'Number',
guiEle: Constants.GuiSpinbox, // 'Number',
group: 'Number'
},
{
icon: 'filter_3',
label: 'Page number',
description: 'The page number is needed for page classes in case of part of properties',
subPropOf: Constants.KnoraApiV2 + Constants.HashDelimiter + 'seqnum',
subPropOf: Constants.SeqNum,
objectType: Constants.IntValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Spinbox', // 'Number',
guiEle: Constants.GuiSpinbox, // 'Number',
group: 'Number'
}
]
Expand All @@ -176,26 +179,26 @@ export class DefaultProperties {
description: 'Refers to a resource class',
subPropOf: Constants.HasLinkTo,
objectType: Constants.LinkValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Searchbox', // 'Autocomplete',
guiEle: Constants.GuiSearchbox, // 'Autocomplete',
group: 'Link / Relation'
},
{
icon: 'extension',
label: 'Part of class',
description: 'Is part of a resource class',
subPropOf: Constants.KnoraApiV2 + Constants.HashDelimiter + 'isPartOf',
subPropOf: Constants.IsPartOf,
objectType: Constants.LinkValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Searchbox', // 'Autocomplete',
group: 'Relation'
guiEle: Constants.GuiSearchbox, // 'Autocomplete',
group: 'Link / Relation'
},
{
icon: 'language',
label: 'External URL',
description: 'Link to an external website',
subPropOf: Constants.HasValue,
objectType: Constants.UriValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'SimpleText',
group: 'Link'
guiEle: Constants.GuiSimpleText,
group: 'Link / Relation'
}
]
},
Expand All @@ -208,7 +211,7 @@ export class DefaultProperties {
description: 'Geographical location',
subPropOf: Constants.HasValue,
objectType: Constants.GeonameValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Geonames',
guiEle: Constants.GuiGeonames,
group: 'Location'
}
]
Expand All @@ -222,7 +225,7 @@ export class DefaultProperties {
description: 'A simple color value',
subPropOf: Constants.HasValue,
objectType: Constants.ColorValue,
guiEle: Constants.SalsahGui + Constants.HashDelimiter + 'Colorpicker',
guiEle: Constants.GuiColorPicker,
group: 'Shape'
}
]
Expand Down
14 changes: 12 additions & 2 deletions src/app/project/ontology/ontology.service.spec.ts
@@ -1,12 +1,22 @@
import { TestBed } from '@angular/core/testing';

import { CacheService } from 'src/app/main/cache/cache.service';
import { OntologyService } from './ontology.service';


describe('OntologyService', () => {
let service: OntologyService;

const cacheServiceSpy = jasmine.createSpyObj('CacheService', ['get']);

beforeEach(() => {
TestBed.configureTestingModule({});
TestBed.configureTestingModule({
providers: [
{
provide: CacheService,
useValue: cacheServiceSpy
},
]
});
service = TestBed.inject(OntologyService);
});

Expand Down
108 changes: 106 additions & 2 deletions src/app/project/ontology/ontology.service.ts
@@ -1,5 +1,16 @@
import { Injectable } from '@angular/core';
import { Cardinality, Constants } from '@dasch-swiss/dsp-js';
import {
Cardinality,
Constants, ReadOntology,
ResourcePropertyDefinitionWithAllLanguages
} from '@dasch-swiss/dsp-js';
import { Observable, of } from 'rxjs';
import { CacheService } from 'src/app/main/cache/cache.service';
import {
DefaultProperties,
DefaultProperty,
PropertyCategory
} from './default-data/default-properties';

/**
* helper methods for the ontology editor
Expand All @@ -9,7 +20,12 @@ import { Cardinality, Constants } from '@dasch-swiss/dsp-js';
})
export class OntologyService {

constructor() { }
// list of default property types
defaultProperties: PropertyCategory[] = DefaultProperties.data;

constructor(
private _cache: CacheService
) { }

/**
* create a unique name (id) for resource classes or properties;
Expand Down Expand Up @@ -79,4 +95,92 @@ export class OntologyService {
return Cardinality._0_1;
}
}

getSuperProperty(property: ResourcePropertyDefinitionWithAllLanguages): string {
// get ontology from property info
const ontoIri = property.id.split(Constants.HashDelimiter)[0];

let superPropIri: string;

// get iri from sub properties
if (property.subPropertyOf.length) {
for (const subProp of property.subPropertyOf) {
const baseOntoIri = subProp.split(Constants.HashDelimiter)[0];
// compare with knora base ontology
if (baseOntoIri !== Constants.KnoraApiV2) {
// the property is not a subproperty of knora base ontology
// get property iri from another ontology
this._cache.get('currentProjectOntologies').subscribe(
(ontologies: ReadOntology[]) => {
const onto = ontologies.find(i => i.id === baseOntoIri);
superPropIri = onto.properties[subProp].subPropertyOf[0];
}
);
}

if (superPropIri) {
break;
}
}
}

return (superPropIri ? superPropIri : undefined);
}

/**
* get default property information for a certain ontology property
*/
getDefaultPropType(property: ResourcePropertyDefinitionWithAllLanguages): Observable<DefaultProperty> {
let propType: DefaultProperty;

if (!property.guiElement) {
// we don't know what element to use, so it's unsupported property
return of (DefaultProperties.unsupported);
}

for (const group of this.defaultProperties) {
if (property.subPropertyOf.length) {
for (const subProp of property.subPropertyOf) {
// if subProp is of type "link to" or "part of" we have to check the subproperty;
// otherwise we get the necessary property info from the objectType
if (subProp === Constants.HasLinkTo || subProp === Constants.IsPartOf) {
propType = (group.elements.find(i =>
i.guiEle === property.guiElement && i.subPropOf === subProp
));
} else {

// if the property is type of number or list, the gui element is not relevant
// because the app supports only one gui element (at the moment): the spinbox resp. the list pulldown
if (property.objectType === Constants.DecimalValue || property.objectType === Constants.ListValue) {
propType = (group.elements.find(i =>
i.objectType === property.objectType
));
} else if (property.objectType === Constants.IntValue && subProp === Constants.SeqNum) {
propType = (group.elements.find(i =>
i.objectType === property.objectType && i.subPropOf === Constants.SeqNum
));
} else {
propType = (group.elements.find(i =>
i.guiEle === property.guiElement && i.objectType === property.objectType
));
}

}
}
if (propType) {
break;
}
}
}

if (!propType) {
// property type could not be found in the list of default properties
// maybe it's not supported e.g. if propDef.objectType === Constants.GeomValue || propDef.subPropertyOf[0] === Constants.HasRepresentation
return of (DefaultProperties.unsupported);
}

// return of(propType);
return of (propType);

}
}
Expand Up @@ -5,8 +5,8 @@

<p *ngIf="resClassIri && propertyInfo.propDef" class="note warning mat-caption center">
You're adding an already existing property to this class.
The property can't modified here. If you want to modify it,
go to the "properties" view.
The property can't be modified here.<br>
If you want to modify the label, the comment or the type, please go to the "properties" view.
<!-- Be careful when editing it, it could have an effect in other resource classes if it is used there. -->
</p>

Expand All @@ -31,6 +31,9 @@
</mat-option>
</mat-optgroup>
</mat-select>
<mat-hint *ngIf="unsupportedPropertyType" class="ontology-warning-with-prefix">
{{propertyForm.controls['propType'].value.description}}
</mat-hint>
</mat-form-field>

<!-- name -->
Expand Down

0 comments on commit c838043

Please sign in to comment.