Revision 0535d452dd426ba62d6d312766906ddd1230c7eb authored by Andrey Zhavoronkov on 02 November 2023, 15:59:39 UTC, committed by GitHub on 02 November 2023, 15:59:39 UTC
This PR speeds up the preparation of chunks by: 
1. loading images once instead of twice in each writer,
2. as well as by allowing simultaneous preparation of more than 1 chunk
using multithreading.
This allows to reduce the time for preparation of chunks for 4895 images
from 0:04:36 to 0:01:20 in case of preparation of 3 chunks in parallel
and 0:02:46 in case of 1 chunk in my environment.

Co-authored-by: Maria Khrustaleva <maya17grd@gmail.com>
1 parent 1f8d5d3
Raw File
analytics-report.ts
// Copyright (C) 2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import { ArgumentError } from './exceptions';

export interface SerializedDataEntry {
    date?: string;
    value?: number | Record<string, number>
}

export interface SerializedTransformBinaryOp {
    left: string;
    operator: string;
    right: string;
}

export interface SerializedTransformationEntry {
    name: string;
    binary?: SerializedTransformBinaryOp;
}

export interface SerializedAnalyticsEntry {
    name?: string;
    title?: string;
    description?: string;
    granularity?: string;
    default_view?: string;
    data_series?: Record<string, SerializedDataEntry[]>;
    transformations?: SerializedTransformationEntry[];
}

export interface SerializedAnalyticsReport {
    id?: number;
    target?: string;
    created_date?: string;
    statistics?: SerializedAnalyticsEntry[];
}

export enum AnalyticsReportTarget {
    JOB = 'job',
    TASK = 'task',
    PROJECT = 'project',
}

export enum AnalyticsEntryViewType {
    HISTOGRAM = 'histogram',
    NUMERIC = 'numeric',
}

export class AnalyticsEntry {
    #name: string;
    #title: string;
    #description: string;
    #granularity: string;
    #defaultView: AnalyticsEntryViewType;
    #dataSeries: Record<string, SerializedDataEntry[]>;
    #transformations: SerializedTransformationEntry[];

    constructor(initialData: SerializedAnalyticsEntry) {
        this.#name = initialData.name;
        this.#title = initialData.title;
        this.#description = initialData.description;
        this.#granularity = initialData.granularity;
        this.#defaultView = initialData.default_view as AnalyticsEntryViewType;
        this.#transformations = initialData.transformations;
        this.#dataSeries = this.applyTransformations(initialData.data_series);
    }

    get name(): string {
        return this.#name;
    }

    get title(): string {
        return this.#title;
    }

    get description(): string {
        return this.#description;
    }

    // Probably need to create enum for this
    get granularity(): string {
        return this.#granularity;
    }

    get defaultView(): AnalyticsEntryViewType {
        return this.#defaultView;
    }

    get dataSeries(): Record<string, SerializedDataEntry[]> {
        return this.#dataSeries;
    }

    get transformations(): SerializedTransformationEntry[] {
        return this.#transformations;
    }

    private applyTransformations(
        dataSeries: Record<string, SerializedDataEntry[]>,
    ): Record<string, SerializedDataEntry[]> {
        this.#transformations.forEach((transform) => {
            if (transform.binary) {
                let operator: (left: number, right: number) => number;
                switch (transform.binary.operator) {
                    case '+': {
                        operator = (left: number, right: number) => left + right;
                        break;
                    }
                    case '-': {
                        operator = (left: number, right: number) => left - right;
                        break;
                    }
                    case '*': {
                        operator = (left: number, right: number) => left * right;
                        break;
                    }
                    case '/': {
                        operator = (left: number, right: number) => (right !== 0 ? left / right : 0);
                        break;
                    }
                    default: {
                        throw new ArgumentError(
                            `Cannot apply transformation: got unsupported operator type ${transform.binary.operator}.`,
                        );
                    }
                }

                const leftName = transform.binary.left;
                const rightName = transform.binary.right;
                dataSeries[transform.name] = dataSeries[leftName].map((left, i) => {
                    const right = dataSeries[rightName][i];
                    if (typeof left.value === 'number' && typeof right.value === 'number') {
                        return {
                            value: operator(left.value, right.value),
                            date: left.date,
                        };
                    }
                    return {
                        value: 0,
                        date: left.date,
                    };
                });
                delete dataSeries[leftName];
                delete dataSeries[rightName];
            }
        });
        return dataSeries;
    }
}

export default class AnalyticsReport {
    #id: number;
    #target: AnalyticsReportTarget;
    #createdDate: string;
    #statistics: AnalyticsEntry[];

    constructor(initialData: SerializedAnalyticsReport) {
        this.#id = initialData.id;
        this.#target = initialData.target as AnalyticsReportTarget;
        this.#createdDate = initialData.created_date;
        this.#statistics = [];
        for (const analyticsEntry of initialData.statistics) {
            this.#statistics.push(new AnalyticsEntry(analyticsEntry));
        }
    }

    get id(): number {
        return this.#id;
    }

    get target(): AnalyticsReportTarget {
        return this.#target;
    }

    get createdDate(): string {
        return this.#createdDate;
    }

    get statistics(): AnalyticsEntry[] {
        return this.#statistics;
    }
}
back to top