Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ontology): set the cache earlier in case of only one ontology (DSP-1374) #397

Merged
merged 15 commits into from Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 0 additions & 8 deletions src/app/project/ontology/ontology.component.html
Expand Up @@ -141,14 +141,6 @@ <h3 class="mat-title space-reducer" [matTooltip]="ontology.id">{{ontology?.label
</mat-list>
</mat-sidenav>

<!-- <div class="ontology-editor-subheader">
<p *ngIf="!ontology.lastModificationDate" class="note warning center">
This data model can't be edited because of missing "lastModificationDate"!
</p>
<!-- button to select and add new resource class to the ontology --

</div> -->

<mat-sidenav-content class="ontology-editor-canvas drag-drop-stop">
<div class="ontology-editor-grid" *ngIf="view === 'classes'">

Expand Down
167 changes: 81 additions & 86 deletions src/app/project/ontology/ontology.component.ts
Expand Up @@ -126,8 +126,6 @@ export class OntologyComponent implements OnInit {
this.view = (this._route.snapshot.params.view ? this._route.snapshot.params.view : 'classes');
}

//

// set the page title
if (this.ontologyIri) {
this._titleService.setTitle('Project ' + this.projectcode + ' | Data model');
Expand All @@ -138,7 +136,7 @@ export class OntologyComponent implements OnInit {
}

ngOnInit() {
// this.loading = true;
this.loading = true;

// get information about the logged-in user
this.session = this._session.getSession();
Expand All @@ -148,7 +146,7 @@ export class OntologyComponent implements OnInit {
// default value for projectAdmin
this.projectAdmin = this.sysAdmin;

// set the cache
// set the project cache
this._cache.get(this.projectcode, this._dspApiConnection.admin.projectsEndpoint.getProjectByShortcode(this.projectcode));

// get the project data from cache
Expand All @@ -160,7 +158,7 @@ export class OntologyComponent implements OnInit {
this.projectAdmin = this.sysAdmin ? this.sysAdmin : this.session.user.projectAdmin.some(e => e === this.project.id);

// get the ontologies for this project
this.initList();
this.initOntologiesList();

this.ontologyForm = this._fb.group({
ontology: new FormControl({
Expand All @@ -178,6 +176,7 @@ export class OntologyComponent implements OnInit {
);
}

waitFor = (ms: number) => new Promise(r => setTimeout(r, ms));

/**
* Asyncs for each: Get all ontologies of project as ReadOntology
Expand All @@ -193,36 +192,91 @@ export class OntologyComponent implements OnInit {
this.existingOntologyNames.push(name);

// get each ontology
this.getOntology(ontologies[i].id, true);
// this.getOntology(ontologies[i].id, true);
this._dspApiConnection.v2.onto.getOntology(ontologies[i].id, true).subscribe(
(response: ReadOntology) => {
this.ontologies.push(response);
if (ontologies[i].id === this.ontologyIri) {
// one ontology is selected:
// get all information to display this ontology
// with all classes, properties and connected lists
this.loadOntology = true;
this.ontology = this.ontologies.find(onto => onto.id === this.ontologyIri);
this._cache.set('currentOntology', this.ontology);
this.ontology = response;

// grab the onto class information to display
const allOntoClasses = response.getAllClassDefinitions();

// reset the ontology classes
this.ontoClasses = [];

// display only the classes which are not a subClass of Standoff
allOntoClasses.forEach(resClass => {
const splittedSubClass = resClass.subClassOf[0].split('#');
if (!splittedSubClass[0].includes(Constants.StandoffOntology) && !splittedSubClass[1].includes('Standoff')) {
this.ontoClasses.push(resClass);
}
});
// sort classes by label
// --> TODO: add sort functionallity to the gui
this.ontoClasses = this._sortingService.keySortByAlphabetical(this.ontoClasses, 'label');

// grab the onto properties information to display
const allOntoProperties = response.getAllPropertyDefinitions();

// reset the ontology properties
this.ontoProperties = [];

// display only the properties which are not a subjectType of Standoff
allOntoProperties.forEach(resProp => {
const standoff = (resProp.subjectType ? resProp.subjectType.includes('Standoff') : false);
if (resProp.objectType !== Constants.LinkValue && !standoff) {
this.ontoProperties.push(resProp);
}
});
// sort properties by label
// --> TODO: add sort functionallity to the gui
this.ontoProperties = this._sortingService.keySortByAlphabetical(this.ontoProperties, 'label');
}
},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
}
);
await callback(ontologies[i]);
}
}

async loadAndCache(ontologies: OntologyMetadata[]) {
await this.asyncForEach(ontologies, async (onto: OntologyMetadata) => {
await this.waitFor(200);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify why this method is needed? I'm not a huge fan of waitFor and we have stated in our design doc that we shouldn't use setTimeout in production code. How will this behave with a rather slow internet connection?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats happens if wait is over but data is still not ready?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I know, that we have this rule in our conventions. But I have no idea how to solve this async requests in another way. I found this solution in a tutorial. Maybe someone of you has a better idea?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll pull the changes from main and check it out

Copy link
Contributor

@waychal waychal Feb 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way is:

  1. set Boolean variable to false
  2. once your data is ready, set this variable to true
  3. add watch on the boolean variable defined above and so when it becomes true, call the function you want run after the Ajax data is ready

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try that...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk if you've already found this @kilchenmann but the issue isn't that the data isn't arriving in time from the API, it's that this.ontologies isn't modified in time for the length check within loadAndCache so its length is always 0.

Without waitFor:
Screen Shot 2021-02-26 at 16 44 37

With waitFor:
Screen Shot 2021-02-26 at 16 47 18

I think it's due to your callback. Admittedly I'm quite horrible at dealing with async/await things but I'm quite certain it's an issue with your callback getting triggered earlier than it should.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @mdelez
I'm open for better solution than async/await. At the moment I'm lost. Even with the solution from @waychal I'm not 100% successful (yet). I'm pretty sure I'm doing something wrong, but I have no idea what and how to solve it 😞

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I came up with another solution without async/await. See my last commit (d174ba2).

if (this.ontologies.length === ontologies.length) {
// all ontologies were fetched from the API; we can set the cache
this._cache.set('currentProjectOntologies', this.ontologies);
// await this.waitFor(200);
this.loading = false;
// this.loadOntology = false;
this.setCache();
}
});
}

/**
* build the list of ontologies
* build the list of project ontologies
* and cache them as ReadOntology array
*/
initList(): void {
initOntologiesList(): void {

this.loading = true;

// reset existing ontology names and ontologies
this.existingOntologyNames = [];
this.ontologies = [];

const waitFor = (ms: number) => new Promise(r => setTimeout(r, ms));

this._dspApiConnection.v2.onto.getOntologiesByProjectIri(this.project.id).subscribe(
(response: OntologiesMetadata) => {

const loadAndCache = async () => {
await this.asyncForEach(response.ontologies, async (onto: OntologyMetadata) => {
await waitFor(200);
if (this.ontologies.length === response.ontologies.length) {
this.setCache();
}
});
};

if (!response.ontologies.length) {
this.setCache();
} else {
Expand All @@ -233,13 +287,12 @@ export class OntologyComponent implements OnInit {
this.openOntologyRoute(response.ontologies[0].id, this.view);
this.ontologyIri = response.ontologies[0].id;
}
loadAndCache();

this.loadAndCache(response.ontologies);
}

},
(error: ApiResponseError) => {
// temporary solution. There's a bug in js-lib in case of 0 ontologies
// s. youtrack issue DSP-863
this.ontologies = [];
this._errorHandler.showMessage(error);
this.loading = false;
Expand Down Expand Up @@ -270,63 +323,12 @@ export class OntologyComponent implements OnInit {
this._router.navigateByUrl(goto, { skipLocationChange: false });
}

// get ontology info
getOntology(id: string, updateOntologiesList: boolean = false) {
this._dspApiConnection.v2.onto.getOntology(id, true).subscribe(
(response: ReadOntology) => {

if (updateOntologiesList) {
this.ontologies.push(response);
}

// get current ontology as a separate part
if (response.id === this.ontologyIri) {
this.ontology = response;
// the ontology is the selected one
// grab the onto class information to display
this.ontoClasses = [];

const classKeys: string[] = Object.keys(response.classes);
// create list of resource classes without standoff classes
for (const c of classKeys) {
const splittedSubClass = this.ontology.classes[c].subClassOf[0].split('#');

if (splittedSubClass[0] !== Constants.StandoffOntology && splittedSubClass[1] !== 'StandoffTag' && splittedSubClass[1] !== 'StandoffLinkTag' && splittedSubClass[1] !== 'StandoffEventTag') {
this.ontoClasses.push(this.ontology.classes[c]);
}
}
this.ontoClasses = this._sortingService.keySortByAlphabetical(this.ontoClasses, 'label');

// grab the onto properties information to display
this.ontoProperties = [];
const propKeys: string[] = Object.keys(response.properties);
// create list of resource classes without standoff classes
for (const p of propKeys) {
const standoff = (this.ontology.properties[p].subjectType ? this.ontology.properties[p].subjectType.includes('Standoff') : false);
if (this.ontology.properties[p].objectType !== Constants.LinkValue && !standoff) {
this.ontoProperties.push(this.ontology.properties[p]);
}
}

this.ontoProperties = this._sortingService.keySortByAlphabetical(this.ontoProperties, 'label');

this.loadOntology = false;
}

},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
this.loadOntology = false;
}
);
}

resetOntology(id: string) {

this.ontology = undefined;
this.ontoClasses = [];
this.openOntologyRoute(id, this.view);
this.getOntology(id);
this.initOntologiesList();

}

Expand Down Expand Up @@ -362,7 +364,7 @@ export class OntologyComponent implements OnInit {
// reset and open selected ontology
this.ontologyForm.controls['ontology'].setValue(this.ontologyIri);
} else {
this.initList();
this.initOntologiesList();
}
});
}
Expand Down Expand Up @@ -394,8 +396,7 @@ export class OntologyComponent implements OnInit {

dialogRef.afterClosed().subscribe(result => {
// update the view
this.initList();
this.getOntology(this.ontologyIri);
this.initOntologiesList();
});
}

Expand Down Expand Up @@ -426,8 +427,7 @@ export class OntologyComponent implements OnInit {

dialogRef.afterClosed().subscribe(result => {
// update the view
this.initList();
this.getOntology(this.ontologyIri);
this.initOntologiesList();
});
}

Expand Down Expand Up @@ -468,7 +468,7 @@ export class OntologyComponent implements OnInit {
// reset current ontology
this.ontology = undefined;
// get the ontologies for this project
this.initList();
this.initOntologiesList();
// go to project ontology page
const goto = 'project/' + this.projectcode + '/ontologies/';
this._router.navigateByUrl(goto, { skipLocationChange: false });
Expand All @@ -479,7 +479,6 @@ export class OntologyComponent implements OnInit {
this.loadOntology = false;
}
);

break;

case 'ResourceClass':
Expand All @@ -488,13 +487,10 @@ export class OntologyComponent implements OnInit {
const resClass: DeleteResourceClass = new DeleteResourceClass();
resClass.id = id;
resClass.lastModificationDate = this.ontology.lastModificationDate;


this._dspApiConnection.v2.onto.deleteResourceClass(resClass).subscribe(
(response: OntologyMetadata) => {
this.loading = false;
this.resetOntology(this.ontologyIri);
// this.getOntology(this.ontologyIri);
},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
Expand All @@ -511,8 +507,7 @@ export class OntologyComponent implements OnInit {

setCache() {
// set cache for current ontology
this._cache.set('currentOntology', this.ontology);
this._cache.set('currentProjectOntologies', this.ontologies);
// this._cache.set('currentOntology', this.ontology);
kilchenmann marked this conversation as resolved.
Show resolved Hide resolved

// get all lists from the project
// it will be used to set gui attribute in a list property
Expand Down