Skip to content

Commit

Permalink
Merge pull request #4 from fcollonval/ft/parametrize
Browse files Browse the repository at this point in the history
Parametrize using settings
  • Loading branch information
fcollonval authored Nov 21, 2020
2 parents 45eb444 + db683c7 commit 03b0882
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 47 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@jupyterlab/apputils": "^2.2.6",
"@jupyterlab/coreutils": "^4.2.5",
"@jupyterlab/launcher": "^2.2.6",
"@jupyterlab/settingregistry": "^2.2.0",
"@jupyterlab/statedb": "^2.2.5",
"@jupyterlab/ui-components": "^2.2.4",
"@lumino/algorithm": "^1.2.3",
Expand Down
23 changes: 22 additions & 1 deletion schema/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,28 @@
"selector": "body"
}
],
"properties": {},
"properties": {
"categories": {
"title": "Launcher categories in displayed order",
"description": "Absent categories will be appended.",
"default": ["Kernels", "Other"],
"type": "array",
"items": {
"type": "string"
}
},
"maxUsageAge": {
"title": "Maximal age of the most recent use of a card (in days)",
"description": "If the latest use of a card is higher than maxUsageAge, the card will be considered as never used before.",
"default": 30,
"type": "number"
},
"nRecentCards": {
"title": "Number of recent cards to display",
"default": 4,
"type": "number"
}
},
"additionalProperties": false,
"type": "object"
}
19 changes: 15 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { ICommandPalette, MainAreaWidget } from '@jupyterlab/apputils';

import { ILauncher } from '@jupyterlab/launcher';
import { ISettingRegistry } from '@jupyterlab/settingregistry';

import { IStateDB } from '@jupyterlab/statedb';

Expand Down Expand Up @@ -37,7 +38,7 @@ const plugin: JupyterFrontEndPlugin<ILauncher> = {
activate,
id: EXTENSION_ID,
requires: [ILabShell],
optional: [ICommandPalette, IStateDB],
optional: [ICommandPalette, ISettingRegistry, IStateDB],
provides: ILauncher,
autoStart: true
};
Expand All @@ -50,15 +51,25 @@ export default plugin;
/**
* Activate the launcher.
*/
function activate(
async function activate(
app: JupyterFrontEnd,
labShell: ILabShell,
palette: ICommandPalette | null,
settingRegistry: ISettingRegistry | null,
state: IStateDB | null
): ILauncher {
): Promise<ILauncher> {
const { commands } = app;

const model = new LauncherModel(state);
let settings: ISettingRegistry.ISettings | null = null;
if (settingRegistry) {
try {
settings = await settingRegistry.load(EXTENSION_ID);
} catch (reason) {
console.log(`Failed to load settings for ${EXTENSION_ID}.`, reason);
}
}

const model = new LauncherModel(settings, state);

if (state) {
Promise.all([
Expand Down
129 changes: 88 additions & 41 deletions src/launcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { PageConfig } from '@jupyterlab/coreutils';

import { ILauncher } from '@jupyterlab/launcher';

import { ISettingRegistry } from '@jupyterlab/settingregistry';

import { IStateDB } from '@jupyterlab/statedb';

import { classes, folderIcon, LabIcon } from '@jupyterlab/ui-components';
Expand Down Expand Up @@ -49,27 +51,12 @@ export const EXTENSION_ID = '@jlab-enhanced/launcher:plugin';
*/
const LAUNCHER_CLASS = 'jp-NewLauncher';

/**
* The known categories of launcher items and their default ordering.
*/
const KNOWN_CATEGORIES = ['Kernels', 'Other'];

/**
* These launcher item categories are known to have kernels, so the kernel icons
* are used.
*/
const KERNEL_CATEGORIES = ['Notebook', 'Console'];

/**
* The maximum number of cards showed in recent section
*/
const MAX_RECENT_CARDS = 4;

/**
* Time (in milliseconds) after which the usage is considered to old
*/
const OLD_USAGE = 30 * 24 * 3600 * 1000;

/**
* IUsageData records the count of usage and the most recent date of usage
* for a certain kernel or card.
Expand All @@ -91,19 +78,66 @@ export interface IUsageData {
* LauncherItems, which the Launcher will render.
*/
export class LauncherModel extends VDomModel implements ILauncher {
constructor(state?: IStateDB) {
constructor(settings?: ISettingRegistry.ISettings, state?: IStateDB) {
super();
this._settings = settings || null;
this._state = state || null;

this.dispose();
}

/**
* Generate an unique identifier for a launcher item
*
* @param item Launcher item
*/
static getItemUID(item: ILauncher.IItemOptions): string {
return `${item.command}${JSON.stringify(item.args || {})}`;
}

/**
* The known categories of launcher items and their default ordering.
*/
get categories(): string[] {
if (this._settings) {
return this._settings.composite['categories'] as string[];
} else {
return ['Kernels', 'Other'];
}
}

/**
* The maximum number of cards showed in recent section
*/
get nRecentCards(): number {
if (this._settings) {
return this._settings.composite['nRecentCards'] as number;
} else {
return 4;
}
}

/**
* Time (in milliseconds) after which the usage is considered to old
*/
get maxUsageAge(): number {
let age = 30;
if (this._settings) {
age = this._settings.composite['maxUsageAge'] as number;
}
return age * 24 * 3600 * 1000;
}

/**
* Card usage data
*/
get usage(): { [cardId: string]: IUsageData } {
return this._usageData;
}

/**
* Launcher view mode
*/
get viewMode(): 'cards' | 'table' {
return this._viewMode;
}
Expand Down Expand Up @@ -140,12 +174,30 @@ export class LauncherModel extends VDomModel implements ILauncher {
});
}

/**
* Return an iterator of copied launcher items.
*/
items(): IIterator<INewLauncher.IItemOptions> {
return new ArrayIterator(
this._items.map(item => {
const key = LauncherModel.getItemUID(item);
const usage = this._usageData[key] || { count: 0, mostRecent: 0 };
return { ...item, ...usage };
})
);
}

/**
* Handle card usage data when used.
*
* @param item Launcher item
*/
useCard(item: ILauncher.IItemOptions): void {
const id = LauncherModel.getItemUID(item);
const usage = this._usageData[id];
const now = Date.now();
let currentCount = 0;
if (usage && now - usage.mostRecent < OLD_USAGE) {
if (usage && now - usage.mostRecent < this.maxUsageAge) {
currentCount = usage.count;
}
this._usageData[id] = {
Expand All @@ -164,20 +216,8 @@ export class LauncherModel extends VDomModel implements ILauncher {
}
}

/**
* Return an iterator of copied launcher items.
*/
items(): IIterator<INewLauncher.IItemOptions> {
return new ArrayIterator(
this._items.map(item => {
const key = LauncherModel.getItemUID(item);
const usage = this._usageData[key] || { count: 0, mostRecent: 0 };
return { ...item, ...usage };
})
);
}

private _items: ILauncher.IItemOptions[] = [];
private _settings: ISettingRegistry.ISettings | null = null;
private _state: IStateDB | null = null;
private _usageData: { [key: string]: IUsageData } = {};
private _viewMode: 'cards' | 'table' = 'cards';
Expand Down Expand Up @@ -294,22 +334,28 @@ export class Launcher extends VDomRenderer<LauncherModel> {
const sections: React.ReactElement<any>[] = [];

// Assemble the final ordered list of categories, beginning with
// KNOWN_CATEGORIES.
// model.categories.
const orderedCategories: string[] = [];
each(KNOWN_CATEGORIES, (cat, index) => {
each(this.model.categories, (cat, index) => {
if (cat in categories) {
orderedCategories.push(cat);
}
});
for (const cat in categories) {
if (KNOWN_CATEGORIES.indexOf(cat) === -1) {
if (this.model.categories.indexOf(cat) === -1) {
orderedCategories.push(cat);
}
}

const mostUsedItems = toArray(this.model.items()).sort(
(a: INewLauncher.IItemOptions, b: INewLauncher.IItemOptions) => {
return Private.sortByUsage(a, b, this._cwd, this._commands);
return Private.sortByUsage(
a,
b,
this.model.maxUsageAge,
this._cwd,
this._commands
);
}
);

Expand All @@ -323,7 +369,7 @@ export class Launcher extends VDomRenderer<LauncherModel> {
<div className={`jp-NewLauncher${mode}-cardContainer`}>
{toArray(
map(
mostUsedItems.slice(0, MAX_RECENT_CARDS),
mostUsedItems.slice(0, this.model.nRecentCards),
(item: INewLauncher.IItemOptions) => {
return Card(
KERNEL_CATEGORIES.indexOf(item.category || 'Other') > -1,
Expand Down Expand Up @@ -410,7 +456,7 @@ export class Launcher extends VDomRenderer<LauncherModel> {
className="jp-NewLauncher-search-input"
spellCheck={false}
placeholder="SEARCH"
onChange={event => {
onChange={(event): void => {
this._searchInput = event.target.value || '';
this.update();
}}
Expand All @@ -432,7 +478,7 @@ export class Launcher extends VDomRenderer<LauncherModel> {
<div className="jp-NewLauncher-view">
<button
disabled={this.model.viewMode === 'cards'}
onClick={() => {
onClick={(): void => {
this.model.viewMode = 'cards';
this.update();
}}
Expand All @@ -445,7 +491,7 @@ export class Launcher extends VDomRenderer<LauncherModel> {
</button>
<button
disabled={this.model.viewMode === 'table'}
onClick={() => {
onClick={(): void => {
this.model.viewMode = 'table';
this.update();
}}
Expand Down Expand Up @@ -589,7 +635,7 @@ function Card(

// With tabindex working, you can now pick a kernel by tabbing around and
// pressing Enter.
const onkeypress = (event: React.KeyboardEvent) => {
const onkeypress = (event: React.KeyboardEvent): void => {
if (event.key === 'Enter') {
mainOnClick(event);
}
Expand Down Expand Up @@ -705,13 +751,14 @@ namespace Private {
export function sortByUsage(
a: INewLauncher.IItemOptions,
b: INewLauncher.IItemOptions,
maxUsageAge: number,
cwd: string,
commands: CommandRegistry
): number {
const now = Date.now();

const aCount = now - a.mostRecent < OLD_USAGE ? a.count : 0;
const bCount = now - b.mostRecent < OLD_USAGE ? b.count : 0;
const aCount = now - a.mostRecent < maxUsageAge ? a.count : 0;
const bCount = now - b.mostRecent < maxUsageAge ? b.count : 0;
if (aCount === bCount) {
const mostRecent = b.mostRecent - a.mostRecent;
return mostRecent === 0 ? sortCmp(a, b, cwd, commands) : mostRecent;
Expand Down
1 change: 1 addition & 0 deletions style/table.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
.jp-NewLauncher-Table-Cell.jp-NewLauncher-label {
width: auto;
height: auto;
font-size: var(--jp-ui-font-size1);
text-align: left;
}

Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@
node-fetch "^2.6.0"
ws "^7.2.0"

"@jupyterlab/settingregistry@^2.2.5":
"@jupyterlab/settingregistry@^2.2.0", "@jupyterlab/settingregistry@^2.2.5":
version "2.2.5"
resolved "https://registry.yarnpkg.com/@jupyterlab/settingregistry/-/settingregistry-2.2.5.tgz#665d2f5bfb601acd7020e2868f8bba1513d8c9cf"
integrity sha512-LoKa27F1WNmeMT168TYo+MgjsYsVawKCZbmU7OGQS6h6J5dx0xQBQvE38NkhCsjnPYyUv4tYmGIFyHQceCDDaA==
Expand Down

0 comments on commit 03b0882

Please sign in to comment.