diff --git a/nietzsche-beta-app/src/app/crossref-editor-component/crossref-editor-data-service.service.ts b/nietzsche-beta-app/src/app/crossref-editor-component/crossref-editor-data-service.service.ts index 0d35933..855b7a9 100644 --- a/nietzsche-beta-app/src/app/crossref-editor-component/crossref-editor-data-service.service.ts +++ b/nietzsche-beta-app/src/app/crossref-editor-component/crossref-editor-data-service.service.ts @@ -1,901 +1,904 @@ import {EventEmitter, Injectable} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; -import { BASEURL, RQ_CROSSREF_TREE_LINES, WRITEURL } from '../constants'; +import { BASEURL, RQ_CROSSREF_TREE_LINES } from '../constants'; import {QueryService} from '../services/query.service'; import * as N3 from 'node_modules/n3/src'; import {moveItemInArray} from '@angular/cdk/drag-drop'; import {NavigationEntity} from '../models/models'; import {TlnLine} from '../tln-edition/datatypes/line'; import {PageViewService} from '../page-view/page-view.service'; import {TlnQueryServiceInterface} from '../tln-edition/models'; import {SafeUrl} from '@angular/platform-browser'; import {ChangeMgmntDef, TlnPredicate} from '../rdf-editor-module/statement-handler'; -import {RdfData} from '../rdf-editor-module/rdf-editor-DataSets'; +import {RdfData, RdfStoredData} from '../rdf-editor-module/rdf-editor-DataSets'; import {TlnResource, TlnTransaction} from '../rdf-editor-module/editor-resources'; @Injectable({ providedIn: 'root' }) export class CrossrefEditorDataServiceService { textGenesis: TextGenesis; // the one textGenesis used by all components textGenesisEmitter: EventEmitter; transaction = new TlnTransaction(); def: ChangeMgmntDef; queryService: TlnQueryServiceInterface; queryServiceEmitter: EventEmitter; constructor(private activatedRoute: ActivatedRoute, private qService: QueryService, private pageViewService: PageViewService) { this.queryServiceEmitter = new EventEmitter(); this.textGenesisEmitter = new EventEmitter(); // creating a def: keep the patient statements as they got deleted by their agents anyway this.def = new ChangeMgmntDef( true, true, false, false, true, [], [], [], [], false, {keepAgentLists: false, keepAgentStatements: false, keepPatientLists: true, keepPatientStatements: true} ); // subcribing at klicked lines to set them as selection this.pageViewService.onClickedLine.subscribe((clickedLine: TlnLine) => { - if (clickedLine.page === this.textGenesis.checkedOutTextVersion().internalTextVersion.checkedOutTextUnit().pageData.pageIri) { + if (clickedLine.page === this.textGenesis.checkedOutTextVersion().internalTextVersion.checkedOutTextUnit().textUnit.pageData.pageIri) { this.setLine(clickedLine); this.updateAll(this.textGenesis); } - }) + }); } readInTextGenesis(textGenesisIri: string) { textGenesisIri = 'http://rdfh.ch/projects/0068#_Mp_XIV_TextGenesis0' ; // Todo: make generic TextGenesis.buildAsyncFromStore(this.qService, textGenesisIri).then(textGenesis => this.updateAll(textGenesis)); } resetFromStore() { TextGenesis.buildAsyncFromStore(this.qService, this.textGenesis.iri()).then(textGenesis => this.updateAll(textGenesis)); } // sets the lines: if start and end are existing it resets the startline else sets the endline setLine(line: TlnLine) { // get the active page ... if (!this.textGenesis.checkedOutTextVersion().internalTextVersion.checkedOutTextUnit().textUnit.hasStartLine() || ( this.textGenesis.checkedOutTextVersion().internalTextVersion.checkedOutTextUnit().textUnit.hasStartLine() && this.textGenesis.checkedOutTextVersion().internalTextVersion.checkedOutTextUnit().textUnit.hasEndLine() ) ) { this.textGenesis.checkedOutTextVersion().internalTextVersion.checkedOutTextUnit().textUnit.setStartLine(line.id, line.number); } else { // endLine must be set this.textGenesis.checkedOutTextVersion().internalTextVersion.checkedOutTextUnit().textUnit.setEndLine(line.id, line.number); } } - updateAll(textGenesis:TextGenesis) { + updateAll(textGenesis: TextGenesis) { this.textGenesis = textGenesis; this.textGenesisEmitter.emit(textGenesis); } // sets the textVersion to external/internal and checksout/unchecks the active one. setExternityOfTextVersion(setToExternal: boolean) { this.textGenesis.checkedOutTextVersion().changeExternity(setToExternal); this.updateAll(this.textGenesis); } public deleteGuiTextVersion(iri) { this.textGenesis.deleteTextVersion(iri); this.updateAll(this.textGenesis); } public unDeleteGuiTextVersion(iri) { this.textGenesis.unDeleteTextVersion(iri); this.updateAll(this.textGenesis); } // builds a GuiTextUnit with the added and queried pages data and adds it to the textVersion onAddingPage(navItem: NavigationEntity, index: number){ // Todo: Change to nonStore constructor!!! instore = false - GuiTextUnit.buildAsyncFromStore(this.qService, navItem.tlnEntity.iri, 'Texteinheit', this.textGenesis.checkedOutTextVersion().iri()) + GuiTextUnit.createNewGuiTextUnit(this.qService, navItem.tlnEntity.iri, 'Texteinheit', this.textGenesis.checkedOutTextVersion().iri()) .then(guiTextUnit => { this.textGenesis.checkedOutTextVersion().internalTextVersion.addNewGuiTextUnit(guiTextUnit, index); this.updateAll(this.textGenesis); }); } movePageInArray(previousIndex: number, index: number) { this.textGenesis.checkedOutTextVersion().internalTextVersion.moveTextUnitInArray(previousIndex, index); this.updateAll(this.textGenesis); } onRemovePage(iri: string) { this.textGenesis.checkedOutTextVersion().internalTextVersion.deleteGuiTextUnit(iri); this.updateAll(this.textGenesis); } undeleteGuiTextUnit(iri: string) { this.textGenesis.checkedOutTextVersion().internalTextVersion.unDeleteGuiTextUnit(iri); this.updateAll(this.textGenesis); } // resets wholepage: boolean to the desired truthVal changeWholePage(textUnit: TextUnit, truthVal: boolean) { if (this.textGenesis.checkedOutTextVersion().internalTextVersion.checkedOutTextUnit().wholePage === truthVal ) { return } // guard // getting the idx of the guitextUnit (may be a page or a textUnit) this.textGenesis.checkedOutTextVersion().changeWholePage(textUnit, truthVal); this.updateAll(this.textGenesis) } // collects all tlnResources into one array update/delete/create collectResources(): TlnResource[] { let resources: TlnResource[] = []; resources.push(this.textGenesis.resource); this.textGenesis.childResources.forEach(child => { resources.push(child.externalTextVersion.resource); // add ext resource resources.push(child.internalTextVersion.resource); // int resource child.internalTextVersion.childResources.forEach(iChild => { resources.push(iChild.textUnit.resource); console.log('iChild', iChild) }); }); return resources; } writeOut(store: boolean) { let resources = this.collectResources(); console.log('resources', resources); console.log('del Resources ', resources.filter(res => res.isDeleted() && !res.ignore()).map(res => res.iri)); console.log('stored Resources ', resources.filter(res => res.isInStore()).map( res => res.iri)); console.log('irgnored Resources ', resources.filter(res => res.ignore()).map( res => res.iri)); this.transaction = new TlnTransaction(); this.transaction.commit(resources, '', '', this.def ).then(committed => { this.transaction.committed = committed; console.log('committed', committed); if (committed === false) { console.log('Nothing to commit. No changes made.')} else { console.log('committed', committed); console.log('TRANSACTION', this.transaction) this.transaction.preview(); } }); } readInTtl (ttlData: string) { const parser = new N3.Parser(), store = new N3.Store(); parser.parse(ttlData, function (error, triple, prefixes) { if (triple) store.addTriple(triple); else // is complete onComplete(); return store; }); function onComplete() { const republic = store.find('http://example.org/library/the-republic', null, null)[0]; console.log(store); } parser.parse(this.textGenesis.ttl_export, (error, quad, prefixes) => { if (quad) { store.addQuad(quad); } else console.log('# That\'s all, folks!', prefixes); }); console.log('my store ', store); } } // main super class with basic functionality inherited by all gui classes export class CrossRefEditorElement { protected _iri: string; private _checkedOut: boolean; // wether the thing is checked out for editing atm or not. protected _paerentIri: string; protected label: string; protected parentLabel: string; resource: TlnResource; // the equivalent rdf resource from store and for editing childsPredicate: TlnPredicate; childResources: any[]; // Other CrossrefEditorElements or a subclass/extension public deleted = false; public displayedPredicates: string[]; public mutablePredicates: string[]; // Todo: refactor: set label inside subclasses - not via passing a parameter? constructor(iri?: string, label?: string, parentIri?: string, checkOut = false, parentLabel?: string){ if (iri !== '') { this._iri = iri; } else {// any random iri - does not matter this._iri = RdfData.createIri(label, parentIri); } this._paerentIri = parentIri; this._checkedOut = checkOut; this.parentLabel = parentLabel; this.resource = new TlnResource(this._iri, label); } checkOut(val: boolean) { this._checkedOut = val; } isCheckedOut() { return this._checkedOut; } iri() { return this._iri; } public rdfTypeInStore(): string { return this.resource._rdfDatasetFromStore.rdfType(); } // sets itself to deleted, deletes all statements and runs deletion of each child setDeleted() { this.deleted = true; this.resource.setDeleted(); // set the whole resource to deleted = true if (!this.childResources || !this.childResources.length) {return} // guard; else deletes also all children recursively this.childResources.forEach(child => { child.setDeleted(); // run deletion also for children }); } setUndeleted() { this.deleted = false; this.resource.setUndeleted(); if (!this.childResources || !this.childResources.length) {return} // guard; else deletes also all children recursively this.childResources.forEach(child => { child.setUndeleted(); // run also for children }); } } export class TextGenesis extends CrossRefEditorElement{ public childResources: GuiTextVersion[] = []; // setting childresources to GuiTextVersion[] // for writing out ttl allQuads: N3.Quad[]; public ttl_export: string; public ttl_exportReady: EventEmitter; - constructor(iri: string, label?: string, parentIri?: string, checkOut = false, parentLabel?: string) { + constructor(iri: string, label?: string, parentIri?: string, checkOut = false) { super(iri, label, parentIri, checkOut); const ranges = new Set(); ranges.add('http://www.w3.org/1999/02/22-rdf-syntax-ns#List'); this.childsPredicate = new TlnPredicate('http://www.nie.org/ontology/nietzsche#hasGeneticOrder', ranges); this.allQuads = []; this.ttl_exportReady = new EventEmitter(); } public static async buildAsyncFromStore(qService: QueryService, iri: string): Promise { let textGenesis = new TextGenesis(iri, 'Textgenese'); textGenesis.resource = await TlnResource.buildFromStoreAsync(qService, iri, 'TextGenesis'); let children = await textGenesis.resource.getChildIris(textGenesis.childsPredicate.iri()); if (!children) {return textGenesis} // guard; else we create children children.forEach((child, index) => { GuiTextVersion.buildAsyncFromStore(qService, child, iri).then(guiTV => textGenesis.childResources.splice(index,0, guiTV)) }); return textGenesis; } public textVersions(): GuiTextVersion[] { return this.childResources; } public addTextVersion(textVersion: GuiTextVersion, index: number = this.childResources.length){ this.childResources.splice(index, 0, textVersion); this.resource._rdfDatasetToEdit.putStatementInListAsStatement(this.childsPredicate, textVersion.iri(), index); } public setTextVersion(textVersion: GuiTextVersion) { const idx = this.getTextVersionIndex(textVersion.iri()); if (this.childResources[idx].isCheckedOut()) { this.childResources[idx] = textVersion; this.resource._rdfDatasetToEdit.setStatementInListAsStatement(this.childsPredicate, textVersion.iri(), idx); } } deleteTextVersion(iri) { const idx = this.childResources.findIndex(tUnit => tUnit.iri() === iri); // setting both to deleted - no matter whats the type this.childResources[idx].onDelete(); this.resource._rdfDatasetToEdit.deleteAllStatementsWithObject(iri) } unDeleteTextVersion(iri) { const idx = this.childResources.findIndex(tUnit => tUnit.iri() === iri); this.childResources[idx].unDelete(); this.resource._rdfDatasetToEdit.putStatementInListAsStatement(this.childsPredicate, iri, idx); } getTextVersionIndex(iri: string): number { return this.textVersions().findIndex(t => t.iri() === iri) } // CHecks out one TextVersion and unchecks all others. checkoutTextVersion(iri: string) { let idx = this.getTextVersionIndex(iri); for (let i = 0; i <= this.childResources.length-1; i++) { if (i === idx) { this.childResources[i].checkOut(true); } else { this.childResources[i].checkOut(false); } } } checkedOutTextVersion(): GuiTextVersion { return this.childResources[this.checkedOutTextVersionIdx()]; } checkedOutTextVersionIdx(): number { return this.childResources.findIndex(tVersion => tVersion.isCheckedOut()); } // // returns an array of all quads contained // async getQuads(): Promise { // this.allQuads = []; // // adding the textGenesis quads // const textGenesisQuads: N3.Quad[] = await this.resource.buildRdfTransaction(); // this.allQuads = await [... this.allQuads, ... textGenesisQuads] // // adding the textVersion quads // await this.textVersions().forEach(async tVersion => { // let versionQuads = await tVersion.internalTextVersion.resource.buildRdfTransaction(); // this.allQuads = [...this.allQuads, ...versionQuads]; // tVersion.internalTextVersion.guiTextUnits().forEach(async tUnit => { // if (!tUnit.wholePage) { // let textUnitQuads = await tUnit.textUnit.resource.buildRdfTransaction(); // // only if it is not a whole page, i.e. there is a textUnit we get also its quads & its properties as quads TODO: Ontology // this.allQuads = [...this.allQuads, ...textUnitQuads]; // } // }); // }); // return this.allQuads; // } // check for completeness isComplete() { return this.textVersions().every( tVersion => tVersion.isComplete()); } } export class GuiTextVersion extends CrossRefEditorElement{ label: string; private isExternalResource: boolean; private changedExternity: boolean; internalTextVersion: InternalTextVersion; // the internal TextVersion Resource with its properties from RDF externalTextVersion: ExternalTextVersion; // the internal TextVersion Resource with its properties from RDF constructor(iri: string, // iri of internal or external resource label: string, parentIri: string, checkOut = false, inStore = false, isExternalResource = false, parentLabel?) { super(iri, label, parentIri, checkOut); this.isExternalResource = isExternalResource; this.changedExternity = false; this.label = label; this.parentLabel = parentLabel; } public static async buildAsyncFromStore(qService: QueryService, iri: string, parentIri?: string): Promise { - let guiTextVersion = new GuiTextVersion(iri, 'TextVersion', parentIri); - guiTextVersion.resource = await TlnResource.buildFromStoreAsync(qService, iri, 'TextVersion'); - // setting external - guiTextVersion.isExternalResource = guiTextVersion.rdfTypeInStore() === 'http://www.nie.org/ontology/nietzsche#ExternalTextUnit'; + const guiTextVersion = new GuiTextVersion(iri, 'TextVersion', parentIri); + // guiTextVersion.resource = await TlnResource.buildFromStoreAsync(qService, iri, 'TextVersion'); + guiTextVersion.isExternalResource = await guiTextVersion.isExternalAsync(qService, iri); + if (guiTextVersion.isExternalResource) { - guiTextVersion.externalTextVersion = await ExternalTextVersion.buildAsyncFromStore(qService,iri, parentIri); + guiTextVersion.externalTextVersion = await ExternalTextVersion.buildAsyncFromStore(qService, iri, parentIri); // creating also an internal one, but set it to deleted for switching easily guiTextVersion.internalTextVersion = new InternalTextVersion('', 'Textversion', parentIri); - guiTextVersion.internalTextVersion.setDeleted() // set the resource to deleted because it should be ignored if not switched to internal + // set the resource to deleted because it should be ignored if not switched to internal + guiTextVersion.internalTextVersion.setDeleted(); } else { guiTextVersion.internalTextVersion = await InternalTextVersion.buildAsyncFromStore(qService, iri, parentIri); - // building also a new exteral one: and set it to deleted + // building also a new external one and set it to deleted guiTextVersion.externalTextVersion = new ExternalTextVersion('', 'TextVersion', parentIri); guiTextVersion.externalTextVersion.setDeleted(); // set to deleted because we like to ignore it unless switched to externaö } return guiTextVersion; } public static buildNew(parentIri: string) { - let guiTextVersion = new GuiTextVersion('', 'Textversion', parentIri, true); + const guiTextVersion = new GuiTextVersion('', 'Textversion', parentIri, true); guiTextVersion.externalTextVersion = new ExternalTextVersion(guiTextVersion.iri(), 'TextVersion', parentIri); guiTextVersion.internalTextVersion = new InternalTextVersion(guiTextVersion.iri(), 'Textversion', parentIri ); return guiTextVersion; + } + public async isExternalAsync(qService, iri) { + const type = await RdfStoredData.getRdfTypeFromStore(qService, iri); + return type === 'http://www.nie.org/ontology/nietzsche#ExternalTextUnit'; } isComplete() { if (this.isExternalResource) { return this.externalTextVersion.isComplete() } else { return this.internalTextVersion.isComplete(); } } changeExternity(isExternal: boolean) { this.isExternalResource = isExternal; if (isExternal) { // changed to external // change deletions this.internalTextVersion.setDeleted(); if (this.externalTextVersion.resource.isInStore()) { this.externalTextVersion.setUndeleted(); } this.externalTextVersion.checkOut(true); this.internalTextVersion.checkOut(false); } else { // chenged to internal this.externalTextVersion.setDeleted(); if (this.internalTextVersion.resource.isInStore()) { this.internalTextVersion.setUndeleted() } this.internalTextVersion.checkOut(true); this.externalTextVersion.checkOut(false); } this.changedExternity = !this.changedExternity; } changeWholePage(textUnit: TextUnit, truthVal: boolean) { this.internalTextVersion.checkedOutTextUnit().setWholePage(truthVal); const idx = this.internalTextVersion.getGuiTextUnitIdxByIri(textUnit.iri()); - let predicate = this.internalTextVersion.childsPredicate; + const predicate = this.internalTextVersion.childsPredicate; // If wholePage Changes we have to change the the list statements because of the ontology: if (truthVal) { // If wholePage = true, there is the page iri to store in the list // resetting link to child this.internalTextVersion.resource._rdfDatasetToEdit.setStatementInListAsStatement(predicate, textUnit._belongsToPage, idx); // setting wholePage of child //this.internalTextVersion.childResources[idx].setWholePage(truthVal) } else { // resetting link to child this.internalTextVersion.resource._rdfDatasetToEdit.setStatementInListAsStatement(predicate, textUnit.iri(), idx); } //console.log('changing wholePage ', textUnit.iri(), truthVal) //this.internalTextVersion.childResources[idx].setWholePage(truthVal); } // simply set everything to deleted - no matter if in use or not onDelete() { this.setDeleted(); this.internalTextVersion.setDeleted(); this.externalTextVersion.setDeleted(); } unDelete() { this.setUndeleted(); if (this.isExternalResource) { this.externalTextVersion.setUndeleted(); } else { this.internalTextVersion.setUndeleted(); } } hasChangedExternity(): boolean { return this.changedExternity; } } export class InternalTextVersion extends CrossRefEditorElement{ childResources: GuiTextUnit[] = []; _pagesCollection: TlnCrossrefPage[]= []; constructor(iri: string, label?: string, parentIri?: string, checkOut = false) { // iri?: string, label?: string, parentIri?: string, checkOut = false super(iri, label, parentIri, checkOut); const ranges = new Set(); ranges.add('http://www.w3.org/1999/02/22-rdf-syntax-ns#List'); this.childsPredicate = new TlnPredicate('http://www.nie.org/ontology/nietzsche#identifiesAsVersion', ranges); } // builds an internalTextVersion public static async buildAsyncFromStore(qService: QueryService, iri: string, parentIri?: string): Promise { - let intVersion = new InternalTextVersion(iri, 'Interne TextVersion', parentIri); + const intVersion = new InternalTextVersion(iri, 'Interne TextVersion', parentIri); intVersion.resource = await TlnResource.buildFromStoreAsync(qService, iri, 'TextVersion'); - let children = await intVersion.resource.getChildIris(intVersion.childsPredicate.iri()); + const children = await intVersion.resource.getChildIris(intVersion.childsPredicate.iri()); if (!children || children.length === 0 ) {return intVersion} // guard children.forEach((child, index) => { GuiTextUnit.buildAsyncFromStore(qService, child, 'Texteinheit', parentIri).then( guiTextUnit => { - intVersion._pagesCollection.splice(index,0, guiTextUnit.pageData); // adding/changing _pagescollection + intVersion._pagesCollection.splice(index,0, guiTextUnit.textUnit.pageData); // adding/changing _pagescollection intVersion.childResources.splice(index, 0, guiTextUnit); }); } ); return intVersion; } - public static async buildNew(iri: string, label?: string, parentIri?: string, checkout = true) { - let intVersion = new InternalTextVersion(iri, label, parentIri, checkout); - return intVersion; - } - guiTextUnits(): GuiTextUnit[] { return this.childResources; } hasTextUnits(): boolean { return this.guiTextUnits().length > 0; } async getPagesCollection() { - let pagesCollection: TlnCrossrefPage[] = []; + const pagesCollection: TlnCrossrefPage[] = []; for (let unit of this.guiTextUnits()) { - pagesCollection.push(unit.pageData); + pagesCollection.push(unit.textUnit.pageData); } return pagesCollection; } // overwrites an existig textUnit or simply adds it depending on the delete // to _pages, _versions, and to the statements setTextUnit(guiTextUnit: GuiTextUnit, index) { - this._pagesCollection.splice(index,1, guiTextUnit.pageData); // changing _pagescollection + this._pagesCollection.splice(index,1, guiTextUnit.textUnit.pageData); // changing _pagescollection this.childResources.splice(index, 1, guiTextUnit); this.resource._rdfDatasetToEdit.setStatementInListAsStatement(this.childsPredicate, guiTextUnit.iri(), index); } // adds a new guiTextUnit addNewGuiTextUnit(guiTextUnit: GuiTextUnit, index?: number) { if (index === null || index === undefined) {index = this.childResources.length} // if no index passed it will be added at the end // setting the list entry at the textVersion - this._pagesCollection.splice(index,0, guiTextUnit.pageData); // adding/changing _pagescollection + this._pagesCollection.splice(index,0, guiTextUnit.textUnit.pageData); // adding/changing _pagescollection this.childResources.splice(index, 0, guiTextUnit); this.resource._rdfDatasetToEdit.putStatementInListAsStatement(this.childsPredicate, guiTextUnit.iri(), index); } deleteGuiTextUnit(textUnitIri: string) { const idx = this.childResources.findIndex(tUnit => tUnit.iri() === textUnitIri); this._pagesCollection[idx].deleted = true; this.childResources[idx].setDeleted(); this.childResources[idx].textUnit.setDeleted(); // deleting also the statement connecting the textUnit this.resource._rdfDatasetToEdit.deleteAllStatementsWithObject(textUnitIri); } unDeleteGuiTextUnit(textUnitIri: string) { const idx = this.childResources.findIndex(tUnit => tUnit.iri() === textUnitIri); this._pagesCollection[idx].deleted = false; this.childResources[idx].setUndeleted(); this.childResources[idx].textUnit.setUndeleted(); // resetting also the statement connecting the textUnit this.resource._rdfDatasetToEdit.putStatementInListAsStatement(this.childsPredicate, textUnitIri, idx) } // Todo: 1) Refactor so everything is in superclass; 2) remove children if not needed except onInit, remove pagesCollection if same moveTextUnitInArray(previousIndex: number, newIndex: number) { - console.log('HERE MOVING'); moveItemInArray(this.childResources, previousIndex, newIndex); moveItemInArray(this._pagesCollection, previousIndex, newIndex); this.resource._rdfDatasetToEdit.moveObjectInListAsStatement(this.childsPredicate.iri(), previousIndex, newIndex); - - console.log('storeData', this.resource._rdfDatasetFromStore.listsAsStatements()) } getGuiTextUnitIdxByIri(iri: string) { return this.guiTextUnits().findIndex(tUnit => tUnit.iri() === iri); } getTextUnitByIri(iri: string) { const idx = this.getGuiTextUnitIdxByIri(iri); return this.childResources[idx]; } checkedOutTextUnit(): GuiTextUnit { return this.childResources[this.checkedOutTextVersionIdx()]; } checkedOutTextVersionIdx(): number { return this.childResources.findIndex(tUnit => tUnit.isCheckedOut()); } - checkOutTextUnit(iri: string):GuiTextUnit { + checkOutTextUnit(iri: string): GuiTextUnit { let idx = this.childResources.findIndex(t => t.iri() === iri); for (let i = 0; i <= this.childResources.length-1; i++) { if (i === idx) { this.childResources[i].checkOut(true); } else { this.childResources[i].checkOut(false); } } return this.childResources[idx]; } // simple check for completeness - isComplete(){ + isComplete() { return true; //return (this.textUnits().length && this.textUnits().every(u => // u.startLine() !== undefined && u.endLine() !== undefined || u.wholePage )); } } export class ExternalTextVersion extends CrossRefEditorElement{ hasUrl: string; hasTitle: string; hasTitleP: TlnPredicate; hasUrlP: TlnPredicate; constructor(iri?: string, label?: string, parentIri?: string, checkOut = false ) { super(iri, label, parentIri, checkOut); this.hasTitleP = new TlnPredicate('http://www.nie.org/ontology/nietzsche#textUnitHasTitle', new Set(['http://www.w3.org/2001/XMLSchema#string'])); this.hasUrlP = new TlnPredicate('http://www.nie.org/ontology/nietzsche#textUnitHasUrl', new Set(['http://www.w3.org/2001/XMLSchema#anyURI'])); } public static async buildAsyncFromStore(qService: QueryService, iri: string, parentIri): Promise { let extVersion = new ExternalTextVersion(iri, 'TextVersion', parentIri); extVersion.resource = await TlnResource.buildFromStoreAsync(qService, iri, 'Externe Textversion'); extVersion.hasTitle = extVersion.resource.rdfDatasetFromStore().getFirstObject('http://www.nie.org/ontology/nietzsche#textUnitHasTitle'); extVersion.hasUrl = extVersion.resource.rdfDatasetFromStore().getFirstObject('http://www.nie.org/ontology/nietzsche#textUnitHasUrl'); return extVersion; } // simple check for basic completeness isComplete(){ return this.hasUrl && this.hasTitle } setTitle(title: string) { this.resource._rdfDatasetToEdit.setStatement(this.hasTitleP, title, 0) } setUrl(url: string) { this.resource._rdfDatasetToEdit.setStatement(this.hasUrlP, url, 0) } } // wrapper class export class GuiTextUnit extends CrossRefEditorElement{ textUnit: TextUnit; wholePage: boolean; deleted = false; - pageData: TlnCrossrefPage; // iri?: string, label?: string, parentIri?: string, checkOut = false, parentLabel?: string - constructor(iri, label: string, parentIri?: string, checkOut = true, wholePage = true, parentLabel?: string, pageData?: TlnCrossrefPage) { + constructor(iri, label: string, parentIri?: string, checkOut = true, wholePage = true, parentLabel?: string) { super(iri, label, parentIri, checkOut); this.wholePage = wholePage; this.parentLabel = parentLabel; - this.pageData = pageData; - //this.resource = new TlnResource(iri, 'TextUnit', 'http://www.nie.org/ontology/nietzsche#PartOfPageTextUnit', parentIri, parentLabel); this.displayedPredicates = ['http://www.nie.org/ontology/nietzsche#belongsToPage']; // } // async builder static async buildAsyncFromStore(qService: QueryService, iri: string, label, parentIri?: string) { - let guiTextUnit: GuiTextUnit = new GuiTextUnit(iri, label, parentIri, false); - // in case the thing is a textUnit we like to know the page it belongs to - if it is a page, we need nothing but the iri itself - let stmDef = new ChangeMgmntDef(); // Todo: maybe whitelist/blacklist - // set the resource of our guiTextUnit: only to know what the thing actually is! - guiTextUnit.resource = await TlnResource.buildFromStoreAsync(qService, iri, 'Texteinheit', stmDef); - if (guiTextUnit.rdfTypeInStore() === 'http://www.nie.org/ontology/nietzsche#PartOfPageTextUnit') { - const pageIri = guiTextUnit.resource.rdfDatasetFromStore().getFirstObject('http://www.nie.org/ontology/nietzsche#belongsToPage'); - guiTextUnit.pageData = await TlnCrossrefPage.buildFromStoreAsync(qService, pageIri); - guiTextUnit.textUnit = await TextUnit.buildFromStoreAsync(qService, iri, guiTextUnit.pageData.lineData); + const guiTextUnit: GuiTextUnit = new GuiTextUnit(iri, label, parentIri, false); + if (!await GuiTextUnit.isPageAysnc(qService, iri)) { // if the thing is a textUnit + guiTextUnit.textUnit = await TextUnit.buildFromStoreAsync(qService, iri); guiTextUnit.wholePage = false; // default is true, so we have to set it here } - if (guiTextUnit.rdfTypeInStore() === 'http://www.nie.org/ontology/nietzsche#Page') { + if (await GuiTextUnit.isPageAysnc(qService, iri)) { // if the thing is a page // the thing is a page, the iri passed a pages iri, but we already add a new textUnit - // in every case so it can be switched easily to the already created one - guiTextUnit.pageData = await TlnCrossrefPage.buildFromStoreAsync(qService, iri); - guiTextUnit.textUnit = TextUnit.createNewTextUnit(guiTextUnit.pageData.pageIri, guiTextUnit.pageData.lineData, parentIri); + // in every case so it can be switched easily to that created one + const pageData = await TlnCrossrefPage.buildFromStoreAsync(qService, iri); + guiTextUnit.textUnit = TextUnit.createNewTextUnit(pageData.pageIri, pageData, parentIri); guiTextUnit.textUnit.setDeleted(); // set the textUnit to deleted hence there is no textUnit if it is wholepage } return guiTextUnit; } + static async createNewGuiTextUnit(qService, pageIri, label, parentIri, checkOut = true) { + const guiTextUnit: GuiTextUnit = new GuiTextUnit(pageIri, label, parentIri, checkOut); + const pageData = await TlnCrossrefPage.buildFromStoreAsync(qService, pageIri); + guiTextUnit.textUnit = TextUnit.createNewTextUnit(pageIri, pageData, parentIri); + guiTextUnit.textUnit.setDeleted(); // set the textUnit to deleted hence there is no textUnit if it is wholepage + return guiTextUnit; + } + + static async isPageAysnc(qService, iri) { + const type = await RdfStoredData.getRdfTypeFromStore(qService, iri); + return type === 'http://www.nie.org/ontology/nietzsche#Page'; + } + // resets the wholePage boolean and changes the iri according to the chosen state (wholepage means that there is not a textUnit - // it is a page) setWholePage(truthVal: boolean) { - console.log() this.wholePage = truthVal; if (truthVal) { // if wholePage === true then there is only a link to the page and no textUnit at all. So we set the iri to the pages Iri and the // textUnit to deleted - this._iri = this.pageData.pageIri; + this._iri = this.textUnit.pageData.pageIri; this.textUnit.setDeleted(); } else { this._iri = this.textUnit.iri(); this.textUnit.setUndeleted(); } - } } export class TextUnit extends CrossRefEditorElement { _belongsToPage: string; private _startLine?: string; private _endLine?: string; - lineData: Line[]; + pageData: TlnCrossrefPage; displayedLabel: string; displayedStartLine: number; displayedEndLine: number; selectedLines: string[]; startLinePredicate: TlnPredicate; endLinePredicate: TlnPredicate; belongsToPagePredicate: TlnPredicate; constructor(iri: string, - label:string, - lineData: Line[], + label: string, + pageData?: TlnCrossrefPage, parentIri: string = '', checkOut = false, parentLabel?: string, startLine?: string, endLine?: string, startLineNumber?: number, endLineNumber?: number) { super(iri, label, parentIri, checkOut, parentLabel); - //this.rdfsType= 'http://www.nie.org/ontology/nietzsche#PartOfPageTextUnit'; - this.startLinePredicate = new TlnPredicate('http://www.nie.org/ontology/nietzsche#startLine', new Set(['http://www.w3.org/2001/XMLSchema#anyURI'])); - this.endLinePredicate = new TlnPredicate('http://www.nie.org/ontology/nietzsche#endLine', new Set(['http://www.w3.org/2001/XMLSchema#anyURI']) ); - this.belongsToPagePredicate = new TlnPredicate('http://www.nie.org/ontology/nietzsche#belongsToPage', new Set(['http://www.w3.org/2001/XMLSchema#anyURI'])) - this.lineData = lineData; + // this.rdfsType= 'http://www.nie.org/ontology/nietzsche#PartOfPageTextUnit'; + this.startLinePredicate = new TlnPredicate('http://www.nie.org/ontology/nietzsche#startLine', + new Set(['http://www.w3.org/2001/XMLSchema#anyURI'])); + this.endLinePredicate = new TlnPredicate('http://www.nie.org/ontology/nietzsche#endLine', + new Set(['http://www.w3.org/2001/XMLSchema#anyURI']) ); + this.belongsToPagePredicate = new TlnPredicate('http://www.nie.org/ontology/nietzsche#belongsToPage', + new Set(['http://www.w3.org/2001/XMLSchema#anyURI'])) + this.pageData = pageData; this._startLine = startLine; this._endLine = endLine; this.selectedLines = []; this.setLines(startLine, startLineNumber, endLine, endLineNumber); } - static async buildFromStoreAsync(qService: QueryService, iri, lineData: Line[]) { - let textUnit = new TextUnit(iri, 'Texteinheit', lineData ); + static async buildFromStoreAsync(qService: QueryService, iri) { + const textUnit = new TextUnit(iri, 'Texteinheit' ); textUnit.resource = await TlnResource.buildFromStoreAsync(qService, iri); textUnit._belongsToPage = textUnit.resource.rdfDatasetFromStore().getFirstObject(textUnit.belongsToPagePredicate.iri()); + textUnit.pageData = await TlnCrossrefPage.buildFromStoreAsync(qService, textUnit._belongsToPage); textUnit._startLine = textUnit.resource.rdfDatasetFromStore().getFirstObject(textUnit.startLinePredicate.iri()); textUnit.displayedStartLine = textUnit.getLineNumber(textUnit._startLine); textUnit._endLine = textUnit.resource.rdfDatasetFromStore().getFirstObject(textUnit.endLinePredicate.iri()); textUnit.displayedEndLine = textUnit.getLineNumber(textUnit._endLine); textUnit.setSelectedLines(textUnit._startLine, textUnit._endLine); return textUnit; } // creates a new TextUnit which is not yet in store - static createNewTextUnit(pageIri: string, lineData: Line[], parentIri: string) { - let textUnit = new TextUnit('', 'Texteinheit', lineData, parentIri); + static createNewTextUnit(pageIri: string, pageData: TlnCrossrefPage, parentIri: string) { + const textUnit = new TextUnit('', 'Texteinheit', pageData, parentIri); textUnit._belongsToPage = pageIri; - let a = new TlnPredicate('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); + const a = new TlnPredicate('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); textUnit.resource._rdfDatasetToEdit.statements().putAgentStatement(a, 'http://www.nie.org/ontology/nietzsche#PartOfPageTextUnit'); textUnit.resource._rdfDatasetToEdit.statements().putAgentStatement(textUnit.belongsToPagePredicate, pageIri); return textUnit; } // returns the lineNumber for a given lineIri getLineNumber(lineIri: string) { - return this.lineData[this.lineData.findIndex(l => l.line === lineIri)].lNumber; + return this.pageData.lineData[this.pageData.lineData.findIndex(l => l.line === lineIri)].lNumber; } setLines(sLine: string, sLineNo: number, eLine: string, eLineNo: number){ if (sLine && sLineNo && eLine && eLineNo) { this.setStartLine(sLine, sLineNo); this.setEndLine(eLine, eLineNo); this.setSelectedLines(sLine, eLine); } } startLine() { return this._startLine } hasStartLine() { return this._startLine !== undefined && this._startLine !== null } endLine() { return this._endLine } hasEndLine() { return this._endLine !== undefined && this._endLine !== null } // sets the selected lines for highlighting/passing to tln-page-view only public setSelectedLines(startLine: string, endLine?: string) { if (!endLine || endLine === undefined) { this.selectedLines = [this.startLine()] } else { let started = false; let ended = false; - this.lineData.forEach(line => { + this.pageData.lineData.forEach(line => { if (line.line === startLine) { started = true; } if (line.line === endLine) { this.selectedLines.push(line.line); ended = true; } if (started && !ended) { this.selectedLines.push(line.line); } }); } } // sets or resets the startLine depending on its existance setStartLine(startLine: string, lineNumber: number) { this._startLine = startLine; this.displayedStartLine = lineNumber; this.setSelectedLines(this.startLine()); if (this.endLine()) {this.deleteEndLine();}// resetting the startline must delete the selected endline if (this.resource._rdfDatasetToEdit.hasStatementEntriesForPredicate(this.startLinePredicate.iri())) { this.resource._rdfDatasetToEdit.setStatement(this.startLinePredicate, startLine, 0); } else {this.resource._rdfDatasetToEdit.putStatement(this.startLinePredicate, startLine);} } setEndLine(endLine: string, lineNumber: number) { this._endLine = endLine; this.displayedEndLine = lineNumber; this.setSelectedLines(this.startLine(), endLine); if (this.resource._rdfDatasetToEdit.hasStatementEntriesForPredicate(this.endLinePredicate.iri())) { this.resource._rdfDatasetToEdit.setStatement(this.endLinePredicate, endLine, 0); } else {this.resource._rdfDatasetToEdit.putStatement(this.endLinePredicate, endLine);} } deleteEndLine() { this._endLine = null; this.displayedEndLine = null; } } export interface Line { line: string; lNumber: number; } export class TlnCrossrefPage { lineData: Line[]; deleted = false; constructor(public pageIri: string, public pageNumber?: Number, public thumb?: SafeUrl, public svg?: SafeUrl) { } // async builder public static async buildFromStoreAsync(qService: QueryService, pageIri: string): Promise { const query = QueryService.bindVariableWithIri(PAGE_PREVIEW_RQ, 'page', pageIri); - let crossRefPage = await qService.getData(BASEURL, query,'SELECT') + const crossRefPage = await qService.getData(BASEURL, query,'SELECT') .then( pageData => { let page = pageData['results']['bindings'][0]; let pageNumber = page['pageNumber'] && page['pageNumber']['value']? page['pageNumber']['value']: ''; let thumb = page['thumb'] && page['thumb']['value']? page['thumb']['value']: ''; let svg = page['svgUrl'] && page['svgUrl']['value']? page['svgUrl']['value']: ''; return new TlnCrossrefPage(pageIri, pageNumber, thumb, svg); }); crossRefPage.lineData = await TlnCrossrefPage.getLineData(pageIri, qService); return crossRefPage; } // sets the lineData available for the checked out textUnit public static async getLineData(pageIri: string, qService: QueryService) { - let lines = await TlnCrossrefPage.getLinesFromStore(pageIri, qService); + const lines = await TlnCrossrefPage.getLinesFromStore(pageIri, qService); return lines.map(line => { return {line: line['line']['value'], lNumber: line['lNumber']['value']} }); } public static async getLinesFromStore(pageIri: string, qService: QueryService): Promise { const query = await qService.parametrizeWhereClauseWithItems(RQ_CROSSREF_TREE_LINES, pageIri, '', ''); return await qService.getData(BASEURL, query, 'SELECT').then(res => { return res['results']['bindings']; }); } } export const PAGE_PREVIEW_RQ = ` PREFIX tln: PREFIX rdf: PREFIX xsd: SELECT DISTINCT ?page ?pageNumber ?image ?svgUrl ?thumb WHERE { ?page ?p ?o. OPTIONAL { ?page tln:hasNumber ?pageNumber. ?page tln:hasSvgImage ?svg. ?svg tln:hasPrimaryurl ?svgUrl. ?page tln:hasFaksimileImage ?image. ?image tln:hasFileName ?imgLabel; tln:hasPrimaryurl ?primaryUrl; tln:hasThumburl ?thumb; } }`; diff --git a/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/line-selector-component/line-selector-component.component.ts b/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/line-selector-component/line-selector-component.component.ts index 51278a6..7282357 100644 --- a/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/line-selector-component/line-selector-component.component.ts +++ b/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/line-selector-component/line-selector-component.component.ts @@ -1,50 +1,55 @@ import {Component, OnChanges, forwardRef, Inject} from '@angular/core'; import {CrossrefEditorDataServiceService, GuiTextUnit, TextUnit} from '../../crossref-editor-data-service.service'; import {TlnQueryServiceInterface} from '../../../tln-edition/models'; import {NavigationServiceService} from '../../../services/navigation-service.service'; import {MatButtonToggleChange} from '@angular/material'; import {NG_VALUE_ACCESSOR} from '@angular/forms'; import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; @Component({ selector: 'app-line-selector-component', templateUrl: './line-selector-component.component.html', styleUrls: ['./line-selector-component.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: forwardRef(() => LineSelectorComponentComponent), } ] }) export class LineSelectorComponentComponent implements OnChanges { // subscribe to page selections linesOfPages: Map; // The page data with all the lines // For passing into TlnPageView ... max_width: number; queryService: TlnQueryServiceInterface; preferPrimaryUrl = true; constructor(private naviService: NavigationServiceService, private dataService: CrossrefEditorDataServiceService, private dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public guiTextUnit: GuiTextUnit) { this.linesOfPages = new Map(); - this.linesOfPages.set(this.guiTextUnit.pageData.pageIri,this.guiTextUnit.pageData.lineData); + this.setLinesOfPages(this.guiTextUnit.textUnit.pageData); + this.linesOfPages.set(this.guiTextUnit.textUnit.pageData.pageIri, this.guiTextUnit.textUnit.pageData.lineData); this.max_width = window.innerWidth/1.5; } ngOnChanges() { } public changeWholePage({ value }: MatButtonToggleChange) { const isTrue = (value === 'true'); this.dataService.changeWholePage(this.guiTextUnit.textUnit, isTrue); } + setLinesOfPages(pageData) { + console.log(pageData); + } + closeDialog() { this.dialogRef.close(); } } diff --git a/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/page-collector-component/page-collector-component.component.html b/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/page-collector-component/page-collector-component.component.html index 026b2a1..ba614b0 100644 --- a/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/page-collector-component/page-collector-component.component.html +++ b/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/page-collector-component/page-collector-component.component.html @@ -1,59 +1,59 @@

Ziehen Sie die Manuskriptseiten der Textversion von der Navigationsleiste links per Drag'n drop in das Feld "Texteinheiten". Die Reihenfolge der erfassten Seiten lässt sich ebenfalls per Drag'n drop ändern.

{{internalTextVersion.iri()}}
Seiten/Texteinheiten check * - Texteinheit S. {{unit.pageData.pageNumber}}: + Texteinheit S. {{unit.textUnit.pageData.pageNumber}}: (gesamte Seite) Zeilen {{unit.textUnit.displayedStartLine}}-{{unit.textUnit.displayedEndLine}}
- - + +
{{unit.iri()}}
hier Seiten ablegen ...
diff --git a/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/page-collector-component/page-collector-component.component.ts b/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/page-collector-component/page-collector-component.component.ts index 54e403a..5b63ccb 100644 --- a/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/page-collector-component/page-collector-component.component.ts +++ b/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/page-collector-component/page-collector-component.component.ts @@ -1,92 +1,92 @@ import {Component, Input, OnChanges} from '@angular/core'; import {CdkDragDrop} from '@angular/cdk/drag-drop'; -import {NavigationEntity, TlnEntity} from '../../../models/models'; +import {NavigationEntity} from '../../../models/models'; import {NavigationServiceService} from '../../../services/navigation-service.service'; import {ActivatedRoute} from '@angular/router'; import { CrossrefEditorDataServiceService, InternalTextVersion, GuiTextVersion, TlnCrossrefPage } from '../../crossref-editor-data-service.service'; import {TlnQueryServiceInterface} from '../../../tln-edition/models'; import {MatDialog} from '@angular/material/dialog'; import {LineSelectorComponentComponent} from '../line-selector-component/line-selector-component.component'; @Component({ selector: 'app-page-collector-component', templateUrl: './page-collector-component.component.html', styleUrls: ['./page-collector-component.component.scss'] }) export class PageCollectorComponentComponent implements OnChanges { @Input() instanceIdx: number; // the number of the instance @Input() internalTextVersion: InternalTextVersion; @Input() pages: TlnCrossrefPage[] = []; instanceId: string; // lines ... linesOfPages: Map; // The page data with all the lines // For passing to TlnPageView ... max_width: number; queryService: TlnQueryServiceInterface; preferPrimaryUrl = true; // selected start and endLine ... startLine: string; endLine: string; constructor(private naviService: NavigationServiceService, private activatedRoute: ActivatedRoute, private dataService: CrossrefEditorDataServiceService, public dialog: MatDialog) { this.linesOfPages = new Map(); } ngOnChanges() { this.instanceId = `crossref-editor-drop-list-${this.instanceIdx}`; - if ( this.internalTextVersion.hasTextUnits() ){ + if ( this.internalTextVersion.hasTextUnits() ) { this.internalTextVersion.guiTextUnits().forEach(tUnit => { - if (!tUnit.wholePage && tUnit.textUnit && tUnit.textUnit._belongsToPage){ - this.linesOfPages.set(tUnit.textUnit._belongsToPage,tUnit.textUnit.lineData) + if (!tUnit.wholePage && tUnit.textUnit && tUnit.textUnit._belongsToPage) { + this.linesOfPages.set(tUnit.textUnit._belongsToPage, tUnit.textUnit.pageData.lineData); } }); } - this.max_width = window.innerWidth/2; + this.max_width = window.innerWidth / 2; } itemDropped(event: CdkDragDrop) { if (event.previousContainer === event.container) { this.dataService.movePageInArray(event.previousIndex, event.currentIndex) } else { this.dataService.onAddingPage(event.item.data, event.currentIndex); } } removeFromCollection(itemToRemove: string) { this.dataService.onRemovePage(itemToRemove); } unDelete(iri: string) { this.dataService.undeleteGuiTextUnit(iri); } openLineSelectorDialog(textUnitIri): void { this.checkoutTextUnit(textUnitIri); console.log('opening dialoge with ', this.internalTextVersion.checkedOutTextUnit()) const dialogRef = this.dialog.open(LineSelectorComponentComponent, { width: '90vw', height: '90vh', data: this.internalTextVersion.checkedOutTextUnit() }); dialogRef.afterClosed().subscribe(result => { console.log('The dialog was closed'); }); } checkoutTextUnit(textUnitIri) { this.dataService.textGenesis.checkedOutTextVersion().internalTextVersion.checkOutTextUnit(textUnitIri); } } diff --git a/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/text-genesis-editor-component/text-genesis-editor.component.html b/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/text-genesis-editor-component/text-genesis-editor.component.html index cb0024c..2e2a617 100644 --- a/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/text-genesis-editor-component/text-genesis-editor.component.html +++ b/nietzsche-beta-app/src/app/crossref-editor-component/text-genesis-editor/text-genesis-editor-component/text-genesis-editor.component.html @@ -1,113 +1,113 @@ Textgenese: {{textGenesis.iri()}} {{textUnit.displayedLabel}}: {{textUnit.displayedLabel}} {{textUnit.displayedStartLine}}-{{textUnit.displayedEndLine}} ; -> {{tVersion.externalTextVersion.hasTitle}} (ext. Resource)
{{i+1}}. Textversion: - {{textUnit.pageData.pageNumber}} + {{textUnit.pageData.pageNumber}} : {{textUnit.displayedStartLine}}-{{textUnit.displayedEndLine}} ; (extern): {{textVersion.externalTextVersion.hasTitle}}
{{textVersion.isCheckedOut()? '' : 'Panel ausklappen zum Bearbeiten ...'}}
Interne Textversion Externe Textversion
{{delTextVersion.iri()}}
{{textGenesis.ttl_export}} diff --git a/nietzsche-beta-app/src/app/rdf-editor-module/editor-resources.ts b/nietzsche-beta-app/src/app/rdf-editor-module/editor-resources.ts index 9b99df3..3774582 100644 --- a/nietzsche-beta-app/src/app/rdf-editor-module/editor-resources.ts +++ b/nietzsche-beta-app/src/app/rdf-editor-module/editor-resources.ts @@ -1,588 +1,592 @@ // The main super class for every thing which has to be added/edited or deleted in the triple store import {StatementHandler, ChangeMgmntDef, TlnStatementCollection, OnDelete, StatementOperations} from './statement-handler'; import {QueryService} from '../services/query.service'; import {RdfData, RdfEditorData, RdfStoredData} from './rdf-editor-DataSets'; import * as N3 from "node_modules/n3/src"; export class TlnResource { iri: string; label: string; _rdfDatasetFromStore: RdfStoredData; // the data from store if there is such a thing _rdfDatasetToEdit: RdfEditorData; // the data which is edited or created _deprecatedVersions: DeprecatedVersion[]; // all previous depricated versions of the resource parentIri: string; parentLabel: string; private _inStore: boolean; // exists in Store or in memory/JS only private _deprecated: boolean; // states that a resource has either been edited or deleted. private _edited: boolean; // whether an existing thing has been edited or not private _deleted: boolean; // whether a thing has been deleted in the frontend constructor(iri: string, label?: string, rdfsType?: string, parentIri?: string, parentLabel?: string) { this.iri = iri; this._inStore = false; this._deleted = false; this._edited = false; this._deprecated = false; this._rdfDatasetToEdit = new RdfEditorData(this.iri, rdfsType); this.parentIri = parentIri; this.parentLabel = parentLabel; } -// loads an existing resource (or a predicate) from strore +// loads an existing resource (or a predicate) from store static async buildFromStoreAsync( queryService: QueryService, existingIri: string, - label?:string, + label?: string, editorDef?: ChangeMgmntDef ){ - let resource = new TlnResource(existingIri, label); - resource._rdfDatasetFromStore = await RdfStoredData.buildAsync(queryService, existingIri, editorDef); - resource._inStore = await resource._rdfDatasetFromStore.askIfInStore(existingIri); - resource._rdfDatasetToEdit = await resource._rdfDatasetFromStore.exportAsEditorData(editorDef); - if (resource._rdfDatasetFromStore.statements() === resource._rdfDatasetToEdit.statements()) {console.log('shiiiit not be the same')} + const resource = new TlnResource(existingIri, label); + resource._inStore = await RdfStoredData.askIfInStore(queryService, existingIri); + if (resource._inStore) { + resource._rdfDatasetFromStore = await RdfStoredData.buildAsync(queryService, existingIri, editorDef); + resource._rdfDatasetToEdit = await resource._rdfDatasetFromStore.exportAsEditorData(editorDef); + if (resource._rdfDatasetFromStore.statements() === resource._rdfDatasetToEdit.statements()) {console.log('shiiiit not be the same')} + } else { + console.error('resource ', existingIri, ' not found in store'); + } return resource; } // returns the iri's of all children depending on a given childsPredicate async getChildIris(childsPredicate: string): Promise { let childIris = []; // guard if childspredicate is not available or there are no statements for the thing if (!this._rdfDatasetToEdit.isPredicateAvailable(childsPredicate)) { return childIris } // else we get the childIris if (this._rdfDatasetToEdit.availablePredicates().get(childsPredicate).takesListAsObject()) { // get children out of a list as object if (this._rdfDatasetToEdit.listsAsStatements().size === 0) { return childIris } // another guard childIris = this._rdfDatasetToEdit.listsAsStatements().get(childsPredicate).getNodes().map(node => node.value); } else { // get children from statements if (this._rdfDatasetToEdit.agentStatements().size === 0) { return childIris } // another guard childIris = this._rdfDatasetToEdit.agentStatements().get(childsPredicate).getNodes().map(node => node.value); } return childIris; } isInStore() { return this._inStore; } isEdited() { return this._edited; } ignore() { // if the resource is not in the store and is set to deleted it must not be processed return (!this._inStore && this._deleted); } isDeleted() { return this._deleted; } rdfDatasetFromStore() { return this._rdfDatasetFromStore; } // creates a deprecated data set from the original dataset - we only deprecate existing things in store public async deprecate(timestamp: string, def: ChangeMgmntDef): Promise { if (!this._inStore) { return null } // guard if (!this._deleted) { // if it is not deleted, we have to deprecate the thing against the new one return await TlnDeprecation.buildAsync(this._rdfDatasetFromStore, timestamp, def, this._rdfDatasetToEdit) } else { // it has been deleted, so we just depricate the whole thing without passing a new version return await TlnDeprecation.buildAsync(this._rdfDatasetFromStore, timestamp, def) } } // Marks the TlnResource as deleted and starts deprecation of that resource with all its properties public setDeleted() { this._deleted = true; } public setUndeleted() { this._deleted = false; } // returns all statements of a thing as n3.Quad[]; // First getting all outgoing/agent statements. patientStatements are not needed, hence they are in the parent and handled by deprecation: // if // (properties with object) as n3 quad array in which there are both: the usual spo as well as a list containing all o's // public getAllAgentStatementQuads(): N3.Quad[] { // let quads: N3.Quad[] = this._rdfDatasetToEdit.statements().agentStmAsQuads(this._rdfDatasetToEdit.iri()) // getting all outgoing statements as quads // console.log(quads); // return quads; // } // // // returns the new thing with all its poperties & objects as N3-quads/lists // async buildRdfTransaction(): Promise { // // get all agent statements // let quads: N3.Quads[] = [].concat(this.getAllAgentStatementQuads()); // concadinating with [] so never undefined // // Sometimes there is none e.g. an existing page is part of identifiesAsVersion so we don't have to create iris & properties // // TODO get selfStatement from statementHandler - and get rid of the variable this.selfstatement // if (this._rdfDatasetToEdit.selfStatement() !== undefined && this._rdfDatasetToEdit.selfStatement() !== null) { // quads.splice(0,0, this._rdfDatasetToEdit.selfStatement()); // } // return quads; // } // get all deprecated versionsof a given resource iri async getDeprecations(queryService: QueryService, iri: string): Promise { let deprecations: DeprecatedVersion[]; let iris: string[] = []; // the versions iri's // Todo: get versions by query for (const iri of iris) { let formerVeersion: DeprecatedVersion = await DeprecatedVersion.buildFromStoreAsync(queryService, iri); deprecations.push(formerVeersion) } return deprecations; } async getDeprecatedResources(resourceIri: string) { } undeprecate(iri: string) { // Todo: pass resource iri or verion iri? -> gui decision let resource = this._deprecatedVersions[this._deprecatedVersions.findIndex(version => version.version.iri === iri)]; // map resource (a tln resource) to what it is, i.e. // populate all edcitorData with the data needed // populate cascading! so if a Version is populated, also its textUnits must be restored/undeprecated! // => get child from deprecatedChild??? // if someOne saves it // ... } } // for undeprecating export class DeprecatedVersion { version: TlnResource; resource: TlnResource; constructor() { } static async buildFromStoreAsync(queryService, iri) { let version = new DeprecatedVersion(); version.version = await TlnResource.buildFromStoreAsync(queryService,iri, 'Version'); const resourceIri = version.version._rdfDatasetFromStore.getFirstObject('http://www.nie.org/ontology/nietzsche#deprecates'); version.resource = await TlnResource.buildFromStoreAsync(queryService,resourceIri, 'Resource'); return version; } } // a canvas for a resource with basic funcionality/features, i.e. only an rdf represenation of a resource with its agent statements. // Used for not yet existing things which have to be added to the store when a deprecation takes place. export class TlnShortHandResource { protected _iri: string; protected _rdfType: string; protected _statements: N3.Quad[] = []; // all the statements of a given thing as agent, e.g. subject constructor() { } // puts a deprecated statement putNamedNodeStatement(predicate: string, o: string ) { this._statements.push(RdfData.createNamedNodeQuad(this._iri, predicate, o)) } // puts a deprecated statement putLiteralStatement(predicate: string, o: string, standOffType?: string ) { let quad = RdfData.createLiteralStatement(this._iri, predicate, o, standOffType); this._statements.push(quad); } iri() { return this._iri; } public statements() { return this._statements; } } // The dep:classes export class TlnDeprecatedAgentStatement extends TlnShortHandResource{ constructor(predicate: string, node: N3.Literal|N3.NamedNode) { super(); // always put basic props this._rdfType = 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#AgentStatement'; this._iri = RdfData.createIri('DepAgentStm'); this.putNamedNodeStatement('http://www.w3.org/1999/02/22-rdf-syntax-ns#type', this._rdfType); this.putLiteralStatement('https://nietzsche.philhist.unibas.ch/ontology/deprecation#hasProperty', predicate, '^^http://www.w3.org/2001/XMLSchema#anyURI'); // the node itsel will be written as ... if ( node instanceof N3.Literal){ // a anyUri if the thing "was" a namednode ... this.putLiteralStatement('https://nietzsche.philhist.unibas.ch/ontology/deprecation#hasObject', node.value, `^^${node.datatypeString}`); } if (node instanceof N3.NamedNode) { // ... or it will be writte as a literal with its standofftype this.putLiteralStatement('https://nietzsche.philhist.unibas.ch/ontology/deprecation#hasObject', node.value, '^^http://www.w3.org/2001/XMLSchema#anyURI'); } } } // the deprecated patient class: All deprecated statements/triples where the thing acted as a patient, i.e. an object export class TlnDeprecatedPatientStatement extends TlnShortHandResource{ constructor(predicate: string, subject: string) { super(); this._rdfType = 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#PatientStatement'; this._iri = RdfData.createIri('DepPatientStm'); this.putNamedNodeStatement('http://www.w3.org/1999/02/22-rdf-syntax-ns#type', this._rdfType); this.putLiteralStatement('https://nietzsche.philhist.unibas.ch/ontology/deprecation#hasProperty', predicate, '^^http://www.w3.org/2001/XMLSchema#anyURI'); this.putLiteralStatement('https://nietzsche.philhist.unibas.ch/ontology/deprecation#hasSubject', subject, '^^http://www.w3.org/2001/XMLSchema#anyURI'); } } // The deprecated resource with all its properties export class TlnDeprecatedResource extends TlnShortHandResource { // creates a new instance of dep:Deprecation to be imported private _deprecatedStatements: any[] = []; // all statements that were valid before deprecation protected _rdfType = 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#DeprecatedResource'; // the class of any deprecated Resource private _depTypePredicate = 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#wasOfType'; // the predicate stating the former class private _linkToDerivate = 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#hasDerivate'; // predicate to link to the derivate //use only builder constructor() { super(); } // static async buildAsync(ancestor: RdfStoredData, timeStamp: string, derivate?: RdfEditorData): Promise { let build = new TlnDeprecatedResource(); if (derivate !== null && derivate !== undefined) { build.deprecateResourceWith(ancestor, derivate, timeStamp) } else { // the resource has no derivate, so it was "deleted" completely build.buildForDeletion(ancestor); } // Changing the type of the resource, but storing its old type build.putNamedNodeStatement('http://www.w3.org/1999/02/22-rdf-syntax-ns#type', build._rdfType); build.putNamedNodeStatement(build._depTypePredicate, ancestor.rdfType()); return build; } // deprecates a resource completely without any newer state, i.e. a deletion private buildForDeletion(ancestor: RdfStoredData) { this._iri = ancestor.iri(); // if we delete a resource, its iri is still kept/valid and used for the deprecatedResource // get stuff to delete: simply delete all! // todo: add patientstatements ... this._deprecatedStatements = this.createDeprecatedStatements(ancestor.statements()); } // deprecates a resource (ancestor) with its updated version (derivate) private async deprecateResourceWith(ancestor: RdfStoredData, derivate: RdfEditorData, timeStamp: string) { this._iri = `${ancestor.iri()}_deprecated_${timeStamp}`; // setting new deprecated iri this.putNamedNodeStatement(this._linkToDerivate, derivate.iri()); // put a link to the derivate/the latest valid version // Finally deprecating all statements which changed or got deleted } // gets all the triples which need to be created in order to deprecate, i.e. the deprecations of the resources statements createDeprecatedStatements(ancestorStatements: StatementHandler): any[]{ const agentStatements: TlnDeprecatedAgentStatement[] = this.createDeprecatedAgentStatements(ancestorStatements.agentStatements()); const patientStatements: TlnDeprecatedPatientStatement[] = this.createDeprecatedPatientStatements(ancestorStatements.patientStatements()); // Todo: get deprecatedAgentlistStatements & patientlistStatements, deletion and change mgm will be outside this method return [... agentStatements, ... patientStatements]; } createDeprecatedAgentStatements(statements: Map): TlnDeprecatedAgentStatement[] { let depAgentStatements: TlnDeprecatedAgentStatement[] = []; for (let [key, value] of statements) { value.getNodes().forEach( node => { depAgentStatements.push(new TlnDeprecatedAgentStatement(key, node)) }); } return depAgentStatements; } // only needed if resource gets deleted createDeprecatedPatientStatements(statements: Map): TlnDeprecatedPatientStatement[] { let depAgentStatements: TlnDeprecatedPatientStatement[] = []; for (let [key, value] of statements) { value.getNodes().forEach( node => { depAgentStatements.push(new TlnDeprecatedPatientStatement(key, node.id)); }) } return depAgentStatements; } createDeprecatedAgentListAsStatements(statements: Map): TlnDeprecatedAgentStatement[] { let depAgentStatements: TlnDeprecatedAgentStatement[] = []; for (let [key, value] of statements) { value.getNodes().forEach( node => { depAgentStatements.push(new TlnDeprecatedAgentStatement(key, node)) }); } return depAgentStatements; } // returns all statements of the deprecated thing as an n3.Quad array, including the own statements of the dep:DeprecatedResource // as well as the deprecatedStatements, i.e. its statements quads(): N3.Quad[] { let deprStatements: N3.Quad[] = []; this._deprecatedStatements.forEach( stm => { deprStatements.push(...stm.statements()) }); return [].concat(this._statements, deprStatements) } } // contains all the triples/meta data of one transaction. export class TlnTransaction extends TlnShortHandResource{ _rdfType = 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#Transaction'; committedAdditions: string; // the final ttl file committedDeletions: string; // the final written ttl file _additions: N3.Quad[] = []; // all the additions including additions, changes & lists to write _deletions: N3.Quad[] = []; // all the deletions including changes _listsToDelete: string[] = []; // all sparql queries for deleting lists public committed = false; constructor() { super(); } private addTimeStampStm(ts: string) { this.putLiteralStatement('https://nietzsche.philhist.unibas.ch/ontology/deprecation#hasTime', ts, 'http://www.w3.org/2001/XMLSchema#dateTimeStamp'); } private addUserStm(user: string) { if (!user || user !== '') { return } // guard this.putLiteralStatement('https://nietzsche.philhist.unibas.ch/ontology/deprecation#hasResponsible', user); } private addCommentStm(comment: string) { if (!comment || comment !== '') { return } // guard this.putLiteralStatement( 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#hasComment', comment, '@de'); } private addOperationType(resources: TlnResource[]) { let oType = 'multi'; let delCount = resources.map(res => res.isDeleted()).filter(del => del === true).length; let edCount = resources.map(res => res.isEdited()).filter(ed => ed === true).length; console.log('resources deleted :', delCount, ' resources edites: ', edCount, ' total resources: ', resources.length) let inStore = resources.map(res => res.isInStore()).filter(st => st === true).length; // amount of resources in store if (!inStore) { // nothing was in store means creation only oType = 'creation' } if (edCount === inStore && edCount === resources.length && delCount === 0) { // if all edited are in store & nothing has been edited oType = 'edition' } if (delCount === resources.length) { // all have been deleted oType = 'deletion' } this.putLiteralStatement( 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#transactionHasOperationType', oType, '@en'); } // writes out all transaction triples as ttl public writeOut() { this.writeOutAdditions(); this.writeOutDeletions(); } writeOutAdditions() { let addWriter = new N3.Writer(); // writing statements of the Transaction for (const statement of this._statements) { addWriter.addQuad(statement) } // writing statements for the resources/deprecated resources for (const addition of this._additions) { addWriter.addQuad(addition) } addWriter.end((error, result) => { if (error) {console.error(error); return} this.committedAdditions = result; }) } writeOutDeletions() { let delWriter = new N3.Writer(); for (const deletion of this._deletions) { delWriter.addQuad(deletion) } delWriter.end((error, result) => { if (error) {console.error(error); return} this.committedDeletions = result; }) } // creates the commitment from any given array of TlnResources, runs all deprecation needed public async commit(resources: TlnResource[], user: string, comment: string, def: ChangeMgmntDef): Promise { const ts = Date.now().valueOf().toString(); // timeStamp as string if (def.metaData) { // creating metadata about the transaction to perform this._iri = RdfData.createIri('transaction'); this.putNamedNodeStatement('http://www.w3.org/1999/02/22-rdf-syntax-ns#type', this._rdfType); this.addTimeStampStm(ts); this.addUserStm(user); this.addCommentStm(comment); this.addOperationType(resources); } for (let resource of resources) { if (resource.ignore() === false) { if (resource.isInStore()) { let deprecation = await resource.deprecate(ts, def); this._additions.push(...deprecation.quadsToCreate()); this._deletions.push(...deprecation.quadsToDelete()); this._listsToDelete.push(...deprecation.listDeletionQueries()); } else { // nothing to deprecate, only add new resource/statements so // we simply add all quads directly to the transactions additions. We only handle statements top down, i.e. agentStatements only, // hence patient statements are handled by their agents && a new resource to be created does not have any patientStatements this._additions.push(...resource._rdfDatasetToEdit.getAllAgentStatementQuads()); } } } console.log('additions: ', this._additions); console.log('deletions: ', this._deletions); if (this._additions.length + this._deletions.length === 0) { return false } else { return true } } preview() { this.writeOut(); } push(queryServeice: QueryService) { if (!this.committed) { console.warn('nothing to push. Commit before pushing'); return; } this.writeOut(); //todo: import } } // creates/contains all rdf data needed if a resource gets deleted or deprecated due to changes export class TlnDeprecation { private _iri: string; private deprecatedResource: TlnDeprecatedResource; // the deprecated thing dep:DeprecatedRescource with its props to be imported private _productiveOperations: StatementOperations; // the operations needed to perform for the new valid resouce, e.g.creating, updating, deleting, ... private _predecessor: RdfStoredData; // the original thing which needs to be depricated/deleted private _additions: N3.Quad[] = []; // new statements of the dep:deprecation private _depTreeDeletions: N3.Quad[] = []; // MetaData only: former statements of the deprecation tree which have to be deleted in order to update the tree // Todo: lists??? constructor(predecessor: RdfStoredData) { this._iri = RdfData.createIri('Deprecation_'); this._predecessor = predecessor; this._productiveOperations = new StatementOperations(); } // asnc builder for the deprecation public static async buildAsync(predecessor: RdfStoredData, timestamp: string, def: ChangeMgmntDef, derivate?: RdfEditorData): Promise { let deprecation = new TlnDeprecation(predecessor); if (derivate && derivate !== undefined) { // If there is a newer version of the resource we make the diff deprecation._productiveOperations = await StatementOperations.getProductiveOperationsAsync(predecessor, derivate, def.updateLists); } if (!derivate || derivate === undefined) { // if there is no derivate at all, the resource and its statements got deleted in the gui deprecation._productiveOperations = await deprecation.getOpsOnDelete(def.onDelete); console.log('deprecation._productiveOperations', deprecation._productiveOperations) } if (def.metaData && deprecation._productiveOperations.hasChanges()) { deprecation.addSelfStatement(); deprecation.addOperationTypeStm(!!derivate); // if metaData should be written and if there are any changes made, we build a deprecated resource && update the derivation tree deprecation.deprecatedResource = await TlnDeprecatedResource.buildAsync(deprecation._predecessor, timestamp, derivate); if (derivate && derivate !== undefined) { deprecation.updateDerivationTree(derivate.iri()); } } return deprecation; } private async getOpsOnDelete(onDelete: OnDelete): Promise { let ops = new StatementOperations(); if (!onDelete.keepAgentStatements) { ops.deletions.push(... this._predecessor.statements().getAgentStmQuads(this._predecessor.iri())); } if (!onDelete.keepAgentLists) { ops.setListUpdates(this._predecessor) } if (!onDelete.keepPatientStatements) { // deleting also all patient statements ops.deletions.push(... this._predecessor.statements().getPatientStmAsQuads(this._predecessor.iri())); } // /Todo: pATIENT Lists???! return ops; } // Statements for the dep:deprecation // adds the statement with the rdf:type of the dep:Deprecation private addSelfStatement() { const selfStatement = RdfData.selfStatement(this._iri, 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#Deprecation'); this._additions.push(selfStatement); } private addOperationTypeStm(edited: boolean) { if (edited) { this._additions.push(RdfData.createLiteralStatement(this._iri, 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#deprecationHasOperationType', 'edition', '@en')); } else { this._additions.push(RdfData.createLiteralStatement(this._iri, 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#deprecationHasOperationType', 'deletion', '@en')); } } // Updates the derivation tree with the latest changes: Changes the link from the latest Version to this depretaion and takes over the link to the former deprecation private updateDerivationTree(derivateIri: string) { if (!derivateIri || derivateIri === '') { return } // guard const formerDerivation = this._predecessor.getFirstObject('https://nietzsche.philhist.unibas.ch/ontology/deprecation#derivesFrom'); if ( formerDerivation !== '') { // deleting the "dep:derivesFrom" statement from the valid resource to the latest predecessor/deprecation this._depTreeDeletions.push(RdfData.createNamedNodeQuad(derivateIri,'https://nietzsche.philhist.unibas.ch/ontology/deprecation#derivesFrom', formerDerivation)); // adding the "dep:derivesFrom" statement from this deprecation to the former one, i.e. its predecessor this._additions.push(RdfData.createNamedNodeQuad(this._iri, 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#derivesFrom', formerDerivation)); } // Add the link between the new valid version/ the derivate to this deprecation which is the latest predecessor this._additions.push(RdfData.createNamedNodeQuad(derivateIri, 'https://nietzsche.philhist.unibas.ch/ontology/deprecation#derivesFrom', this._iri)); } // public methods/accessors public iri() { return this._iri; } // returns all the quads which have to be added/created as an N3.Quad array, so all the new additions as well as all // the quads of the Dep:deprecatedResource we like to add to the store public quadsToCreate(): N3.Quad[] { if (this.deprecatedResource) { return this._additions.concat(this._productiveOperations.additions, this._productiveOperations.listsToWrite, this.deprecatedResource.quads()); } else { return this._additions.concat(this._productiveOperations.additions, this._productiveOperations.listsToWrite) } } public quadsToDelete(): N3.Quad[] { return [].concat(this._depTreeDeletions, this._productiveOperations.deletions); } public listDeletionQueries(): string[] { return this._productiveOperations.delListQueries; } } diff --git a/nietzsche-beta-app/src/app/rdf-editor-module/rdf-editor-DataSets.ts b/nietzsche-beta-app/src/app/rdf-editor-module/rdf-editor-DataSets.ts index 2c0694c..5d5be57 100644 --- a/nietzsche-beta-app/src/app/rdf-editor-module/rdf-editor-DataSets.ts +++ b/nietzsche-beta-app/src/app/rdf-editor-module/rdf-editor-DataSets.ts @@ -1,262 +1,261 @@ import { EDITINGURL } from '../constants'; import {StatementHandler, ChangeMgmntDef, TlnPredicate, TlnStatementCollection} from './statement-handler'; import {QueryService} from '../services/query.service'; import * as N3 from "node_modules/n3/src"; import {RQ_ASK_EXISTS_IN_STORE, RQ_ASK_IF_CLASS_INSTANCE, RQ_ASK_IF_PROPERTY, RQ_AVAILABLE_PROPERTIES, RQ_GET_DOMAIN, RQ_GET_RANGE, RQ_RDF_Type} from './constants'; export class RdfData { protected _iri: string; // the subjects iri protected _rdfType: string; // rdfs type uf the subject protected _availablePredicates: Map; // All the predicates available in the store for that resource protected _statements: StatementHandler; constructor() { } static selfStatement(s: string, o:string): N3.Quad { return RdfData.createNamedNodeQuad(s, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', o); } // standOffType can be dataType like "http://www.w3.org/2001/XMLSchema#anyURI" or a lang tag like "@en" in the resulting literal // [literal]^^[standoffType]. Default is a literal in german static createLiteralStatement(s: string, p: string, value: string, standOffType = '@de'): N3.Quad { let lang, dataType = ''; const { DataFactory } = N3; const { namedNode, defaultGraph, quad } = DataFactory; let literal = new N3.Literal(`"${value}"${standOffType}`); return quad( namedNode(s), namedNode(p), literal, defaultGraph()); } static createNamedNodeQuad(s: string, p: string, o: string): N3.Quad { const { DataFactory } = N3; const { namedNode, quad } = DataFactory; return quad( namedNode(s), namedNode(p), namedNode(o)) } // creates a unique iri with the given optional label, its optional parent and a generated hash built with timestamp and an additional random hash public static createIri(label?: string, parentIri?: string) { // unique hash: timestamp & random combined const hash = `${Date.now().valueOf().toString(36)}-${(Math.random()).toString(36).replace('.', '')}`; return parentIri !== undefined ? `${parentIri}${label}${hash}` : `${label}${hash}`; } // public methods public iri() { return this._iri; } public rdfType() { return this._rdfType; } public selfStatement() { return RdfData.createNamedNodeQuad(this.iri(), 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', this.getFirstObject('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')); } public availablePredicates(): Map { return this._availablePredicates; } // returns whether a given predicate is available or not acc. to the ontology public isPredicateAvailable(predicate: string): boolean{ return this._availablePredicates.has(predicate); } public agentStatements(): Map { return this._statements.agentStatements(); } public listStatements() { return this._statements.agentListStatements(); } public listsAsStatements() { return this._statements.agentListsAsStatements(); } public statements() { return this._statements; } // returns the first object iri for a given predicate for the resource public getFirstObject(predicateIri: string) { if (this.agentStatements().has(predicateIri) && this.agentStatements().get(predicateIri).getNodes().length > 0) { return this.agentStatements().get(predicateIri).getNodes()[0].value; } else return ''; } // gets all agentstatements, i.e. also the agent list statements as quads public getAllAgentStatementQuads(): N3.Quad[] { let quads: N3.Quad[] = this._statements.getAllAgentStmAsQuads(this._iri); // getting all outgoing statements as quads return quads; } } // RdfStoredData represents the data which is stored in the triple store. It gets/contains all necessary data for any given iri // from the triple store. All properties are immutable. -export class RdfStoredData extends RdfData{ +export class RdfStoredData extends RdfData { constructor(private qService: QueryService, iri: string) { super(); this._iri = iri; } // async rdfDataFactory static async buildAsync(queryService: QueryService, iri, editorDef: ChangeMgmntDef) { const build = await new RdfStoredData(queryService, iri); - if (!build.askIfInStore(iri)) {// guard if iri not living in store + if (!await RdfStoredData.askIfInStore(queryService, iri)) {// guard if iri not living in store console.warn(`Resource ${iri} not existing in store`); - return build } - build._rdfType = await build.getRdfTypeFromStore(iri); + return build; } + build._rdfType = await RdfStoredData.getRdfTypeFromStore(queryService, iri); build._availablePredicates = await build.getAvailablePredicatesFromStore(build._rdfType); // building all statements build._statements = await StatementHandler.buildFromStoreAsync(queryService, iri, build._availablePredicates, editorDef); // adding "a" - let a = await TlnPredicate.buildFromStoreAsync(queryService, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', true, false ); + const a = await TlnPredicate.buildFromStoreAsync(queryService, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', true, false ); build._statements.putAgentStatement(a, build._rdfType); return build; } - // store get methods + // public functions - // asks wether an iri is an instance of a Class + public static async askIfInStore(qService: QueryService, iri: string): Promise { + const query = qService.parametrizeWhereClauseWithItems(RQ_ASK_EXISTS_IN_STORE, iri); + return await qService.getData(EDITINGURL, query, 'ASK') + .then(exists => (exists['boolean'])); + } + + public static async getRdfTypeFromStore(qService: QueryService, iri: string) { + const query = qService.parametrizeWhereClauseWithItems(RQ_RDF_Type, iri); + return await qService.getData(EDITINGURL, query, 'SELECT') + .then( result => result['results']['bindings'].map(p => p['rdf_type'].value)[0]) + } + + + // asks whether an iri is an instance of a Class private async askIfResource(iri: string): Promise { const query = this.qService.parametrizeWhereClauseWithItems(RQ_ASK_IF_CLASS_INSTANCE, iri); return await this.qService.getData(EDITINGURL, query, 'ASK') .then(isResource => (isResource['boolean'])); } - public async getRdfTypeFromStore(iri: string) { - const query = this.qService.parametrizeWhereClauseWithItems(RQ_RDF_Type, iri); - return await this.qService.getData(EDITINGURL, query, 'SELECT') - .then( result => result['results']['bindings'].map(p => p['rdf_type'].value)[0]) - } - // displayableList: whiteList private async getAvailablePredicatesFromStore(rdfType: string, displayableList?: string[], mutableList?: string[] ): Promise> { let avPreds = new Map(); let preds: string[]; const query = this.qService.parametrizeWhereClauseWithItems(RQ_AVAILABLE_PROPERTIES, '' , '', rdfType); preds = await this.qService.getData(EDITINGURL, query, 'SELECT').then( result => result['results']['bindings'].map(p => p['prop'].value) ); // adding the rdfType as well as predicate for (const p of preds) { // todo: add disp/mutable: function displayable () let tlnPredicate = await TlnPredicate.buildFromStoreAsync(this.qService, p); avPreds.set(p, tlnPredicate); } return avPreds; } - // public methods - - public async askIfInStore(iri: string): Promise{ - const query = this.qService.parametrizeWhereClauseWithItems(RQ_ASK_EXISTS_IN_STORE, iri); - return await this.qService.getData(EDITINGURL, query, 'ASK') - .then(exists => (exists['boolean'])); - } - public async exportAsEditorData(statementDef?: ChangeMgmntDef): Promise { return await RdfEditorData.rdfEditorDataFactory(this, statementDef); // creating the rdfEditorData } } // RdfEditorData contains the rdf data to edit. To construct use either rdfEditorDataFactory() to edit an existing thing or build() export class RdfEditorData extends RdfData { protected _displayedPredicates: Map; // All the predicates available for display protected _mutablePredicates: Map; // All the predicates available for editing protected _editorDef: ChangeMgmntDef; public constructor(iri: string, rdfType: string) { super(); this._iri = iri; this._rdfType = rdfType; this._statements = new StatementHandler(); } // async rdfEditorDataFactory, creates the RDFEditorData from a given rdfStoreData by cloning it and all properties/subproperties static async rdfEditorDataFactory(rdfStoredData: RdfStoredData, editorDef?: ChangeMgmntDef ) { // Todo: statementDefs: overwrite TlnPredicates? Filter them? let rdfEditorData = new RdfEditorData(rdfStoredData.iri(), rdfStoredData.rdfType()); rdfEditorData._editorDef = editorDef; rdfEditorData.setAvailablePredicates(rdfStoredData.availablePredicates()); rdfEditorData._statements = await rdfStoredData.statements().selfCloneAsync(); return rdfEditorData; } // public methods public setStatement(predicate: TlnPredicate, object:string, index) { this._statements.setAgentStatement(predicate, object, index) } public putStatement(predicate: TlnPredicate, object:string, index?) { this._statements.putAgentStatement(predicate, object, index) } protected setListStatement(predicate: TlnPredicate, list: string) { this._statements.setListStatement(predicate, list); } // accessor for RdfEditorData Instances public putStatementInListAsStatement(predicate: TlnPredicate, objectIri:string, index?: number) { this._statements.putListAsStmStatement(predicate, objectIri, index); } // accessor for RdfEditorData Instances public setStatementInListAsStatement(predicate: TlnPredicate, objectIri:string, index: number) { this._statements.setListAsStmStatement(predicate, objectIri, index); } public deleteAllAgentStatements() { this._statements.deleteAllAgentStatements(); } // accessor method RdfEditorData Instances public deleteAllStatementsForPredicate(predicateIri: string) { this._statements.deleteAllStatementsForPredicate(predicateIri) } // accessor method RdfEditorData Instances public deleteAllStatementsWithObject(objectIri: string) { this._statements.deleteAllObjectPresence(objectIri); } // accessor method public moveObjectInStatement(predicate: string, previousIndex: number, newIndex: number) { this._statements.moveObjectInAgentStatement(predicate, previousIndex, newIndex); } // accessor method public moveObjectInListAsStatement(predicate: string, previousIndex: number, newIndex: number) { console.log('moving ', predicate, previousIndex, newIndex); this._statements.moveObjectInListAsStatement(predicate, previousIndex, newIndex); } // sets or resets all _availablePredicates public setAvailablePredicates(predicates: Map = new Map()) { this._availablePredicates = predicates; } // sets or resets all _statements public resetListStatements(listStatements: Map = new Map()) { this._statements.resetListStatements(listStatements); } public resetListAsStatements(listAsStatements: Map) { this._statements.resetListAsStatements(listAsStatements); } public createListFromStatements() { } public createListsFromStatements() { } public hasStatementEntriesForPredicate(predicateIri: string) { return this._statements.hasStatementEntriesForPredicate(predicateIri); } }