Skip to content

Commit

Permalink
Update detail view for job definition (#92)
Browse files Browse the repository at this point in the history
* add jobDefinition?: boolean to IJobDetailModel

* manually set job definition to true

* create job definition card

* update conditional field on the model used for Job Definition rendering to be in line with Create Job

* add mock job definition rendering

* create separate DetailView

* swap detail-view into notebook-jobs-panel.tsx

* adjust file composition

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* linebreak

* adjust type

* restore functionality

* JobDetail -> DetailView

* create JobDetail

* fix element update logic

* change initial jobModel state to null

* add output file formats back

* pull Box and Stack up

* pull Box and Stack up into parent

* add getJobDefinition

* add fetchJobDefinitionlModel

* add job-definition

* fix style

* remove comments

* remove detailType? from IJobDetailModel

* pull breadcrumbs and title into parent View component

* pull Job Detail handleModelChange into parent view

* update IJobDefinitionModel and create convertDescribeJobDefinitiontoJobDefinition

* update name

* update model

* update in line with new interfaces

* finalize jobDefinition

* implement Delete Job Definition button, remove mock of Pause Job Definition button as functionality is not yet implemented

* add cronstrue, show human-readable cron expression

* fix duplicate breadcrumbs and heading in job def detail

* fix PATCH job definitions active

* add pause/resume buttons

* delete notebook jobs navigation components

* fix detail model hierarchy

* create folder for detail-view components, update imports

* add index for detail-view folder to import from, update imports

* add setJobsView and setJobsListView methods

* show full cron expression in job def detail

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: David L. Qiu <david@qiu.dev>
  • Loading branch information
3 people committed Oct 7, 2022
1 parent 3c53dd9 commit 2598838
Show file tree
Hide file tree
Showing 12 changed files with 488 additions and 225 deletions.
6 changes: 5 additions & 1 deletion jupyter_scheduler/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ async def post(self):
@tornado.web.authenticated
async def patch(self, job_definition_id):
payload = self.get_json_body()
await ensure_async(self.scheduler.update_job_definition(UpdateJobDefinition(**payload)))
await ensure_async(
self.scheduler.update_job_definition(
UpdateJobDefinition(**payload, job_definition_id=job_definition_id)
)
)
self.set_status(204)
self.finish()

Expand Down
1 change: 1 addition & 0 deletions jupyter_scheduler/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ class UpdateJobDefinition(BaseModel):
url: Optional[str] = None
timezone: Optional[str] = None # Should be a timezone e.g., US/Eastern, Asia/Kolkata
output_filename_template: Optional[str] = OUTPUT_FILENAME_TEMPLATE
active: bool


class ListJobDefinitionsQuery(BaseModel):
Expand Down
37 changes: 0 additions & 37 deletions src/components/notebook-jobs-navigation-tab-list.tsx

This file was deleted.

27 changes: 0 additions & 27 deletions src/components/notebook-jobs-navigation-tab.tsx

This file was deleted.

38 changes: 0 additions & 38 deletions src/components/notebook-jobs-navigation.tsx

This file was deleted.

60 changes: 60 additions & 0 deletions src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,25 @@ export class SchedulerService {
options.serverSettings || ServerConnection.makeSettings();
}

async getJobDefinition(
definition_id: string
): Promise<Scheduler.IDescribeJobDefinition> {
let data;

try {
data = await requestAPI(
this.serverSettings,
`job_definitions/${definition_id}`,
{
method: 'GET'
}
);
} catch (e: any) {
console.error(e);
}
return data as Scheduler.IDescribeJobDefinition;
}

async getJobDefinitions(
definition_id: string
): Promise<Scheduler.IDescribeJobDefinition[]> {
Expand Down Expand Up @@ -42,6 +61,25 @@ export class SchedulerService {
return data as Scheduler.IDescribeJobDefinition;
}

async deleteJobDefinition(
definition_id: string
): Promise<Scheduler.IDescribeJobDefinition> {
let data;

try {
data = await requestAPI(
this.serverSettings,
`job_definitions/${definition_id}`,
{
method: 'DELETE'
}
);
} catch (e: any) {
console.error(e);
}
return data as Scheduler.IDescribeJobDefinition;
}

async getJob(job_id: string): Promise<Scheduler.IDescribeJob> {
let data;
let query = '';
Expand Down Expand Up @@ -182,6 +220,28 @@ export class SchedulerService {
}
}

async pauseJobDefinition(jobDefId: string): Promise<void> {
try {
await requestAPI(this.serverSettings, `job_definitions/${jobDefId}`, {
method: 'PATCH',
body: JSON.stringify({ active: false })
});
} catch (e) {
console.error(e);
}
}

async resumeJobDefinition(jobDefId: string): Promise<void> {
try {
await requestAPI(this.serverSettings, `job_definitions/${jobDefId}`, {
method: 'PATCH',
body: JSON.stringify({ active: true })
});
} catch (e) {
console.error(e);
}
}

/**
* The server settings used to make API requests.
*/
Expand Down
159 changes: 159 additions & 0 deletions src/mainviews/detail-view/detail-view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import React, { useEffect, useState } from 'react';

import { JupyterFrontEnd } from '@jupyterlab/application';
import {
convertDescribeDefinitiontoDefinition,
convertDescribeJobtoJobDetail,
ICreateJobModel,
IDetailViewModel,
IJobDefinitionModel,
IJobDetailModel,
JobsView,
ListJobsView
} from '../../model';
import { useTranslator } from '../../hooks';
import { SchedulerService } from '../../handler';
import { Scheduler } from '../../tokens';
import { JobDetail } from './job-detail';
import { JobDefinition } from './job-definition';

import Stack from '@mui/material/Stack';
import {
Box,
Breadcrumbs,
CircularProgress,
Link,
Typography
} from '@mui/material';
import { Heading } from '../../components/heading';

export interface IDetailViewProps {
app: JupyterFrontEnd;
model: IDetailViewModel;
setCreateJobModel: (createModel: ICreateJobModel) => void;
setJobsView: (view: JobsView) => void;
setListJobsView: (view: ListJobsView) => void;
// Extension point: optional additional component
advancedOptions: React.FunctionComponent<Scheduler.IAdvancedOptionsProps>;
}

interface ILoadingProps {
title: string;
}

const Loading = (props: ILoadingProps) => (
<Stack direction="row" justifyContent="center">
<CircularProgress title={props.title} />
</Stack>
);

/**
* Renders both the job details view and the job definition details view,
* dispatching on `props.model.detailType`.
*/
export function DetailView(props: IDetailViewProps): JSX.Element {
const [jobModel, setJobsModel] = useState<IJobDetailModel | null>(null);
const [jobDefinitionModel, setJobDefinitionModel] =
useState<IJobDefinitionModel | null>(null);
const [outputFormatStrings, setOutputFormatStrings] = useState<
string[] | null
>(null);

const trans = useTranslator('jupyterlab');

const ss = new SchedulerService({});

const fetchJobDetailModel = async () => {
const jobFromService = await ss.getJob(props.model.id);
setOutputFormatStrings(jobFromService.output_formats ?? []);
const jobDetailModel = convertDescribeJobtoJobDetail(jobFromService);
setJobsModel(jobDetailModel);
};

const fetchJobDefinitionModel = async () => {
const definitionFromService = await ss.getJobDefinition(props.model.id);
const jobDefinitionModel = convertDescribeDefinitiontoDefinition(
definitionFromService
);
setJobDefinitionModel(jobDefinitionModel);
};

useEffect(() => {
switch (props.model.detailType) {
case 'Job':
fetchJobDetailModel();
break;
case 'JobDefinition':
fetchJobDefinitionModel();
break;
}
}, [props.model]);

const BreadcrumbsStyled = (
<div role="presentation">
<Breadcrumbs aria-label="breadcrumb">
<Link
underline="hover"
color="inherit"
onClick={(
_:
| React.MouseEvent<HTMLAnchorElement, MouseEvent>
| React.MouseEvent<HTMLSpanElement, MouseEvent>
): void => {
props.setJobsView('ListJobs');
props.setListJobsView(
props.model.detailType === 'Job' ? 'Job' : 'JobDefinition'
);
}}
>
{props.model.detailType === 'Job'
? trans.__('Notebook Jobs')
: trans.__('Notebook Job Definitions')}
</Link>
<Typography color="text.primary">
{props.model.detailType === 'Job'
? jobModel?.jobName ?? ''
: jobDefinitionModel?.name ?? ''}
</Typography>
</Breadcrumbs>
</div>
);

if (props.model.detailType) {
return (
<Box sx={{ p: 4 }}>
<Stack spacing={4}>
{BreadcrumbsStyled}
<Heading level={1}>
{props.model.detailType === 'Job'
? trans.__('Job Detail')
: trans.__('Job Definition')}
</Heading>
{props.model.detailType === 'Job' && jobModel && (
<JobDetail
app={props.app}
model={jobModel}
handleModelChange={() => fetchJobDetailModel()}
setCreateJobModel={props.setCreateJobModel}
setJobsView={props.setJobsView}
setListJobsView={props.setListJobsView}
// Extension point: optional additional component
advancedOptions={props.advancedOptions}
outputFormatsStrings={outputFormatStrings ?? []}
/>
)}
{props.model.detailType === 'JobDefinition' && jobDefinitionModel && (
<JobDefinition
model={jobDefinitionModel}
setJobsView={props.setJobsView}
setListJobsView={props.setListJobsView}
refresh={fetchJobDefinitionModel}
/>
)}
</Stack>
</Box>
);
}

return <Loading title={trans.__('Loading')} />;
}
1 change: 1 addition & 0 deletions src/mainviews/detail-view/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { DetailView } from './detail-view';
Loading

0 comments on commit 2598838

Please sign in to comment.