<commit_before>import {Component, Input, OnChanges} from '@angular/core';
import {Relations, Resource, IdaiFieldDocument, IdaiFieldResource, Document, Messages, ProjectConfiguration}
    from 'idai-components-2';
import {IdaiFieldDocumentReadDatastore} from '../../../core/datastore/field/idai-field-document-read-datastore';
import {M} from '../../m';

const moment = require('moment');

/**
 * @author <NAME>
 */
@Component({
    moduleId: module.id,
    selector: 'docedit-conflicts-tab',
    templateUrl: './docedit-conflicts-tab.html'
})
export class DoceditConflictsTabComponent implements OnChanges {

    @Input() document: IdaiFieldDocument;
    @Input() inspectedRevisions: Document[];

    private conflictedRevisions: Array<IdaiFieldDocument> = [];
    private selectedRevision: IdaiFieldDocument|undefined;
    private differingFields: any[];
    private relationTargets: { [targetId: string]: IdaiFieldDocument };
    private ready = false;


    constructor(
        private datastore: IdaiFieldDocumentReadDatastore,
        private messages: Messages,
        private projectConfiguration: ProjectConfiguration) {}


    async ngOnChanges() {

        for (let revisionId of (this.document as any)['_conflicts']) {
            if (this.inspectedRevisions.map(_ => _.resource.id)
                    .includes(revisionId)) continue;

            try {
                this.conflictedRevisions.push(await this.datastore.getRevision(this.document.resource.id, revisionId));
            } catch (_) {
                console.error("revision not found " + this.document.resource.id + " " + revisionId);
                this.messages.add([M.DATASTORE_NOT_FOUND])
            }
        }

        if (this.conflictedRevisions.length > 0) {
            this.sortRevisions(this.conflictedRevisions);
            this.setSelectedRevision(this.conflictedRevisions[0]);
        } else {
            this.differingFields = [];
        }
        this.ready = true;
    }


    public setSelectedRevision(revision: IdaiFieldDocument) {

        this.selectedRevision = revision;

        this.differingFields = DoceditConflictsTabComponent.createDiff(
            this.document, revision, this.projectConfiguration);

        this.fetchRelationTargets();
    }

    
    public solveConflict() {

        if (!this.selectedRevision) return;
        
        for (let field of this.differingFields) {
            if (field.rightSideWinning) {
                if (field.type === 'relation') {
                    if (this.selectedRevision.resource.relations[field.name]) {
                        this.document.resource.relations[field.name]
                            = this.selectedRevision.resource.relations[field.name];
                    } else {
                        delete this.document.resource.relations[field.name];
                    }
                } else {
                    this.document.resource[field.name] = this.selectedRevision.resource[field.name];
                }
            }
        }

        this.markRevisionAsInspected(this.selectedRevision);
        if (this.conflictedRevisions.length > 0) {
            this.setSelectedRevision(this.conflictedRevisions[0]);
        } else {
            this.selectedRevision = undefined;
            this.differingFields = [];
        }
    }


    private markRevisionAsInspected(revision: IdaiFieldDocument) {

        let index = this.conflictedRevisions.indexOf(revision);
        this.conflictedRevisions.splice(index, 1);
        this.inspectedRevisions.push(revision);
    }


    private fetchRelationTargets() {

        if (!this.selectedRevision) return;

        this.relationTargets = {};

        for (let field of this.differingFields) {
            if (field.type === 'relation') {
                this.fetchRelationTargetsOfField(this.document.resource, field.name);
                this.fetchRelationTargetsOfField(this.selectedRevision.resource, field.name);
            }
        }
    }


    private fetchRelationTargetsOfField(resource: IdaiFieldResource, fieldName: string) {

        if (resource.relations[fieldName]) {
            for (let targetId of resource.relations[fieldName]) {
                this.datastore.get(targetId).then(
                    doc => { this.relationTargets[targetId] = <IdaiFieldDocument> doc; },
                    () => this.messages.add([M.DATASTORE_NOT_FOUND])
                );
            }
        }
    }


    public getTargetIdentifiers(targetIds: string[]): string {

        let result: string = '';

        for (let targetId of targetIds) {
            if (this.relationTargets[targetId]) {
                if (result.length > 0) result += ', ';
                result += this.relationTargets[targetId].resource.identifier;
            }
        }

        return result;
    }


    public getWinningSide(): string {

        if (this.differingFields.length === 0) return 'left';
<commit_msg>show info label if a relation target resource could not be found in conflicts tab instead of showing error message<commit_after>import {Component, Input, OnChanges} from '@angular/core';
import {Relations, Resource, IdaiFieldDocument, IdaiFieldResource, Document, Messages, ProjectConfiguration}
    from 'idai-components-2';
import {IdaiFieldDocumentReadDatastore} from '../../../core/datastore/field/idai-field-document-read-datastore';
import {M} from '../../m';

const moment = require('moment');

/**
 * @author <NAME>
 */
@Component({
    moduleId: module.id,
    selector: 'docedit-conflicts-tab',
    templateUrl: './docedit-conflicts-tab.html'
})
export class DoceditConflictsTabComponent implements OnChanges {

    @Input() document: IdaiFieldDocument;
    @Input() inspectedRevisions: Document[];

    private conflictedRevisions: Array<IdaiFieldDocument> = [];
    private selectedRevision: IdaiFieldDocument|undefined;
    private differingFields: any[];
    private relationTargets: { [targetId: string]: IdaiFieldDocument|undefined };
    private ready = false;


    constructor(
        private datastore: IdaiFieldDocumentReadDatastore,
        private messages: Messages,
        private projectConfiguration: ProjectConfiguration) {}


    async ngOnChanges() {

        for (let revisionId of (this.document as any)['_conflicts']) {
            if (this.inspectedRevisions.map(_ => _.resource.id)
                    .includes(revisionId)) continue;

            try {
                this.conflictedRevisions.push(await this.datastore.getRevision(this.document.resource.id, revisionId));
            } catch (_) {
                console.error("revision not found " + this.document.resource.id + " " + revisionId);
                this.messages.add([M.DATASTORE_NOT_FOUND])
            }
        }

        if (this.conflictedRevisions.length > 0) {
            this.sortRevisions(this.conflictedRevisions);
            this.setSelectedRevision(this.conflictedRevisions[0]);
        } else {
            this.differingFields = [];
        }
        this.ready = true;
    }


    public setSelectedRevision(revision: IdaiFieldDocument) {

        this.selectedRevision = revision;

        this.differingFields = DoceditConflictsTabComponent.createDiff(
            this.document, revision, this.projectConfiguration);

        this.fetchRelationTargets();
    }

    
    public solveConflict() {

        if (!this.selectedRevision) return;
        
        for (let field of this.differingFields) {
            if (field.rightSideWinning) {
                if (field.type === 'relation') {
                    if (this.selectedRevision.resource.relations[field.name]) {
                        this.document.resource.relations[field.name]
                            = this.selectedRevision.resource.relations[field.name];
                    } else {
                        delete this.document.resource.relations[field.name];
                    }
                } else {
                    this.document.resource[field.name] = this.selectedRevision.resource[field.name];
                }
            }
        }

        this.markRevisionAsInspected(this.selectedRevision);
        if (this.conflictedRevisions.length > 0) {
            this.setSelectedRevision(this.conflictedRevisions[0]);
        } else {
            this.selectedRevision = undefined;
            this.differingFields = [];
        }
    }


    private markRevisionAsInspected(revision: IdaiFieldDocument) {

        let index = this.conflictedRevisions.indexOf(revision);
        this.conflictedRevisions.splice(index, 1);
        this.inspectedRevisions.push(revision);
    }


    private fetchRelationTargets() {

        if (!this.selectedRevision) return;

        this.relationTargets = {};

        for (let field of this.differingFields) {
            if (field.type === 'relation') {
                this.fetchRelationTargetsOfField(this.document.resource, field.name);
                this.fetchRelationTargetsOfField(this.selectedRevision.resource, field.name);
            }
        }
    }


    private fetchRelationTargetsOfField(resource: IdaiFieldResource, fieldName: string) {

        if (resource.relations[fieldName]) {
            for (let targetId of resource.relations[fieldName]) {
                this.datastore.get(targetId).then(
                    doc => { this.relationTargets[targetId] = <IdaiFieldDocument> doc; },
                    () => { this.relationTargets[targetId] = undefined; }
                );
            }
        }
    }


    public getTargetIdentifiers(targetIds: string[]): string {

        let result: string = '';

        for (let targetId of targetIds) {
            if (result.length > 0) result += ', ';
            result += this.relationTargets[targetId]
                ? (this.relationTargets[targetId] as IdaiFieldDocument).resource.identifier
                : 'Gelöschte Ressource';
        }

        return result;
    }


    public getWinningSide(): string {

        if (this.differingFields.length === 0) return 'left';

{'fraction_non_alphanumeric': 0.10804620593507269, 'fraction_numerical': 0.001593308105954989, 'mean_word_length': 2.2512139851084494, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5010960', 'n_tokens_mistral': 2805, 'n_tokens_neox': 2653, 'n_words': 479}