Skip to content

Commit

Permalink
Merge branch 'master' into 2.14.x
Browse files Browse the repository at this point in the history
  • Loading branch information
tkleinke committed Nov 12, 2019
2 parents 4118367 + 4562e20 commit 8cb8c2d
Show file tree
Hide file tree
Showing 32 changed files with 500 additions and 261 deletions.
4 changes: 2 additions & 2 deletions app/common/angular-utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
*/
export module AngularUtility {

export async function refresh() {
export async function refresh(duration: number = 1) {

await new Promise(resolve => setTimeout(async () => resolve(), 1));
await new Promise(resolve => setTimeout(async () => resolve(), duration));
}
}
63 changes: 39 additions & 24 deletions app/components/import/import.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import BASE_EXCLUSION = ExportRunner.BASE_EXCLUSION;
import getTypesWithoutExcludedTypes = ExportRunner.getTypesWithoutExcludedTypes;
import {IdaiType} from '../../core/configuration/model/idai-type';
import {ProjectConfiguration} from '../../core/configuration/project-configuration';
import {AngularUtility} from '../../common/angular-utility';


@Component({
Expand Down Expand Up @@ -57,6 +58,7 @@ export class ImportComponent implements OnInit {
public mergeMode = false;
public permitDeletions = false;
public javaInstalled: boolean = true;
public running: boolean = false;

// CSV Import
public resourceTypes: Array<IdaiType> = [];
Expand Down Expand Up @@ -116,38 +118,26 @@ export class ImportComponent implements OnInit {
}


public async startImport() {
public async onImportButtonClick() {

this.messages.removeAllMessages();

const reader: Reader|undefined = ImportComponent.createReader(this.sourceType, this.format,
this.file as any, this.url as any, this.http);
if (!reader) return this.messages.add([M.IMPORT_READER_GENERIC_START_ERROR]);

let uploadModalRef: any = undefined;
let uploadReady = false;
setTimeout(() => {
if (!uploadReady) uploadModalRef = this.modalService.open(UploadModalComponent,
{ backdrop: 'static', keyboard: false });
}, 200);

this.settingsService.stopSync();
if (!this.isReady()) return;

const importReport = await this.doImport(reader);

this.settingsService.startSync();
this.running = true;
await AngularUtility.refresh(100);
await this.startImport();

uploadReady = true;
if(uploadModalRef) uploadModalRef.close();
this.showImportResult(importReport);
// The timeout is necessary to prevent another import from starting if the import button is clicked
// again while the import is running
setTimeout(() => this.running = false, 100);
}


public isReady(): boolean|undefined {

return this.sourceType === 'file'
? this.file !== undefined
: this.url !== undefined;
return !this.running
&& this.sourceType === 'file'
? this.file !== undefined
: this.url !== undefined;
}


Expand Down Expand Up @@ -195,6 +185,31 @@ export class ImportComponent implements OnInit {
}


private async startImport() {

this.messages.removeAllMessages();

const reader: Reader|undefined = ImportComponent.createReader(this.sourceType, this.format,
this.file as any, this.url as any, this.http);
if (!reader) return this.messages.add([M.IMPORT_READER_GENERIC_START_ERROR]);

let uploadModalRef: any = undefined;
let uploadReady = false;
setTimeout(() => {
if (!uploadReady) uploadModalRef = this.modalService.open(UploadModalComponent,
{ backdrop: 'static', keyboard: false });
}, 200);

this.settingsService.stopSync();
const importReport = await this.doImport(reader);
this.settingsService.startSync();

uploadReady = true;
if(uploadModalRef) uploadModalRef.close();
this.showImportResult(importReport);
}


private updateResourceTypes() {

this.resourceTypes = getTypesWithoutExcludedTypes(
Expand Down
2 changes: 1 addition & 1 deletion app/components/import/import.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<button id="importStartButton"
class="btn btn-primary"
[ngClass]="{ 'disabled': !isReady() }"
(click)="!isReady() || startImport()">
(click)="!isReady() || onImportButtonClick()">
<span class="mdi mdi-file-import"></span>
<span class="button-label" i18n="@@import.startImport">Import starten</span>
</button>
Expand Down
8 changes: 1 addition & 7 deletions app/components/import/import.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,8 @@
*/

.import {
.spinner {
position: relative;
top: 15px;
left: 5px;
}

.disabled {
cursor: not-allowed;
transition: none !important;
}

.import-info {
Expand Down
4 changes: 2 additions & 2 deletions app/core/datastore/core/solve-project-document-conflicts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {assoc, to, lookup, flow, map, filter, isDefined, union as tsfunUnion, equal,
import {assoc, assocOn, to, lookup, flow, map, filter, isDefined, union as tsfunUnion, equal,
isEmpty, compose, dissoc, append} from 'tsfun';
import {Document, Resource} from 'idai-components-2';
import {DatastoreUtil} from './datastore-util';
Expand Down Expand Up @@ -41,7 +41,7 @@ export function solveProjectDocumentConflict(latestRevision: Document,
if (resource[CAMPAIGNS] && resource[CAMPAIGNS].length === 0) delete resource[CAMPAIGNS];

// this is to work with the latest changes history
const latestRevisionDocumentWithInsertedResultResource = assoc(RESOURCE, resource)(clonedLatestRevision);
const latestRevisionDocumentWithInsertedResultResource = assocOn(RESOURCE, resource)(clonedLatestRevision);

return [latestRevisionDocumentWithInsertedResultResource, revisionIds];
}
Expand Down
7 changes: 0 additions & 7 deletions app/core/datastore/field/field-type-converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,8 @@ export class FieldTypeConverter extends TypeConverter<Document> {
} else {
takeOrMake(document, 'resource.identifier','');
takeOrMake(document, 'resource.relations.isRecordedIn', []);

if (this.typeUtility.isSubtype(document.resource.type,'Feature')) {
takeOrMake(document, 'resource.relations.isContemporaryWith', []);
takeOrMake(document, 'resource.relations.isAfter', []);
takeOrMake(document, 'resource.relations.isBefore', []);
}
}


return Migrator.migrate(document) as T;
}
}
4 changes: 2 additions & 2 deletions app/core/datastore/index/constraint-index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {getOnOr} from 'tsfun';
import {getOn} from 'tsfun';
import {Document} from 'idai-components-2';
import {IndexItem} from './index-item';
import {IdaiType} from '../../configuration/model/idai-type';
Expand Down Expand Up @@ -142,7 +142,7 @@ export module ConstraintIndex {

function putFor(constraintIndex: ConstraintIndex, indexDefinition: IndexDefinition, doc: Document) {

const elForPath = getOnOr(indexDefinition.path, undefined)(doc);
const elForPath = getOn(indexDefinition.path, undefined)(doc);

switch(indexDefinition.type) {
case 'exist':
Expand Down
4 changes: 1 addition & 3 deletions app/core/import/exec/default-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function buildImportFunction(validator: ImportValidator,
return { errors: [errWithParams], successfulImports: 0 };
}

const [ importDocuments, targetDocuments, msgWithParams ] = await process(
const [importDocuments, targetDocuments, msgWithParams] = await process(
documents,
validator,
operationTypeNames,
Expand All @@ -80,14 +80,12 @@ export function buildImportFunction(validator: ImportValidator,

const updateErrors = [];
try {

await Updater.go(
documentsForImport,
targetDocuments,
datastore,
username,
importOptions.mergeMode === true);

} catch (errWithParams) {
updateErrors.push(errWithParams)
}
Expand Down
5 changes: 1 addition & 4 deletions app/core/import/exec/preprocess-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,12 @@ function collapseEmptyProperties(struct: any|undefined, permitDeletions: boolean
keys(struct)
.map(pairWith(lookup(struct)))
.forEach(([fieldName, fieldValue]: any) => {
if (fieldName === 'relations') return;

if (fieldValue === null) {

if (!permitDeletions) delete struct[fieldName];

} else if (typeof (fieldValue as any) === 'string' && fieldValue === '') {

throw [ImportErrors.MUST_NOT_BE_EMPTY_STRING];

} else if (isObject(fieldValue) || isArray(fieldValue)) {
collapseEmptyProperties(fieldValue, permitDeletions);

Expand Down
18 changes: 14 additions & 4 deletions app/core/import/exec/preprocess-relations.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {Document} from 'idai-components-2/src/model/core/document';
import {hasNot, includedIn, isArray, isnt, isUndefinedOrEmpty} from 'tsfun';
import {Document, Relations} from 'idai-components-2';
import {Find, Get, Id, Identifier, IdentifierMap} from './types';
import {Relations} from 'idai-components-2/src/model/core/relations';
import {iterateRelationsInImport} from './utils';
import {ImportErrors as E} from './import-errors';
import {hasNot, includedIn, isArray, isnt, isUndefinedOrEmpty} from 'tsfun';
import {RESOURCE_ID} from '../../../c';
import {HIERARCHICAL_RELATIONS, PARENT} from '../../model/relation-constants';
import LIES_WITHIN = HIERARCHICAL_RELATIONS.LIES_WITHIN;
Expand All @@ -28,7 +27,7 @@ export async function preprocessRelations(documents: Array<Document>,
generateId: () => string,
find: Find,
get: Get,
{ mergeMode, useIdentifiersInRelations}: ImportOptions) {
{ mergeMode, permitDeletions, useIdentifiersInRelations}: ImportOptions) {

const identifierMap: IdentifierMap = mergeMode ? {} : assignIds(documents, generateId);

Expand All @@ -40,6 +39,7 @@ export async function preprocessRelations(documents: Array<Document>,
}
adjustRelations(document, relations);
removeSelfReferencingIdentifiers(relations, document.resource.identifier);
if (!permitDeletions) removeEmptyRelations(relations);
if (useIdentifiersInRelations) {
await rewriteIdentifiersInRelations(relations, find, identifierMap);
} else {
Expand Down Expand Up @@ -139,4 +139,14 @@ function removeSelfReferencingIdentifiers(relations: Relations, resourceIdentifi
relations[relName] = relations[relName].filter(isnt(resourceIdentifier));
if (isUndefinedOrEmpty(relations[relName])) delete relations[relName];
}
}


function removeEmptyRelations(relations: Relations) {

for (let relName of Object.keys(relations)) {
if (relations[relName] === null || relations[relName] === []) {
delete relations[relName];
}
}
}
56 changes: 32 additions & 24 deletions app/core/import/exec/process/complete-inverse-relations.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Document, Relations} from 'idai-components-2';
import {ImportErrors as E} from '../import-errors';
import {compose, filter, flatten, flow, forEach, intersect, isDefined,
isEmpty, isNot, isUndefinedOrEmpty, lookup, keys, values, empty,
isNot, isUndefinedOrEmpty, lookup, keys, values, empty,
map, remove, subtract, to, undefinedOrEmpty} from 'tsfun';
import {Document, Relations} from 'idai-components-2';
import {ImportErrors as E} from '../import-errors';
import {HIERARCHICAL_RELATIONS, POSITION_RELATIONS, TIME_RELATIONS} from '../../../model/relation-constants';
import {setInverseRelationsForDbResources} from './set-inverse-relations-for-db-resources';
import {assertInSameOperationWith, makeDocumentsLookup} from '../utils';
Expand Down Expand Up @@ -56,7 +56,7 @@ export async function completeInverseRelations(importDocuments: Array<Document>,

const lookupDocument = lookup(makeDocumentsLookup(importDocuments));

setInverseRelationsForImportResource(
setInverseRelationsForImportResources(
importDocuments,
lookupDocument,
pairRelationWithItsInverse(getInverseRelation),
Expand All @@ -80,13 +80,21 @@ function getTargetIds(mergeMode: boolean,

let targetIds = targetIdsReferingToDbResources(document, lookupDocument);
if (mergeMode) {
let oldVersion; try { oldVersion = await get(document.resource.id);
} catch { throw "FATAL existing version of document not found" }
let oldVersion;
try {
oldVersion = await get(document.resource.id);
} catch {
throw 'FATAL: Existing version of document not found';
}

return [
targetIds,
subtract<ResourceId>(targetIds)(targetIdsReferingToDbResources(oldVersion as any, lookupDocument))];
subtract<ResourceId>(targetIds)(
targetIdsReferingToDbResources(oldVersion as any, lookupDocument)
)
];
}
return [ targetIds, [] ];
return [targetIds, []];
}
}

Expand All @@ -102,21 +110,21 @@ function targetIdsReferingToDbResources(document: Document,
}


function setInverseRelationsForImportResource(importDocuments: Array<Document>,
lookupDocument: LookupDocument,
pairRelationWithItsInverse: PairRelationWithItsInverse,
assertIsAllowedRelationDomainType: AssertIsAllowedRelationDomainType): void {
function setInverseRelationsForImportResources(importDocuments: Array<Document>,
lookupDocument: LookupDocument,
pairRelationWithItsInverse: PairRelationWithItsInverse,
assertIsAllowedRelationDomainType: AssertIsAllowedRelationDomainType): void {

for (let importDocument of importDocuments) {

const pairRelationWithItsInverse_ = pairRelationWithItsInverse(importDocument);
const assertNotBadyInterrelated_ = assertNotBadlyInterrelated(importDocument);
const assertNotBadlyInterrelated_ = assertNotBadlyInterrelated(importDocument);
const setInverses_ = setInverses(importDocument, lookupDocument, assertIsAllowedRelationDomainType);

flow(importDocument.resource.relations,
keys,
map(pairRelationWithItsInverse_),
forEach(assertNotBadyInterrelated_),
forEach(assertNotBadlyInterrelated_),
forEach(setInverses_));
}
}
Expand Down Expand Up @@ -156,18 +164,17 @@ function setInverses(importDocument: Document,

function pairRelationWithItsInverse(getInverseRelation: (_: string) => string|undefined) {

return (document: Document) => (relationName: string): [string, string|undefined] => {
return (document: Document) => (relationName: string): [string, string|undefined] => {

if (relationName === RECORDED_IN) return [RECORDED_IN, undefined];
if (isEmpty(document.resource.relations[relationName])) {
throw [E.EMPTY_RELATION, document.resource.identifier];
if (relationName === RECORDED_IN) {
return [RECORDED_IN, undefined];
} else {
return [relationName, getInverseRelation(relationName)];
}
return [relationName, getInverseRelation(relationName)];
}
}



function assertNotBadlyInterrelated(document: Document) {

return ([relationName, inverseRelationName]: [string, string|undefined]) => {
Expand All @@ -176,20 +183,21 @@ function assertNotBadlyInterrelated(document: Document) {

const forbiddenRelations = [];

if (relationName !== inverseRelationName) forbiddenRelations.push(inverseRelationName);
if (relationName !== inverseRelationName) forbiddenRelations.push(inverseRelationName);

if ([IS_ABOVE, IS_BELOW].includes(relationName)) forbiddenRelations.push(IS_EQUIVALENT_TO);
else if (IS_EQUIVALENT_TO === relationName) forbiddenRelations.push(IS_ABOVE, IS_BELOW);
else if (IS_EQUIVALENT_TO === relationName) forbiddenRelations.push(IS_ABOVE, IS_BELOW);

if ([IS_BEFORE, IS_AFTER].includes(relationName)) forbiddenRelations.push(IS_CONTEMPORARY_WITH);
else if (IS_CONTEMPORARY_WITH === relationName) forbiddenRelations.push(IS_BEFORE, IS_AFTER);
else if (IS_CONTEMPORARY_WITH === relationName) forbiddenRelations.push(IS_BEFORE, IS_AFTER);

assertNoForbiddenRelations(forbiddenRelations, document.resource.relations[relationName], document);
}
}


function assertNoForbiddenRelations(forbiddenRelations: string[], relationTargets: string[], document: Document) {
function assertNoForbiddenRelations(forbiddenRelations: string[], relationTargets: string[],
document: Document) {

forbiddenRelations
.map(lookup(document.resource.relations))
Expand Down
Loading

0 comments on commit 8cb8c2d

Please sign in to comment.