diff --git a/packages/api-server/api_server/app.py b/packages/api-server/api_server/app.py index 6fc16a05c..1a4e8a4e4 100644 --- a/packages/api-server/api_server/app.py +++ b/packages/api-server/api_server/app.py @@ -196,13 +196,19 @@ def on_signal(sig, frame): for t in scheduled_tasks: user = await User.load_from_db(t.created_by) if user is None: - logger.warning(f"user [{t.created_by}] does not exist") + logger.warning(f"User who scheduled task, [{t.created_by}] does not exist") + logger.warning(f"Skipping request: [{t.task_request}]") continue task_repo = TaskRepository(user) - await routes.scheduled_tasks.schedule_task(t, task_repo) - scheduled += 1 - logger.info(f"loaded {scheduled} tasks") - logger.info("successfully started scheduler") + try: + await routes.scheduled_tasks.schedule_task(t, task_repo) + scheduled += 1 + except Exception as e: # pylint: disable=broad-except + logger.warning(f"Unable to schedule task requested by {t.created_by}: {e}") + logger.warning(f"Skipping request: [{t.task_request}]") + logger.info( + f"Retrieved {len(scheduled_tasks)} scheduled tasks, scheduled {scheduled} tasks" + ) ros.spin_background() logger.info("started app") diff --git a/packages/api-server/api_server/authenticator.py b/packages/api-server/api_server/authenticator.py index 4196fc48e..f680a9eb4 100644 --- a/packages/api-server/api_server/authenticator.py +++ b/packages/api-server/api_server/authenticator.py @@ -79,13 +79,22 @@ async def dep( class StubAuthenticator(JwtAuthenticator): def __init__(self): # pylint: disable=super-init-not-called - self._user = User(username="stub", is_admin=True) + self._user = None - async def verify_token(self, token: Optional[str]) -> User: + async def _get_user(self, claims: dict) -> User: + if self._user is None: + self._user = await User.load_or_create_from_db("stub") + await self._user.update_admin(True) return self._user + async def verify_token(self, token: Optional[str]) -> User: + return await self._get_user({}) + def fastapi_dep(self) -> Callable[..., Union[Coroutine[Any, Any, User], User]]: - return lambda: self._user + async def dep(): + return await self._get_user({}) + + return dep if app_config.jwt_public_key: diff --git a/packages/api-server/api_server/routes/tasks/scheduled_tasks.py b/packages/api-server/api_server/routes/tasks/scheduled_tasks.py index 992e8f9da..60f4dddd2 100644 --- a/packages/api-server/api_server/routes/tasks/scheduled_tasks.py +++ b/packages/api-server/api_server/routes/tasks/scheduled_tasks.py @@ -54,6 +54,7 @@ def do(): if datetime_to_iso[:10] in task.except_dates: return asyncio.get_event_loop().create_task(run()) + logger.warning(f"schedule has {len(schedule.get_jobs())} jobs left") for _, j in jobs: j.do(do) @@ -150,7 +151,10 @@ async def del_scheduled_tasks_event( for sche in task.schedules: schedule.clear(sche.get_id()) - await schedule_task(task, task_repo) + try: + await schedule_task(task, task_repo) + except schedule.ScheduleError as e: + raise HTTPException(422, str(e)) from e @router.post( diff --git a/packages/api-server/psql_local_config.py b/packages/api-server/psql_local_config.py index 5bb0c8fad..a61c39660 100644 --- a/packages/api-server/psql_local_config.py +++ b/packages/api-server/psql_local_config.py @@ -1,6 +1,8 @@ +import os + from sqlite_local_config import config -here = dirname(__file__) +here = os.path.dirname(__file__) run_dir = f"{here}/run" config.update( diff --git a/packages/dashboard/src/components/app-events.ts b/packages/dashboard/src/components/app-events.ts index 23230dc8a..c93bc1189 100644 --- a/packages/dashboard/src/components/app-events.ts +++ b/packages/dashboard/src/components/app-events.ts @@ -18,6 +18,7 @@ export const AppEvents = { robotSelect: new Subject<[fleetName: string, robotName: string] | null>(), taskSelect: new Subject(), refreshTaskApp: new Subject(), + refreshTaskSchedule: new Subject(), refreshAlert: new Subject(), alertListOpenedAlert: new Subject(), disabledLayers: new ReplaySubject>(), diff --git a/packages/dashboard/src/components/delivery-alert-store.tsx b/packages/dashboard/src/components/delivery-alert-store.tsx index 3f3f4f229..b267856da 100644 --- a/packages/dashboard/src/components/delivery-alert-store.tsx +++ b/packages/dashboard/src/components/delivery-alert-store.tsx @@ -693,12 +693,19 @@ export const DeliveryAlertStore = React.memo(() => { ); } + // Allow resume if the obstruction is related to a latching problem. return ( setAlerts((prev) => Object.fromEntries( diff --git a/packages/dashboard/src/components/tasks/task-schedule.tsx b/packages/dashboard/src/components/tasks/task-schedule.tsx index 800745221..ec1f73571 100644 --- a/packages/dashboard/src/components/tasks/task-schedule.tsx +++ b/packages/dashboard/src/components/tasks/task-schedule.tsx @@ -8,7 +8,7 @@ import { SchedulerHelpers, SchedulerProps, } from '@aldabil/react-scheduler/types'; -import { Button, Grid } from '@mui/material'; +import { Button } from '@mui/material'; import { ApiServerModelsTortoiseModelsScheduledTaskScheduledTask as ScheduledTask, ApiServerModelsTortoiseModelsScheduledTaskScheduledTaskScheduleLeaf as ApiSchedule, @@ -75,7 +75,7 @@ export const TaskSchedule = () => { useCreateTaskFormData(rmf); const username = useGetUsername(rmf); const [eventScope, setEventScope] = React.useState(EventScopes.CURRENT); - const [refreshTaskAppCount, setRefreshTaskAppCount] = React.useState(0); + const [refreshTaskScheduleCount, setRefreshTaskScheduleCount] = React.useState(0); const exceptDateRef = React.useRef(new Date()); const currentEventIdRef = React.useRef(-1); const [currentScheduleTask, setCurrentScheduledTask] = React.useState( @@ -92,9 +92,9 @@ export const TaskSchedule = () => { }); React.useEffect(() => { - const sub = AppEvents.refreshTaskApp.subscribe({ + const sub = AppEvents.refreshTaskSchedule.subscribe({ next: () => { - setRefreshTaskAppCount((oldValue) => ++oldValue); + setRefreshTaskScheduleCount((oldValue) => ++oldValue); }, }); return () => sub.unsubscribe(); @@ -144,7 +144,7 @@ export const TaskSchedule = () => { onClose={() => { scheduler.close(); setEventScope(EventScopes.CURRENT); - AppEvents.refreshTaskApp.next(); + AppEvents.refreshTaskSchedule.next(); }} onSubmit={() => { setOpenCreateTaskForm(true); @@ -157,7 +157,7 @@ export const TaskSchedule = () => { if (eventScope === EventScopes.CURRENT) { setScheduleToEdit(scheduleWithSelectedDay(task.schedules, exceptDateRef.current)); } - AppEvents.refreshTaskApp.next(); + AppEvents.refreshTaskSchedule.next(); scheduler.close(); }} > @@ -200,7 +200,7 @@ export const TaskSchedule = () => { ); setEventScope(EventScopes.CURRENT); - AppEvents.refreshTaskApp.next(); + AppEvents.refreshTaskSchedule.next(); }, [rmf, currentScheduleTask, eventScope], ); @@ -225,7 +225,7 @@ export const TaskSchedule = () => { } else { await rmf.tasksApi.delScheduledTasksScheduledTasksTaskIdDelete(task.id); } - AppEvents.refreshTaskApp.next(); + AppEvents.refreshTaskSchedule.next(); // Set the default values setOpenDeleteScheduleDialog(false); @@ -261,95 +261,15 @@ export const TaskSchedule = () => { disablingCellsWithoutEvents(calendarEvents, { start, ...props }), }; - interface ViewSettings { - daySettings: DayProps | null; - weekSettings: WeekProps | null; - monthSettings: MonthProps | null; - } - - const [viewSettings, setViewSettings] = React.useState({ - daySettings: null, - weekSettings: defaultWeekSettings, - monthSettings: null, - }); - - const translations = { - navigation: { - month: 'Month', - week: 'Week', - day: 'Day', - today: 'Go to today', - }, - form: { - addTitle: 'Add Event', - editTitle: 'Edit Event', - confirm: 'Confirm', - delete: 'Delete', - cancel: 'Cancel', - }, - event: { - title: 'Title', - start: 'Start', - end: 'End', - allDay: 'All Day', - }, - moreEvents: 'More...', - loading: 'Loading...', - }; - return (
- - - - - { value={eventScope} onChange={(event: React.ChangeEvent) => { setEventScope(event.target.value); - AppEvents.refreshTaskApp.next(); + AppEvents.refreshTaskSchedule.next(); }} /> )} - translations={{ - ...translations, - navigation: { - ...translations.navigation, - today: viewSettings.daySettings - ? 'Today' - : viewSettings.weekSettings - ? 'This week' - : 'This month', - }, - }} /> {openCreateTaskForm && ( { onClose={() => { setOpenCreateTaskForm(false); setEventScope(EventScopes.CURRENT); - AppEvents.refreshTaskApp.next(); + AppEvents.refreshTaskSchedule.next(); }} submitTasks={submitTasks} onSuccess={() => { diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 1ad79d44f..6bbffcd02 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -271,7 +271,7 @@ function DeliveryTaskForm({ id="pickup-location" freeSolo fullWidth - options={Object.keys(pickupPoints)} + options={Object.keys(pickupPoints).sort()} value={taskDesc.phases[0].activity.description.activities[0].description} onInputChange={(_ev, newValue) => { const pickupLot = pickupPoints[newValue] ?? ''; @@ -358,7 +358,7 @@ function DeliveryTaskForm({ id="dropoff-location" freeSolo fullWidth - options={Object.keys(dropoffPoints)} + options={Object.keys(dropoffPoints).sort()} value={taskDesc.phases[0].activity.description.activities[2].description} onInputChange={(_ev, newValue) => { const newTaskDesc = { ...taskDesc }; @@ -428,7 +428,7 @@ function DeliveryCustomTaskForm({ id="pickup-zone" freeSolo fullWidth - options={pickupZones} + options={pickupZones.sort()} value={taskDesc.phases[0].activity.description.activities[0].description} onInputChange={(_ev, newValue) => { const newTaskDesc = { ...taskDesc }; @@ -513,7 +513,7 @@ function DeliveryCustomTaskForm({ id="dropoff-location" freeSolo fullWidth - options={dropoffPoints} + options={dropoffPoints.sort()} value={taskDesc.phases[0].activity.description.activities[2].description} onInputChange={(_ev, newValue) => { const newTaskDesc = { ...taskDesc }; @@ -609,7 +609,7 @@ function PatrolTaskForm({ taskDesc, patrolWaypoints, onChange, allowSubmit }: Pa id="place-input" freeSolo fullWidth - options={patrolWaypoints} + options={patrolWaypoints.sort()} onChange={(_ev, newValue) => newValue !== null && onInputChange({