diff --git a/packages/api-client/lib/openapi/api.ts b/packages/api-client/lib/openapi/api.ts index d562cd848..cda5cff5b 100644 --- a/packages/api-client/lib/openapi/api.ts +++ b/packages/api-client/lib/openapi/api.ts @@ -431,55 +431,6 @@ export interface ApiServerModelsTortoiseModelsScheduledTaskScheduledTaskSchedule */ at?: string | null; } -/** - * - * @export - * @interface ApiServerModelsTortoiseModelsTasksTaskFavoriteLeaf - */ -export interface ApiServerModelsTortoiseModelsTasksTaskFavoriteLeaf { - /** - * - * @type {string} - * @memberof ApiServerModelsTortoiseModelsTasksTaskFavoriteLeaf - */ - id: string; - /** - * - * @type {string} - * @memberof ApiServerModelsTortoiseModelsTasksTaskFavoriteLeaf - */ - name: string; - /** - * - * @type {string} - * @memberof ApiServerModelsTortoiseModelsTasksTaskFavoriteLeaf - */ - unix_millis_earliest_start_time?: string | null; - /** - * - * @type {any} - * @memberof ApiServerModelsTortoiseModelsTasksTaskFavoriteLeaf - */ - priority?: any; - /** - * - * @type {string} - * @memberof ApiServerModelsTortoiseModelsTasksTaskFavoriteLeaf - */ - category: string; - /** - * - * @type {any} - * @memberof ApiServerModelsTortoiseModelsTasksTaskFavoriteLeaf - */ - description?: any; - /** - * - * @type {string} - * @memberof ApiServerModelsTortoiseModelsTasksTaskFavoriteLeaf - */ - user: string; -} /** * Which agent (robot) is the task assigned to * @export @@ -2657,51 +2608,57 @@ export interface TaskEventLog { /** * * @export - * @interface TaskFavoritePydantic + * @interface TaskFavorite */ -export interface TaskFavoritePydantic { +export interface TaskFavorite { /** * * @type {string} - * @memberof TaskFavoritePydantic + * @memberof TaskFavorite */ id: string; /** * * @type {string} - * @memberof TaskFavoritePydantic + * @memberof TaskFavorite */ name: string; /** * * @type {number} - * @memberof TaskFavoritePydantic + * @memberof TaskFavorite */ unix_millis_earliest_start_time: number; /** * * @type {object} - * @memberof TaskFavoritePydantic + * @memberof TaskFavorite */ priority?: object; /** * * @type {string} - * @memberof TaskFavoritePydantic + * @memberof TaskFavorite */ category: string; /** * * @type {object} - * @memberof TaskFavoritePydantic + * @memberof TaskFavorite */ description?: object; /** * * @type {string} - * @memberof TaskFavoritePydantic + * @memberof TaskFavorite */ user: string; + /** + * + * @type {Array} + * @memberof TaskFavorite + */ + labels?: Array; } /** * @@ -8885,20 +8842,16 @@ export const TasksApiAxiosParamCreator = function (configuration?: Configuration /** * * @summary Post Favorite Task - * @param {TaskFavoritePydantic} taskFavoritePydantic + * @param {TaskFavorite} taskFavorite * @param {*} [options] Override http request option. * @throws {RequiredError} */ postFavoriteTaskFavoriteTasksPost: async ( - taskFavoritePydantic: TaskFavoritePydantic, + taskFavorite: TaskFavorite, options: AxiosRequestConfig = {}, ): Promise => { - // verify required parameter 'taskFavoritePydantic' is not null or undefined - assertParamExists( - 'postFavoriteTaskFavoriteTasksPost', - 'taskFavoritePydantic', - taskFavoritePydantic, - ); + // verify required parameter 'taskFavorite' is not null or undefined + assertParamExists('postFavoriteTaskFavoriteTasksPost', 'taskFavorite', taskFavorite); const localVarPath = `/favorite_tasks`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -8921,7 +8874,7 @@ export const TasksApiAxiosParamCreator = function (configuration?: Configuration ...options.headers, }; localVarRequestOptions.data = serializeDataIfNeeded( - taskFavoritePydantic, + taskFavorite, localVarRequestOptions, configuration, ); @@ -9654,9 +9607,7 @@ export const TasksApiFp = function (configuration?: Configuration) { */ async getFavoritesTasksFavoriteTasksGet( options?: AxiosRequestConfig, - ): Promise< - (axios?: AxiosInstance, basePath?: string) => AxiosPromise> - > { + ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getFavoritesTasksFavoriteTasksGet( options, ); @@ -9846,21 +9797,16 @@ export const TasksApiFp = function (configuration?: Configuration) { /** * * @summary Post Favorite Task - * @param {TaskFavoritePydantic} taskFavoritePydantic + * @param {TaskFavorite} taskFavorite * @param {*} [options] Override http request option. * @throws {RequiredError} */ async postFavoriteTaskFavoriteTasksPost( - taskFavoritePydantic: TaskFavoritePydantic, + taskFavorite: TaskFavorite, options?: AxiosRequestConfig, - ): Promise< - ( - axios?: AxiosInstance, - basePath?: string, - ) => AxiosPromise - > { + ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.postFavoriteTaskFavoriteTasksPost( - taskFavoritePydantic, + taskFavorite, options, ); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); @@ -10191,7 +10137,7 @@ export const TasksApiFactory = function ( * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getFavoritesTasksFavoriteTasksGet(options?: any): AxiosPromise> { + getFavoritesTasksFavoriteTasksGet(options?: any): AxiosPromise> { return localVarFp .getFavoritesTasksFavoriteTasksGet(options) .then((request) => request(axios, basePath)); @@ -10345,16 +10291,16 @@ export const TasksApiFactory = function ( /** * * @summary Post Favorite Task - * @param {TaskFavoritePydantic} taskFavoritePydantic + * @param {TaskFavorite} taskFavorite * @param {*} [options] Override http request option. * @throws {RequiredError} */ postFavoriteTaskFavoriteTasksPost( - taskFavoritePydantic: TaskFavoritePydantic, + taskFavorite: TaskFavorite, options?: any, - ): AxiosPromise { + ): AxiosPromise { return localVarFp - .postFavoriteTaskFavoriteTasksPost(taskFavoritePydantic, options) + .postFavoriteTaskFavoriteTasksPost(taskFavorite, options) .then((request) => request(axios, basePath)); }, /** @@ -10815,17 +10761,17 @@ export class TasksApi extends BaseAPI { /** * * @summary Post Favorite Task - * @param {TaskFavoritePydantic} taskFavoritePydantic + * @param {TaskFavorite} taskFavorite * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TasksApi */ public postFavoriteTaskFavoriteTasksPost( - taskFavoritePydantic: TaskFavoritePydantic, + taskFavorite: TaskFavorite, options?: AxiosRequestConfig, ) { return TasksApiFp(this.configuration) - .postFavoriteTaskFavoriteTasksPost(taskFavoritePydantic, options) + .postFavoriteTaskFavoriteTasksPost(taskFavorite, options) .then((request) => request(this.axios, this.basePath)); } diff --git a/packages/api-client/lib/version.ts b/packages/api-client/lib/version.ts index fa3fd8f02..9fa05261e 100644 --- a/packages/api-client/lib/version.ts +++ b/packages/api-client/lib/version.ts @@ -3,6 +3,6 @@ import { version as rmfModelVer } from 'rmf-models'; export const version = { rmfModels: rmfModelVer, - rmfServer: 'e78c8685138183b4743776c71ff2c1d990ea52f5', + rmfServer: '4c9eb01b568dd3b5e98d46fd3571d2e5b173179e', openapiGenerator: '6.2.1', }; diff --git a/packages/api-client/schema/index.ts b/packages/api-client/schema/index.ts index 26d54df74..92f72e087 100644 --- a/packages/api-client/schema/index.ts +++ b/packages/api-client/schema/index.ts @@ -1552,7 +1552,7 @@ export default { schema: { title: 'Response Get Favorites Tasks Favorite Tasks Get', type: 'array', - items: { $ref: '#/components/schemas/TaskFavoritePydantic' }, + items: { $ref: '#/components/schemas/TaskFavorite' }, }, }, }, @@ -1565,20 +1565,14 @@ export default { operationId: 'post_favorite_task_favorite_tasks_post', requestBody: { content: { - 'application/json': { schema: { $ref: '#/components/schemas/TaskFavoritePydantic' } }, + 'application/json': { schema: { $ref: '#/components/schemas/TaskFavorite' } }, }, required: true, }, responses: { '200': { description: 'Successful Response', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/api_server.models.tortoise_models.tasks.TaskFavorite.leaf', - }, - }, - }, + content: { 'application/json': { schema: {} } }, }, '422': { description: 'Validation Error', @@ -3734,8 +3728,8 @@ export default { }, additionalProperties: false, }, - TaskFavoritePydantic: { - title: 'TaskFavoritePydantic', + TaskFavorite: { + title: 'TaskFavorite', required: ['id', 'name', 'unix_millis_earliest_start_time', 'category', 'user'], type: 'object', properties: { @@ -3749,6 +3743,7 @@ export default { category: { title: 'Category', type: 'string' }, description: { title: 'Description', type: 'object' }, user: { title: 'User', type: 'string' }, + labels: { title: 'Labels', type: 'array', items: { type: 'string' } }, }, }, TaskInterruptionRequest: { @@ -4263,26 +4258,6 @@ export default { $ref: '#/components/schemas/api_server.models.tortoise_models.scheduled_task.ScheduledTask', }, }, - 'api_server.models.tortoise_models.tasks.TaskFavorite.leaf': { - title: 'TaskFavorite', - required: ['id', 'name', 'category', 'user'], - type: 'object', - properties: { - id: { title: 'Id', maxLength: 255, type: 'string' }, - name: { title: 'Name', maxLength: 255, type: 'string' }, - unix_millis_earliest_start_time: { - title: 'Unix Millis Earliest Start Time', - type: 'string', - format: 'date-time', - nullable: true, - }, - priority: { title: 'Priority' }, - category: { title: 'Category', maxLength: 255, type: 'string' }, - description: { title: 'Description' }, - user: { title: 'User', maxLength: 255, type: 'string' }, - }, - additionalProperties: false, - }, api_server__models__delivery_alerts__DeliveryAlert__Category: { title: 'Category', enum: ['missing', 'wrong', 'obstructed', 'cancelled'], diff --git a/packages/api-server/api_server/models/__init__.py b/packages/api-server/api_server/models/__init__.py index db2e63956..238fdf04b 100644 --- a/packages/api-server/api_server/models/__init__.py +++ b/packages/api-server/api_server/models/__init__.py @@ -51,4 +51,5 @@ from .rmf_api.undo_skip_phase_request import UndoPhaseSkipRequest from .rmf_api.undo_skip_phase_response import UndoPhaseSkipResponse from .task_booking_label import * +from .task_favorite import * from .user import * diff --git a/packages/api-server/api_server/models/task_favorite.py b/packages/api-server/api_server/models/task_favorite.py new file mode 100644 index 000000000..2714954fd --- /dev/null +++ b/packages/api-server/api_server/models/task_favorite.py @@ -0,0 +1,14 @@ +from typing import Dict, List + +from pydantic import BaseModel + + +class TaskFavorite(BaseModel): + id: str + name: str + unix_millis_earliest_start_time: int + priority: Dict | None + category: str + description: Dict | None + user: str + labels: List[str] | None diff --git a/packages/api-server/api_server/models/tortoise_models/__init__.py b/packages/api-server/api_server/models/tortoise_models/__init__.py index 301bed861..0028a5e78 100644 --- a/packages/api-server/api_server/models/tortoise_models/__init__.py +++ b/packages/api-server/api_server/models/tortoise_models/__init__.py @@ -26,7 +26,6 @@ TaskEventLogPhasesEventsLog, TaskEventLogPhasesLog, TaskFavorite, - TaskFavoritePydantic, TaskRequest, TaskState, ) diff --git a/packages/api-server/api_server/models/tortoise_models/tasks.py b/packages/api-server/api_server/models/tortoise_models/tasks.py index f36ac3fbc..d0156e8bb 100644 --- a/packages/api-server/api_server/models/tortoise_models/tasks.py +++ b/packages/api-server/api_server/models/tortoise_models/tasks.py @@ -1,4 +1,3 @@ -from tortoise.contrib.pydantic.creator import pydantic_model_creator from tortoise.fields import ( CharField, DatetimeField, @@ -80,6 +79,4 @@ class TaskFavorite(Model): category = CharField(255, null=False, index=True) description = JSONField() user = CharField(255, null=False, index=True) - - -TaskFavoritePydantic = pydantic_model_creator(TaskFavorite) + labels = JSONField(null=True, default=list) diff --git a/packages/api-server/api_server/routes/tasks/favorite_tasks.py b/packages/api-server/api_server/routes/tasks/favorite_tasks.py index d3dcabef5..6af718d30 100644 --- a/packages/api-server/api_server/routes/tasks/favorite_tasks.py +++ b/packages/api-server/api_server/routes/tasks/favorite_tasks.py @@ -1,32 +1,21 @@ import uuid from datetime import datetime -from typing import Dict, List +from typing import List from fastapi import Depends, HTTPException -from pydantic import BaseModel from tortoise.exceptions import IntegrityError from api_server.authenticator import user_dep from api_server.fast_io import FastIORouter -from api_server.models import User +from api_server.models import TaskFavorite, User from api_server.models import tortoise_models as ttm router = FastIORouter(tags=["Tasks"]) -class TaskFavoritePydantic(BaseModel): - id: str - name: str - unix_millis_earliest_start_time: int - priority: Dict | None - category: str - description: Dict | None - user: str - - -@router.post("", response_model=ttm.TaskFavoritePydantic) +@router.post("") async def post_favorite_task( - request: TaskFavoritePydantic, + request: TaskFavorite, user: User = Depends(user_dep), ): try: @@ -40,6 +29,7 @@ async def post_favorite_task( "category": request.category, "description": request.description if request.description else None, "user": user.username, + "labels": request.labels, }, id=request.id if request.id != "" else uuid.uuid4(), ) @@ -47,13 +37,13 @@ async def post_favorite_task( raise HTTPException(422, str(e)) from e -@router.get("", response_model=List[TaskFavoritePydantic]) +@router.get("", response_model=List[TaskFavorite]) async def get_favorites_tasks( user: User = Depends(user_dep), ): favorites_tasks = await ttm.TaskFavorite.filter(user=user.username) return [ - TaskFavoritePydantic( + TaskFavorite( id=favorite_task.id, name=favorite_task.name, unix_millis_earliest_start_time=int( @@ -65,6 +55,7 @@ async def get_favorites_tasks( if favorite_task.description else None, user=user.username, + labels=favorite_task.labels, ) for favorite_task in favorites_tasks ] diff --git a/packages/api-server/api_server/test/test_data.py b/packages/api-server/api_server/test/test_data.py index 21b028a9d..6ac37a302 100644 --- a/packages/api-server/api_server/test/test_data.py +++ b/packages/api-server/api_server/test/test_data.py @@ -25,9 +25,9 @@ TaskBookingLabel, TaskBookingLabelDescription, TaskEventLog, + TaskFavorite, TaskState, ) -from api_server.models import tortoise_models as ttm def make_door(name: str = "test_door") -> Door: @@ -450,7 +450,7 @@ def make_task_state(task_id: str = "test_task") -> TaskState: def make_task_favorite( favorite_task_id: str = "default_id", -) -> ttm.TaskFavoritePydantic: +) -> TaskFavorite: sample_favorite_task = json.loads( """ { @@ -479,7 +479,7 @@ def make_task_favorite( """ ) sample_favorite_task["id"] = favorite_task_id - return ttm.TaskFavoritePydantic(**sample_favorite_task) + return TaskFavorite(**sample_favorite_task) def make_task_log(task_id: str) -> TaskEventLog: diff --git a/packages/dashboard/src/components/appbar.tsx b/packages/dashboard/src/components/appbar.tsx index df0a51218..fb5514703 100644 --- a/packages/dashboard/src/components/appbar.tsx +++ b/packages/dashboard/src/components/appbar.tsx @@ -31,7 +31,7 @@ import { } from '@mui/material'; import { ApiServerModelsTortoiseModelsAlertsAlertLeaf as Alert, - TaskFavoritePydantic as TaskFavorite, + TaskFavorite, TaskRequest, } from 'api-client'; import React from 'react'; diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 7dcf83486..f6ec34a5f 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -38,16 +38,15 @@ import { useTheme, } from '@mui/material'; import { DatePicker, TimePicker, DateTimePicker } from '@mui/x-date-pickers'; -import type { - TaskBookingLabel, - TaskFavoritePydantic as TaskFavorite, - TaskRequest, -} from 'api-client'; +import type { TaskBookingLabel, TaskFavorite, TaskRequest } from 'api-client'; import React from 'react'; import { Loading } from '..'; import { ConfirmationDialog, ConfirmationDialogProps } from '../confirmation-dialog'; import { PositiveIntField } from '../form-inputs'; -import { serializeTaskBookingLabel } from './task-booking-label-utils'; +import { + getTaskBookingLabelFromJsonString, + serializeTaskBookingLabel, +} from './task-booking-label-utils'; // A bunch of manually defined descriptions to avoid using `any`. export interface PatrolTaskDescription { @@ -1213,6 +1212,7 @@ const defaultFavoriteTask = (): TaskFavorite => { unix_millis_earliest_start_time: 0, priority: { type: 'binary', value: 0 }, user: '', + labels: [], }; }; @@ -1292,7 +1292,18 @@ export function CreateTaskForm({ const [taskRequest, setTaskRequest] = React.useState( () => requestTask ?? defaultTask(), ); - const [requestLabel, setRequestLabel] = React.useState({ description: {} }); + + let bookingLabel: TaskBookingLabel = { description: {} }; + if (requestTask && requestTask.labels) { + for (const label of requestTask.labels) { + const parsedLabel = getTaskBookingLabelFromJsonString(label); + if (parsedLabel) { + bookingLabel = parsedLabel; + } + } + } + const [requestBookingLabel, setRequestBookingLabel] = + React.useState(bookingLabel); const [submitting, setSubmitting] = React.useState(false); const [formFullyFilled, setFormFullyFilled] = React.useState(requestTask !== undefined || false); @@ -1366,7 +1377,7 @@ export function CreateTaskForm({ patrolWaypoints={patrolWaypoints} onChange={(desc) => { handleTaskDescriptionChange('patrol', desc); - setRequestLabel((prev) => { + setRequestBookingLabel((prev) => { return { description: { ...prev.description, @@ -1385,7 +1396,7 @@ export function CreateTaskForm({ taskDesc={taskRequest.description as CustomComposeTaskDescription} onChange={(desc) => { handleCustomComposeTaskDescriptionChange(desc); - setRequestLabel((prev) => { + setRequestBookingLabel((prev) => { return { description: { ...prev.description, @@ -1414,7 +1425,7 @@ export function CreateTaskForm({ handleTaskDescriptionChange('compose', desc); const pickupPerformAction = desc.phases[0].activity.description.activities[1].description.description; - setRequestLabel((prev) => { + setRequestBookingLabel((prev) => { return { description: { ...prev.description, @@ -1444,7 +1455,7 @@ export function CreateTaskForm({ handleTaskDescriptionChange('compose', desc); const pickupPerformAction = desc.phases[0].activity.description.activities[1].description.description; - setRequestLabel((prev) => { + setRequestBookingLabel((prev) => { return { description: { ...prev.description, @@ -1466,6 +1477,7 @@ export function CreateTaskForm({ const handleTaskTypeChange = (ev: React.ChangeEvent) => { const newType = ev.target.value; setTaskType(newType); + setRequestBookingLabel({ description: { task_name: newType } }); if (newType === 'custom_compose') { taskRequest.category = 'custom_compose'; @@ -1550,16 +1562,11 @@ export function CreateTaskForm({ } try { - const labelString = serializeTaskBookingLabel(requestLabel); + const labelString = serializeTaskBookingLabel(requestBookingLabel); if (labelString) { - console.log('pushing label: '); - console.log(labelString); - if (request.labels) { - request.labels.push(labelString); - } else { - request.labels = [labelString]; - } + request.labels = [labelString]; } + console.log(`labels: ${request.labels}`); } catch (e) { console.error('Failed to generate string for task request label'); } @@ -1609,7 +1616,11 @@ export function CreateTaskForm({ } try { setSavingFavoriteTask(true); - await submitFavoriteTask(favoriteTaskBuffer); + + const favoriteTask = favoriteTaskBuffer; + favoriteTask.labels = [serializeTaskBookingLabel(requestBookingLabel)]; + + await submitFavoriteTask(favoriteTask); setSavingFavoriteTask(false); onSuccessFavoriteTask && onSuccessFavoriteTask( @@ -1689,6 +1700,16 @@ export function CreateTaskForm({ unix_millis_earliest_start_time: 0, priority: favoriteTask.priority, }); + let bookingLabel: TaskBookingLabel = { description: {} }; + if (favoriteTask.labels) { + for (const label of favoriteTask.labels) { + const parsedLabel = getTaskBookingLabelFromJsonString(label); + if (parsedLabel) { + bookingLabel = parsedLabel; + } + } + } + setRequestBookingLabel(bookingLabel); }} /> );