Skip to content

Commit

Permalink
Merge branch 'develop' into zm/gt-jobs
Browse files Browse the repository at this point in the history
  • Loading branch information
klakhov authored Jun 23, 2023
2 parents ff76c64 + ea6c68f commit 8c8e2a8
Show file tree
Hide file tree
Showing 73 changed files with 3,012 additions and 272 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## \[Unreleased]
### Added
- Now CVAT supports project/task markdown description with additional assets
(png, jpeg, gif, webp images and pdf files) (<https://github.com/opencv/cvat/pull/6191>)
- Ground Truth jobs and quality analytics for tasks (<https://github.com/opencv/cvat/pull/6039>)


### Changed
- TDB

Expand All @@ -21,12 +24,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- The problem with manifest file in tasks restored from backup (<https://github.com/opencv/cvat/issues/5971>)
- The problem with task mode in a task restored from backup (<https://github.com/opencv/cvat/issues/5668>)
- Visible 'To background' button in review mode (<https://github.com/opencv/cvat/pull/6363>)
- \[API\] Performance of several API endpoints (<https://github.com/opencv/cvat/pull/6340>)
- \[API\] Invalid schema for the owner field in several endpoints (<https://github.com/opencv/cvat/pull/6343>)

### Security
- TDB

## \[2.4.8] - 2023-06-22
### Fixed
- Getting original chunks for items in specific cases (<https://github.com/opencv/cvat/pull/6355>)

## \[2.4.7] - 2023-06-16
### Added
- \[API\] API Now supports the creation and removal of Ground Truth jobs. (<https://github.com/opencv/cvat/pull/6204>)
Expand Down
11 changes: 11 additions & 0 deletions cvat-core/src/api-implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import Project from './project';
import CloudStorage from './cloud-storage';
import Organization from './organization';
import Webhook from './webhook';
import { ArgumentError } from './exceptions';
import { SerializedAsset } from './server-response-types';
import QualityReport from './quality-report';
import QualityConflict from './quality-conflict';
import QualitySettings from './quality-settings';
Expand Down Expand Up @@ -138,6 +140,15 @@ export default function implementAPI(cvat) {
return result;
};

cvat.assets.create.implementation = async (file: File, guideId: number): Promise<SerializedAsset> => {
if (!(file instanceof File)) {
throw new ArgumentError('Assets expect a file');
}

const result = await serverProxy.assets.create(file, guideId);
return result;
};

cvat.users.get.implementation = async (filter) => {
checkFilter(filter, {
id: isInteger,
Expand Down
13 changes: 8 additions & 5 deletions cvat-core/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@
//
// SPDX-License-Identifier: MIT

/**
* External API which should be used by for development
* @module API
*/

import PluginRegistry from './plugins';
import loggerStorage from './logger-storage';
import { EventLogger } from './log';
Expand All @@ -25,6 +20,7 @@ import { FrameData } from './frames';
import CloudStorage from './cloud-storage';
import Organization from './organization';
import Webhook from './webhook';
import AnnotationGuide from './guide';

import * as enums from './enums';

Expand Down Expand Up @@ -147,6 +143,12 @@ function build() {
return result;
},
},
assets: {
async create(file: File, guideId: number) {
const result = await PluginRegistry.apiWrapper(cvat.assets.create, file, guideId);
return result;
},
},
jobs: {
async get(filter = {}) {
const result = await PluginRegistry.apiWrapper(cvat.jobs.get, filter);
Expand Down Expand Up @@ -311,6 +313,7 @@ function build() {
CloudStorage,
Organization,
Webhook,
AnnotationGuide,
},
};

Expand Down
92 changes: 92 additions & 0 deletions cvat-core/src/guide.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (C) 2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import { SerializedGuide } from './server-response-types';
import { ArgumentError, DataError } from './exceptions';
import PluginRegistry from './plugins';
import serverProxy from './server-proxy';

class AnnotationGuide {
#id: AnnotationGuide['id'];
#taskId: AnnotationGuide['taskId'];
#projectId: AnnotationGuide['projectId'];
#createdDate?: AnnotationGuide['createdDate'];
#updatedDate?: AnnotationGuide['updatedDate'];
#markdown: AnnotationGuide['markdown'];

constructor(initialData: Partial<SerializedGuide>) {
this.#id = initialData.id;
this.#taskId = initialData.task_id || null;
this.#projectId = initialData.project_id || null;
this.#createdDate = initialData.created_date;
this.#updatedDate = initialData.updated_date;
this.#markdown = initialData.markdown || '';
}

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

public get taskId(): number | null {
return this.#taskId;
}

public get projectId(): number | null {
return this.#projectId;
}

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

public get updatedDate(): string | undefined {
return this.#updatedDate;
}

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

public set markdown(value: string) {
if (typeof value !== 'string') {
throw new ArgumentError(`Markdown value must be a string, ${typeof value} received`);
}
this.#markdown = value;
}

async save(): Promise<AnnotationGuide> {
const result = await PluginRegistry.apiWrapper.call(this, AnnotationGuide.prototype.save);
return result;
}
}

Object.defineProperties(AnnotationGuide.prototype.save, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(this: AnnotationGuide) {
if (Number.isInteger(this.id)) {
const result = await serverProxy.guides.update(this.id, { markdown: this.markdown });
return new AnnotationGuide(result);
}

if (this.projectId === null && this.taskId === null) {
throw new DataError('One of projectId or taskId must be specified for a guide');
}

if (this.projectId !== null && this.taskId !== null) {
throw new DataError('Both projectId and taskId must not be presented for a guide');
}

const result = await serverProxy.guides.create({
task_id: this.taskId,
project_id: this.projectId,
markdown: this.markdown,
});
return new AnnotationGuide(result);
},
},
});

export default AnnotationGuide;
10 changes: 10 additions & 0 deletions cvat-core/src/project-implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Project from './project';
import { exportDataset, importDataset } from './annotations';
import { SerializedLabel } from './server-response-types';
import { Label } from './labels';
import AnnotationGuide from './guide';

export default function implementProject(projectClass) {
projectClass.prototype.save.implementation = async function () {
Expand Down Expand Up @@ -125,5 +126,14 @@ export default function implementProject(projectClass) {
return result;
};

projectClass.prototype.guide.implementation = async function guide() {
if (this.guideId === null) {
return null;
}

const result = await serverProxy.guides.get(this.guideId);
return new AnnotationGuide(result);
};

return projectClass;
}
11 changes: 11 additions & 0 deletions cvat-core/src/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import { ArgumentError } from './exceptions';
import { Label } from './labels';
import User from './user';
import { FieldUpdateTrigger } from './common';
import AnnotationGuide from './guide';

export default class Project {
public readonly id: number;
public name: string;
public assignee: User;
public bugTracker: string;
public readonly status: ProjectStatus;
public readonly guideId: number | null;
public readonly organization: string | null;
public readonly owner: User;
public readonly createdDate: string;
Expand All @@ -39,6 +41,7 @@ export default class Project {
name: undefined,
status: undefined,
assignee: undefined,
guide_id: undefined,
organization: undefined,
owner: undefined,
bug_tracker: undefined,
Expand Down Expand Up @@ -98,6 +101,9 @@ export default class Project {
owner: {
get: () => data.owner,
},
guideId: {
get: () => data.guide_id,
},
organization: {
get: () => data.organization,
},
Expand Down Expand Up @@ -230,6 +236,11 @@ export default class Project {
const result = await PluginRegistry.apiWrapper.call(this, Project.restore, storage, file);
return result;
}

async guide(): Promise<AnnotationGuide | null> {
const result = await PluginRegistry.apiWrapper.call(this, Project.prototype.guide);
return result;
}
}

Object.defineProperties(
Expand Down
94 changes: 78 additions & 16 deletions cvat-core/src/server-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
SerializedLabel, SerializedAnnotationFormats, ProjectsFilter,
SerializedProject, SerializedTask, TasksFilter, SerializedUser,
SerializedAbout, SerializedRemoteFile, SerializedUserAgreement,
SerializedRegister, JobsFilter, SerializedJob,
SerializedRegister, JobsFilter, SerializedJob, SerializedGuide, SerializedAsset,
} from 'server-response-types';
import { SerializedQualityReportData } from 'quality-report';
import { SerializedQualitySettingsData } from 'quality-settings';
Expand Down Expand Up @@ -2200,33 +2200,53 @@ async function receiveWebhookEvents(type: WebhookSourceType): Promise<string[]>
}
}

async function getQualityReports(filter): Promise<SerializedQualityReportData[]> {
async function getGuide(id: number): Promise<SerializedGuide> {
const { backendAPI } = config;

try {
const response = await Axios.get(`${backendAPI}/quality/reports`, {
params: {
...filter,
},
});
const response = await Axios.get(`${backendAPI}/guides/${id}`);
return response.data;
} catch (errorData) {
throw generateError(errorData);
}
}

return response.data.results;
async function createGuide(data: Partial<SerializedGuide>): Promise<SerializedGuide> {
const { backendAPI } = config;

try {
const response = await Axios.post(`${backendAPI}/guides`, data);
return response.data;
} catch (errorData) {
throw generateError(errorData);
}
}

async function getQualityConflicts(filter): Promise<SerializedQualityConflictData[]> {
const params = enableOrganization();
async function updateGuide(id: number, data: Partial<SerializedGuide>): Promise<SerializedGuide> {
const { backendAPI } = config;

try {
const response = await fetchAll(`${backendAPI}/quality/conflicts`, {
...params,
...filter,
});
const response = await Axios.patch(`${backendAPI}/guides/${id}`, data);
return response.data;

return response.results;
} catch (errorData) {
throw generateError(errorData);
}
}

async function createAsset(file: File, guideId: number): Promise<SerializedAsset> {
const { backendAPI } = config;
const form = new FormData();
form.append('file', file);
form.append('guide_id', guideId);

try {
const response = await Axios.post(`${backendAPI}/assets`, form, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
return response.data;
} catch (errorData) {
throw generateError(errorData);
}
Expand Down Expand Up @@ -2265,6 +2285,39 @@ async function updateQualitySettings(
}
}

async function getQualityConflicts(filter): Promise<SerializedQualityConflictData[]> {
const params = enableOrganization();
const { backendAPI } = config;

try {
const response = await fetchAll(`${backendAPI}/quality/conflicts`, {
...params,
...filter,
});

return response.results;
} catch (errorData) {
throw generateError(errorData);
}
}


async function getQualityReports(filter): Promise<SerializedQualityReportData[]> {
const { backendAPI } = config;

try {
const response = await Axios.get(`${backendAPI}/quality/reports`, {
params: {
...filter,
},
});

return response.data.results;
} catch (errorData) {
throw generateError(errorData);
}
}

export default Object.freeze({
server: Object.freeze({
setAuthData,
Expand Down Expand Up @@ -2411,14 +2464,23 @@ export default Object.freeze({
events: receiveWebhookEvents,
}),

guides: Object.freeze({
get: getGuide,
create: createGuide,
update: updateGuide,
}),

assets: Object.freeze({
create: createAsset,
}),

analytics: Object.freeze({
quality: Object.freeze({
reports: getQualityReports,
conflicts: getQualityConflicts,
settings: Object.freeze({
get: getQualitySettings,
update: updateQualitySettings,
}),
}),
}),
});
Loading

0 comments on commit 8c8e2a8

Please sign in to comment.