Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Markdown documentation for tasks/projects #6191

Merged
merged 67 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from 63 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
7ebf2e5
Initial commit
bsekachev May 22, 2023
520aad1
Some fixes, guide works for tasks
bsekachev May 23, 2023
2d4ea90
Create/update for a project
bsekachev May 23, 2023
08a085d
Attaching assets basic implementation
bsekachev May 23, 2023
a0d863e
Assignment assets to guides and removing unnecessary assets
bsekachev May 23, 2023
6313e51
Aborted unnecessary changes
bsekachev May 23, 2023
ecab185
Added icons on annotation view
bsekachev May 24, 2023
c460380
Showing guide on annotaion view
bsekachev May 24, 2023
d54ea00
Fixed eslint errors
bsekachev May 24, 2023
9e78d71
Some size adjustments
bsekachev May 24, 2023
ce9bfe3
Improved markdown editor page
bsekachev May 24, 2023
388771f
Some code refactoring
bsekachev May 24, 2023
a9cd369
Minor fixes
bsekachev May 26, 2023
e7268b3
Added more plugins
bsekachev May 28, 2023
155312b
added missed migration file
bsekachev May 30, 2023
33d466e
Implemented basic rules for sandbox
bsekachev Jun 8, 2023
32d29c4
Added create permissions
bsekachev Jun 8, 2023
1465428
Implemented permissions for annotator
bsekachev Jun 8, 2023
6643533
Update mixing -> PartialUpdate
bsekachev Jun 8, 2023
bb19139
Squashed migrations
bsekachev Jun 8, 2023
1c5e3ee
Assets always associated with a guide
bsekachev Jun 8, 2023
348f0ca
Assets permissions
bsekachev Jun 8, 2023
5eb2166
Removed guide owner (useless field), added opa rules for organizations
bsekachev Jun 8, 2023
5080b38
Improved some code
bsekachev Jun 8, 2023
655ea29
Some code refactored
bsekachev Jun 8, 2023
0a790d9
Merged develop
bsekachev Jun 8, 2023
4fb8850
Org permissions
bsekachev Jun 8, 2023
9f3d05e
Merged develop
bsekachev Jun 13, 2023
05f2496
Added limits, updated migrations
bsekachev Jun 13, 2023
802cc43
Implemented plugin for tags
bsekachev Jun 13, 2023
a2aa250
Fixed permission
bsekachev Jun 14, 2023
7f3c23d
Added missed settings
bsekachev Jun 14, 2023
749ae39
Implemented backup for tasks and projects
bsekachev Jun 14, 2023
eb16f1b
Simplified backup code
bsekachev Jun 14, 2023
7299c9f
Improved write code
bsekachev Jun 15, 2023
8f4d227
Added restore functionality
bsekachev Jun 15, 2023
a5d1a4f
Minor fixes
bsekachev Jun 15, 2023
70ec0e0
Update task or project when guide was changed
bsekachev Jun 15, 2023
3dc7d84
Some styles fixes
bsekachev Jun 16, 2023
667f7d4
Merge branch 'develop' into bs/md
bsekachev Jun 16, 2023
d0d6275
License headers, aborted extra changes in base.py
bsekachev Jun 16, 2023
83a494e
Fixed client warning
bsekachev Jun 16, 2023
2ea127c
Updated version & changelog
bsekachev Jun 16, 2023
844392f
Merged develop
bsekachev Jun 19, 2023
a8d0bc6
Fixed eslint issues
bsekachev Jun 19, 2023
69c0e83
Fixed pylint issues
bsekachev Jun 19, 2023
ded0cfd
Updated schema yml
bsekachev Jun 19, 2023
5d3b02b
Fixed e2e test and unit tests
bsekachev Jun 19, 2023
64dc246
Added guide_id to rest api tests
bsekachev Jun 19, 2023
77ba562
Applied comments
bsekachev Jun 20, 2023
0dadf33
Applied comments
bsekachev Jun 20, 2023
e50b713
Added API scheme description
bsekachev Jun 20, 2023
a3ae8da
Fixed pylint
bsekachev Jun 20, 2023
8fc4926
Updated schema
bsekachev Jun 20, 2023
69bb060
Added OPA tests
bsekachev Jun 21, 2023
d7f827f
Applied comments
bsekachev Jun 21, 2023
f3bba90
Added missed rules for admin
bsekachev Jun 21, 2023
b20724f
Updated schema
bsekachev Jun 21, 2023
fa2a335
Merge branch 'develop' into bs/md
bsekachev Jun 22, 2023
3ed3e36
Using target property
bsekachev Jun 22, 2023
e83b808
Updated permissions
bsekachev Jun 22, 2023
a69369c
Updated schema
bsekachev Jun 22, 2023
abdf402
Updated generator
bsekachev Jun 22, 2023
cb1d9cf
Removed outdated code
bsekachev Jun 23, 2023
8ef072c
Removed old code
bsekachev Jun 23, 2023
5753e7a
Updated is job staff method
bsekachev Jun 23, 2023
5b68998
Fixed unused report
bsekachev Jun 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## \[Unreleased]
### Added
- TDB
- 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>)

### Changed
- TDB
Expand Down
2 changes: 1 addition & 1 deletion cvat-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-core",
"version": "9.1.3",
"version": "9.2.0",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "src/api.ts",
"scripts": {
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 @@ -25,6 +25,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';

export default function implementAPI(cvat) {
cvat.plugins.list.implementation = PluginRegistry.list;
Expand Down Expand Up @@ -133,6 +135,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 @@ -287,6 +289,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
63 changes: 62 additions & 1 deletion 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 { Storage } from './storage';
import { StorageLocation, WebhookSourceType } from './enums';
Expand Down Expand Up @@ -2167,6 +2167,57 @@ async function receiveWebhookEvents(type: WebhookSourceType): Promise<string[]>
}
}

async function getGuide(id: number, params = {}): Promise<SerializedGuide> {
const { backendAPI } = config;

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

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 updateGuide(id: number, data: Partial<SerializedGuide>): Promise<SerializedGuide> {
const { backendAPI } = config;

try {
const response = await Axios.patch(`${backendAPI}/guides/${id}`, data);
return response.data;
} 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);
}
}

export default Object.freeze({
server: Object.freeze({
setAuthData,
Expand Down Expand Up @@ -2310,4 +2361,14 @@ export default Object.freeze({
ping: pingWebhook,
events: receiveWebhookEvents,
}),

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

assets: Object.freeze({
create: createAsset,
}),
});
25 changes: 23 additions & 2 deletions cvat-core/src/server-response-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface SerializedProject {
dimension: DimensionType;
name: string;
organization: number | null;
guide_id: number | null;
owner: SerializedUser;
source_storage: { id: number; location: 'local' | 'cloud'; cloud_storage_id: null };
target_storage: { id: number; location: 'local' | 'cloud'; cloud_storage_id: null };
Expand Down Expand Up @@ -94,6 +95,7 @@ export interface SerializedTask {
overlap: number | null;
owner: SerializedUser;
project_id: number | null;
guide_id: number | null;
segment_size: number;
size: number;
source_storage: { id: number; location: 'local' | 'cloud'; cloud_storage_id: null };
Expand All @@ -115,10 +117,11 @@ export interface SerializedJob {
labels: { count: number; url: string };
mode: TaskMode;
project_id: number | null;
guide_id: number | null;
stage: JobStage;
state: JobState;
startFrame: number;
stopFrame: number;
start_frame: number;
stop_frame: number;
task_id: number;
updated_date: string;
url: string;
Expand Down Expand Up @@ -174,3 +177,21 @@ export interface SerializedRegister {
last_name: string;
username: string;
}

export interface SerializedGuide {
id?: number;
task_id: number | null;
project_id: number | null;
owner: SerializedUser;
created_date: string;
updated_date: string;
markdown: string;
}

export interface SerializedAsset {
uuid?: string;
guide?: number;
filename: string;
created_date: string;
owner: SerializedUser;
}
Loading