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

Added telemetry support. #448

Merged
merged 6 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ dmypy.json
# job outputs in local development env
dev/jobs

# Notebook files created in dev folder
dev/*.ipynb

# jupyter releaser local checkout
.jupyter_releaser_checkout

Expand Down
7 changes: 6 additions & 1 deletion src/components/create-schedule-options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { ChangeEvent } from 'react';
import { FormControlLabel, InputLabel, Radio, RadioGroup } from '@mui/material';
import Stack from '@mui/system/Stack';

import { useTranslator } from '../hooks';
import { useEventLogger, useTranslator } from '../hooks';
import { ICreateJobModel } from '../model';
import { ScheduleInputs } from './schedule-inputs';
import { Scheduler } from '../tokens';
Expand All @@ -26,10 +26,15 @@ export function CreateScheduleOptions(

const labelId = `${props.id}-label`;

const log = useEventLogger();

const handleScheduleOptionsChange = (
event: ChangeEvent<HTMLInputElement>,
value: string
) => {
log(
`create-job.job-type.${value === 'Job' ? 'run-now' : 'run-on-schedule'}`
);
const name = event.target.name;
props.handleModelChange({ ...props.model, [name]: value });
};
Expand Down
11 changes: 9 additions & 2 deletions src/components/job-definition-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import TableRow from '@mui/material/TableRow';
import TableCell from '@mui/material/TableCell';

import { Scheduler, SchedulerService } from '../handler';
import { useTranslator } from '../hooks';
import { useEventLogger, useTranslator } from '../hooks';
import { TranslationBundle } from '@jupyterlab/translation';
import { ConfirmDeleteButton } from './confirm-buttons';

Expand Down Expand Up @@ -94,10 +94,14 @@ export function buildJobDefinitionRow(
ss: SchedulerService,
handleApiError: (error: string | null) => void
): JSX.Element {
const log = useEventLogger();
const cellContents: React.ReactNode[] = [
// name
<Link
onClick={() => openJobDefinitionDetail(jobDef.job_definition_id)}
onClick={() => {
log('job-definition-list.open-detail');
openJobDefinitionDetail(jobDef.job_definition_id);
}}
title={`Open detail view for "${jobDef.name}"`}
>
{jobDef.name}
Expand All @@ -110,6 +114,7 @@ export function buildJobDefinitionRow(
<PauseButton
jobDef={jobDef}
clickHandler={async () => {
log('job-definition-list.pause');
handleApiError(null);
ss.pauseJobDefinition(jobDef.job_definition_id)
.then(_ => {
Expand All @@ -123,6 +128,7 @@ export function buildJobDefinitionRow(
<ResumeButton
jobDef={jobDef}
clickHandler={async () => {
log('job-definition-list.resume');
handleApiError(null);
ss.resumeJobDefinition(jobDef.job_definition_id)
.then(_ => {
Expand All @@ -136,6 +142,7 @@ export function buildJobDefinitionRow(
<ConfirmDeleteButton
name={jobDef.name}
clickHandler={async () => {
log('job-definition-list.delete');
handleApiError(null);
ss.deleteJobDefinition(jobDef.job_definition_id)
.then(_ => {
Expand Down
14 changes: 13 additions & 1 deletion src/components/job-file-link.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

import { Scheduler } from '../handler';
import { useTranslator } from '../hooks';
import { useEventLogger, useTranslator } from '../hooks';

import { JupyterFrontEnd } from '@jupyterlab/application';

Expand All @@ -11,6 +11,7 @@ export interface IJobFileLinkProps {
jobFile: Scheduler.IJobFile;
app: JupyterFrontEnd;
children?: React.ReactNode;
parentComponentName?: string;
}

export function JobFileLink(props: IJobFileLinkProps): JSX.Element | null {
Expand All @@ -20,6 +21,8 @@ export function JobFileLink(props: IJobFileLinkProps): JSX.Element | null {
return null;
}

const log = useEventLogger();

const fileBaseName = props.jobFile.file_path.split('/').pop();

const title =
Expand All @@ -38,6 +41,15 @@ export function JobFileLink(props: IJobFileLinkProps): JSX.Element | null {
| React.MouseEvent<HTMLAnchorElement, MouseEvent>
) => {
e.preventDefault();
if (props.parentComponentName) {
log(
`${props.parentComponentName}.${
props.jobFile.file_format === 'input'
? 'open-input-file'
: 'open-output-file'
}`
);
}
props.app.commands.execute('docmanager:open', {
path: props.jobFile.file_path
});
Expand Down
37 changes: 29 additions & 8 deletions src/components/job-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ConfirmDeleteButton, ConfirmButton } from './confirm-buttons';
import { JobFileLink } from './job-file-link';
import { CommandIDs } from '..';
import { Scheduler } from '../handler';
import { useTranslator } from '../hooks';
import { useEventLogger, useTranslator } from '../hooks';
import { ICreateJobModel } from '../model';
import DownloadIcon from '@mui/icons-material/Download';
import StopIcon from '@mui/icons-material/Stop';
Expand All @@ -16,6 +16,7 @@ function StopButton(props: {
clickHandler: () => void;
}): JSX.Element | null {
const trans = useTranslator('jupyterlab');
const log = useEventLogger();
const buttonTitle = props.job.name
? trans.__('Stop "%1"', props.job.name)
: trans.__('Stop job');
Expand All @@ -26,7 +27,10 @@ function StopButton(props: {
>
<ConfirmButton
name={buttonTitle}
onConfirm={props.clickHandler}
onConfirm={() => {
log('job-list.stop-confirm');
props.clickHandler();
}}
confirmationText={trans.__('Stop')}
icon={<StopIcon fontSize="small" />}
remainAfterConfirmation
Expand Down Expand Up @@ -60,7 +64,11 @@ function JobFiles(props: {
{props.job.job_files
.filter(jobFile => jobFile.file_format !== 'input' && jobFile.file_path)
.map(jobFile => (
<JobFileLink jobFile={jobFile} app={props.app} />
<JobFileLink
jobFile={jobFile}
app={props.app}
parentComponentName="jobs-list"
/>
))}
</>
);
Expand All @@ -76,6 +84,7 @@ type DownloadFilesButtonProps = {
function DownloadFilesButton(props: DownloadFilesButtonProps) {
const [downloading, setDownloading] = useState(false);
const trans = useTranslator('jupyterlab');
const log = useEventLogger();

return (
<IconButton
Expand All @@ -84,13 +93,15 @@ function DownloadFilesButton(props: DownloadFilesButtonProps) {
disabled={downloading}
onClick={async () => {
setDownloading(true);
log('jobs-list.download');
props.app.commands
.execute(CommandIDs.downloadFiles, {
id: props.job.job_id,
redownload: false
})
.then(_ =>
new Promise(res => setTimeout(res, 5000)).then(_ => {
log('jobs-list.download');
setDownloading(false);
props.reload();
})
Expand Down Expand Up @@ -120,16 +131,24 @@ export function buildJobRow(
jobFile => jobFile.file_format === 'input' && jobFile.file_path
);
const trans = useTranslator('jupyterlab');
const log = useEventLogger();

const cellContents: React.ReactNode[] = [
<Link
onClick={() => showDetailView(job.job_id)}
onClick={() => {
log('jobs-list.open-detail');
showDetailView(job.job_id);
}}
title={trans.__('Open detail view for "%1"', job.name)}
>
{job.name}
</Link>,
inputFile ? (
<JobFileLink app={app} jobFile={inputFile}>
<JobFileLink
app={app}
jobFile={inputFile}
parentComponentName="jobs-list"
>
{job.input_filename}
</JobFileLink>
) : (
Expand All @@ -153,6 +172,7 @@ export function buildJobRow(
<ConfirmDeleteButton
name={job.name}
clickHandler={() => {
log('jobs-list.delete');
app.commands
.execute(CommandIDs.deleteJob, {
id: job.job_id
Expand All @@ -163,13 +183,14 @@ export function buildJobRow(
/>
<StopButton
job={job}
clickHandler={() =>
clickHandler={() => {
log('jobs-list.stop');
app.commands
.execute(CommandIDs.stopJob, {
id: job.job_id
})
.catch((e: Error) => setDisplayError(e.message))
}
.catch((e: Error) => setDisplayError(e.message));
}}
/>
</Stack>
];
Expand Down
5 changes: 5 additions & 0 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
import React from 'react';

export type Logger = (eventName: string) => void;
export const LogContext = React.createContext<Logger>((eventName: string) => {
/*noop*/
});

// Context to be overridden with JupyterLab context
const TranslatorContext = React.createContext<ITranslator>(nullTranslator);
export default TranslatorContext;
7 changes: 6 additions & 1 deletion src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ import { useContext } from 'react';

import { TranslationBundle } from '@jupyterlab/translation';

import TranslatorContext from './context';
import TranslatorContext, { Logger, LogContext } from './context';

export function useTranslator(bundleId: string): TranslationBundle {
const translator = useContext(TranslatorContext);
return translator.load(bundleId);
}

export function useEventLogger(): Logger {
const logger: Logger = useContext(LogContext);
return logger;
}
24 changes: 22 additions & 2 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ const schedulerPlugin: JupyterFrontEndPlugin<void> = {
INotebookTracker,
ITranslator,
ILayoutRestorer,
Scheduler.IAdvancedOptions
Scheduler.IAdvancedOptions,
Scheduler.TelemetryHandler
],
optional: [ILauncher],
autoStart: true,
Expand All @@ -67,6 +68,22 @@ const advancedOptions: JupyterFrontEndPlugin<Scheduler.IAdvancedOptions> = {
}
};

// Default Telemetry Handler
const telemetryHandler = async (
eventLog: Scheduler.IEventLog
): Promise<void> => {
console.log(JSON.stringify(eventLog, undefined, 4));
};

const telemetry: JupyterFrontEndPlugin<Scheduler.TelemetryHandler> = {
id: '@jupyterlab/scheduler:TelemetryHandler',
autoStart: true,
provides: Scheduler.TelemetryHandler,
activate: (app: JupyterFrontEnd) => {
return telemetryHandler;
}
};

function getSelectedItem(widget: FileBrowser | null): Contents.IModel | null {
if (widget === null) {
return null;
Expand Down Expand Up @@ -127,6 +144,7 @@ async function activatePlugin(
translator: ITranslator,
restorer: ILayoutRestorer,
advancedOptions: Scheduler.IAdvancedOptions,
telemetryHandler: Scheduler.TelemetryHandler,
launcher: ILauncher | null
): Promise<void> {
const trans = translator.load('jupyterlab');
Expand Down Expand Up @@ -170,6 +188,7 @@ async function activatePlugin(
jobsPanel = new NotebookJobsPanel({
app,
translator,
telemetryHandler,
advancedOptions: advancedOptions
});
// Create new main area widget
Expand Down Expand Up @@ -290,7 +309,8 @@ async function activatePlugin(

const plugins: JupyterFrontEndPlugin<any>[] = [
schedulerPlugin,
advancedOptions
advancedOptions,
telemetry
];

export { JobsView };
Expand Down
10 changes: 8 additions & 2 deletions src/mainviews/create-job-from-definition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Heading } from '../components/heading';
import { Cluster } from '../components/cluster';
import { ParametersPicker } from '../components/parameters-picker';
import { Scheduler, SchedulerService } from '../handler';
import { useTranslator } from '../hooks';
import { useEventLogger, useTranslator } from '../hooks';
import { ICreateJobModel, IJobParameter, JobsView } from '../model';
import { Scheduler as SchedulerTokens } from '../tokens';

Expand Down Expand Up @@ -198,6 +198,8 @@ export function CreateJobFromDefinition(
const cantSubmit = trans.__('One or more of the fields has an error.');
const createError: string | undefined = props.model.createError;

const log = useEventLogger();

return (
<Box sx={{ p: 4 }}>
<form className={`${formPrefix}form`} onSubmit={e => e.preventDefault()}>
Expand Down Expand Up @@ -227,14 +229,18 @@ export function CreateJobFromDefinition(
<>
<Button
variant="outlined"
onClick={e => props.showListView(JobsView.ListJobs)}
onClick={e => {
log('create-job-from-definition.cancel');
props.showListView(JobsView.ListJobs);
}}
>
{trans.__('Cancel')}
</Button>

<Button
variant="contained"
onClick={(e: React.MouseEvent) => {
log('create-job-from-definition.create');
submitCreateJobRequest(e);
return false;
}}
Expand Down
Loading
Loading