diff --git a/extensions/machine-learning-services/images/background.svg b/extensions/machine-learning-services/images/background.svg
new file mode 100644
index 0000000000..50fd10a899
--- /dev/null
+++ b/extensions/machine-learning-services/images/background.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/extensions/machine-learning-services/images/createNotebook.svg b/extensions/machine-learning-services/images/createNotebook.svg
new file mode 100644
index 0000000000..1f0a03b9b4
--- /dev/null
+++ b/extensions/machine-learning-services/images/createNotebook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/extensions/machine-learning-services/images/installPackages.svg b/extensions/machine-learning-services/images/installPackages.svg
new file mode 100644
index 0000000000..5b75cda4c0
--- /dev/null
+++ b/extensions/machine-learning-services/images/installPackages.svg
@@ -0,0 +1,3 @@
+
diff --git a/extensions/machine-learning-services/images/linkIcon.svg b/extensions/machine-learning-services/images/linkIcon.svg
new file mode 100644
index 0000000000..63f69fc22e
--- /dev/null
+++ b/extensions/machine-learning-services/images/linkIcon.svg
@@ -0,0 +1,3 @@
+
diff --git a/extensions/machine-learning-services/images/registerRuntime.svg b/extensions/machine-learning-services/images/registerRuntime.svg
new file mode 100644
index 0000000000..f0b395a639
--- /dev/null
+++ b/extensions/machine-learning-services/images/registerRuntime.svg
@@ -0,0 +1 @@
+
diff --git a/extensions/machine-learning-services/images/video1.svg b/extensions/machine-learning-services/images/video1.svg
new file mode 100644
index 0000000000..01f4180c8b
--- /dev/null
+++ b/extensions/machine-learning-services/images/video1.svg
@@ -0,0 +1,9 @@
+
diff --git a/extensions/machine-learning-services/images/video2.svg b/extensions/machine-learning-services/images/video2.svg
new file mode 100644
index 0000000000..618673834d
--- /dev/null
+++ b/extensions/machine-learning-services/images/video2.svg
@@ -0,0 +1,9 @@
+
diff --git a/extensions/machine-learning-services/package.json b/extensions/machine-learning-services/package.json
index abe3a7f0a8..9e649f2616 100644
--- a/extensions/machine-learning-services/package.json
+++ b/extensions/machine-learning-services/package.json
@@ -55,7 +55,11 @@
"commands": [
{
"command": "mls.command.managePackages",
- "title": "%mls.command.managePackages%"
+ "title": "%mls.command.managePackages%",
+ "icon": {
+ "light": "./images/installPackages.svg",
+ "dark": "./images/installPackages.svg"
+ }
},
{
"command": "mls.command.predictModel",
@@ -70,8 +74,8 @@
"title": "%mls.command.manageModels%"
},
{
- "command": "mls.command.registerModel",
- "title": "%mls.command.registerModel%",
+ "command": "mls.command.importModel",
+ "title": "%mls.command.importModel%",
"icon": {
"light": "./images/registerModel.svg",
"dark": "./images/registerModel.svg"
@@ -79,15 +83,11 @@
},
{
"command": "mls.command.manageLanguages",
- "title": "%mls.command.manageLanguages%"
- },
- {
- "command": "mls.command.odbcdriver",
- "title": "%mls.command.odbcdriver%"
- },
- {
- "command": "mls.command.mlsdocs",
- "title": "%mls.command.mlsdocs%"
+ "title": "%mls.command.manageLanguages%",
+ "icon": {
+ "light": "./images/registerRuntime.svg",
+ "dark": "./images/registerRuntime.svg"
+ }
},
{
"command": "mls.command.dependencies",
@@ -110,21 +110,20 @@
"widget": {
"tasks-widget": [
"mls.command.managePackages",
- "mls.command.manageLanguages",
- "mls.command.manageModels",
- "mls.command.predictModel"
+ "mls.command.manageLanguages"
]
}
},
{
- "name": "%title.documents%",
+ "name": "",
"row": 0,
"col": 1,
+ "rowspan": 3,
+ "colspan": 4,
"widget": {
- "tasks-widget": [
- "mls.command.odbcdriver",
- "mls.command.mlsdocs"
- ]
+ "modelview": {
+ "id":"mls.dashboard"
+ }
}
}
]
diff --git a/extensions/machine-learning-services/package.nls.json b/extensions/machine-learning-services/package.nls.json
index 509f2e87cb..5ad08fabf3 100644
--- a/extensions/machine-learning-services/package.nls.json
+++ b/extensions/machine-learning-services/package.nls.json
@@ -1,6 +1,6 @@
{
- "displayName": "SQL Server Machine Learning Services",
- "description": "SQL Server Machine Learning Services",
+ "displayName": "SQL Machine Learning",
+ "description": "SQL Machine Learning",
"title.tasks": "Tasks",
"title.documents": "Documents",
"title.configurations": "Configurations",
@@ -9,13 +9,11 @@
"mls.command.manageLanguages": "Manage external languages",
"mls.command.predictModel": "Make prediction",
"mls.command.manageModels": "Manage models",
- "mls.command.registerModel": "Register model",
- "mls.command.odbcdriver": "Install ODBC Driver for SQL Server",
- "mls.command.mlsdocs": "Machine Learning Services Documentation",
- "mls.configuration.title": "Machine Learning Services configurations",
- "mls.pythonPath.description": "Local path to a preexisting Python installation used by Machine Learning Services.",
+ "mls.command.importModel": "Import model",
+ "mls.configuration.title": "SQL Machine Learning Configurations",
+ "mls.pythonPath.description": "Local path to a preexisting Python installation used by SQL Machine Learning.",
"mls.enablePython.description": "Enable Python package management.",
"mls.enableR.description": "Enable R package management.",
- "mls.rPath.description": "Local path to a preexisting R installation used by Machine Learning Services.",
- "mls.command.dependencies": "Install Machine Learning Services Dependencies"
+ "mls.rPath.description": "Local path to a preexisting R installation used by SQL Machine Learning.",
+ "mls.command.dependencies": "Install SQL Machine Learning Dependencies"
}
diff --git a/extensions/machine-learning-services/src/common/apiWrapper.ts b/extensions/machine-learning-services/src/common/apiWrapper.ts
index bf2cb4867c..9beea070aa 100644
--- a/extensions/machine-learning-services/src/common/apiWrapper.ts
+++ b/extensions/machine-learning-services/src/common/apiWrapper.ts
@@ -129,4 +129,8 @@ export class ApiWrapper {
public createButton(label: string, position?: azdata.window.DialogButtonPosition): azdata.window.Button {
return azdata.window.createButton(label, position);
}
+
+ public registerWidget(widgetId: string, handler: (view: azdata.ModelView) => void): void {
+ azdata.ui.registerModelViewProvider(widgetId, handler);
+ }
}
diff --git a/extensions/machine-learning-services/src/common/constants.ts b/extensions/machine-learning-services/src/common/constants.ts
index 175eaa80d0..6a580d6559 100644
--- a/extensions/machine-learning-services/src/common/constants.ts
+++ b/extensions/machine-learning-services/src/common/constants.ts
@@ -16,7 +16,7 @@ export const rLPackagedFolderName = 'r_packages';
export const mlEnableMlsCommand = 'mls.command.enableMls';
export const mlDisableMlsCommand = 'mls.command.disableMls';
-export const extensionOutputChannel = 'Machine Learning Services';
+export const extensionOutputChannel = 'SQL Machine Learning';
export const notebookExtensionName = 'Microsoft.notebook';
export const azureSubscriptionsCommand = 'azure.accounts.getSubscriptions';
export const azureResourceGroupsCommand = 'azure.accounts.getResourceGroups';
@@ -26,11 +26,10 @@ export const azureResourceGroupsCommand = 'azure.accounts.getResourceGroups';
export const mlManageLanguagesCommand = 'mls.command.manageLanguages';
export const mlsPredictModelCommand = 'mls.command.predictModel';
export const mlManageModelsCommand = 'mls.command.manageModels';
-export const mlRegisterModelCommand = 'mls.command.registerModel';
+export const mlImportModelCommand = 'mls.command.importModel';
export const mlManagePackagesCommand = 'mls.command.managePackages';
-export const mlOdbcDriverCommand = 'mls.command.odbcdriver';
-export const mlsDocumentsCommand = 'mls.command.mlsdocs';
export const mlsDependenciesCommand = 'mls.command.dependencies';
+export const notebookCommandNew = 'notebook.command.new';
// Configurations
//
@@ -148,8 +147,12 @@ export const currentModelsTitle = localize('models.currentModelsTitle', "Models"
export const azureRegisterModel = localize('models.azureRegisterModel', "Deploy");
export const predictModel = localize('models.predictModel', "Predict");
export const registerModelTitle = localize('models.RegisterWizard', "Deployed models");
-export const deployModelTitle = localize('models.deployModelTitle', "Deploy models");
-export const makePredictionTitle = localize('models.makePredictionTitle', "Make prediction");
+export const importModelTitle = localize('models.importModelTitle', "Import models");
+export const importModelDesc = localize('models.importModelDesc', "Build, import and expose a machine learning model");
+export const makePredictionTitle = localize('models.makePredictionTitle', "Make predictions");
+export const makePredictionDesc = localize('models.makePredictionDesc', "Generates a predicted value or scores using a managed model");
+export const createNotebookTitle = localize('models.createNotebookTitle', "Create notebook");
+export const createNotebookDesc = localize('models.createNotebookDesc', "Run experiments and create models");
export const modelRegisteredSuccessfully = localize('models.modelRegisteredSuccessfully', "Model registered successfully");
export const modelFailedToRegister = localize('models.modelFailedToRegistered', "Model failed to register");
export const localModelSource = localize('models.localModelSource', "File upload");
@@ -166,8 +169,15 @@ export function importModelFailedError(modelName: string | undefined, filePath:
export const loadModelParameterFailedError = localize('models.loadModelParameterFailedError', "Failed to load model parameters'");
export const unsupportedModelParameterType = localize('models.unsupportedModelParameterType', "unsupported");
-
-
+export const dashboardTitle = localize('dashboardTitle', "SQL ML");
+export const dashboardDesc = localize('dashboardDesc', "Machine learning for SQL databases");
+export const dashboardLinksTitle = localize('dashboardLinksTitle', "Useful links");
+export const dashboardVideoLinksTitle = localize('dashboardVideoLinksTitle', "Video tutorials");
+export const learnMoreTitle = localize('learnMoreTitle', "Learn more");
+export const mlsInstallMlsDocTitle = localize('mlsInstallMlsDocTitle', "Install SQL Server Machine Learning Services");
+export const mlsInstallMlsDocDesc = localize('mlsInstallMlsDocDesc', "This document guides you in the installation of SQL Server Machine Learning Services. Python and R scripts can be executed in-database using Machine Learning Services.");
+export const mlsInstallOdbcDocTitle = localize('mlsInstallObdcDocTitle', "Install the Microsoft ODBC driver for SQL Server");
+export const mlsInstallOdbcDocDesc = localize('mlsInstallOdbcDocDesc', "This document explains how to install the Microsoft ODBC Driver for SQL Server.");
// Links
//
diff --git a/extensions/machine-learning-services/src/controllers/mainController.ts b/extensions/machine-learning-services/src/controllers/mainController.ts
index a9cc6babf6..005c600582 100644
--- a/extensions/machine-learning-services/src/controllers/mainController.ts
+++ b/extensions/machine-learning-services/src/controllers/mainController.ts
@@ -22,6 +22,7 @@ import { DeployedModelService } from '../modelManagement/deployedModelService';
import { AzureModelRegistryService } from '../modelManagement/azureModelRegistryService';
import { ModelPythonClient } from '../modelManagement/modelPythonClient';
import { PredictService } from '../prediction/predictService';
+import { DashboardWidget } from '../views/widgets/dashboardWidget';
/**
* The main controller class that initializes the extension
@@ -110,13 +111,16 @@ export default class MainController implements vscode.Disposable {
let modelManagementController = new ModelManagementController(this._apiWrapper, this._rootPath,
azureModelsService, registeredModelService, predictService);
+ let dashboardWidget = new DashboardWidget(this._apiWrapper, this._rootPath);
+ dashboardWidget.register();
+
this._apiWrapper.registerCommand(constants.mlManageLanguagesCommand, (async () => {
await languageController.manageLanguages();
}));
this._apiWrapper.registerCommand(constants.mlManageModelsCommand, (async () => {
await modelManagementController.manageRegisteredModels();
}));
- this._apiWrapper.registerCommand(constants.mlRegisterModelCommand, (async () => {
+ this._apiWrapper.registerCommand(constants.mlImportModelCommand, (async () => {
await modelManagementController.registerModel();
}));
this._apiWrapper.registerCommand(constants.mlsPredictModelCommand, (async () => {
@@ -134,18 +138,12 @@ export default class MainController implements vscode.Disposable {
this._apiWrapper.registerTaskHandler(constants.mlManageModelsCommand, async () => {
await modelManagementController.manageRegisteredModels();
});
- this._apiWrapper.registerTaskHandler(constants.mlRegisterModelCommand, async () => {
+ this._apiWrapper.registerTaskHandler(constants.mlImportModelCommand, async () => {
await modelManagementController.registerModel();
});
this._apiWrapper.registerTaskHandler(constants.mlsPredictModelCommand, async () => {
await modelManagementController.predictModel();
});
- this._apiWrapper.registerTaskHandler(constants.mlOdbcDriverCommand, async () => {
- await this.packageManagementService.openOdbcDriverDocuments();
- });
- this._apiWrapper.registerTaskHandler(constants.mlsDocumentsCommand, async () => {
- await this.packageManagementService.openDocuments();
- });
}
/**
diff --git a/extensions/machine-learning-services/src/packageManagement/packageManagementService.ts b/extensions/machine-learning-services/src/packageManagement/packageManagementService.ts
index fcb73a6555..093cbb817f 100644
--- a/extensions/machine-learning-services/src/packageManagement/packageManagementService.ts
+++ b/extensions/machine-learning-services/src/packageManagement/packageManagementService.ts
@@ -29,28 +29,6 @@ export class PackageManagementService {
return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.mlsDocuments));
}
- /**
- * Opens ODBC driver documents
- */
- public async openOdbcDriverDocuments(): Promise {
- if (utils.isWindows()) {
- return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.odbcDriverWindowsDocuments));
- } else {
- return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.odbcDriverLinuxDocuments));
- }
- }
-
- /**
- * Opens install MLS documents
- */
- public async openInstallDocuments(): Promise {
- if (utils.isWindows()) {
- return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.installMlsWindowsDocs));
- } else {
- return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.installMlsLinuxDocs));
- }
- }
-
/**
* Returns true if mls is installed in the give SQL server instance
*/
diff --git a/extensions/machine-learning-services/src/test/packageManagement/packageManagementService.test.ts b/extensions/machine-learning-services/src/test/packageManagement/packageManagementService.test.ts
index 8a6315b094..86adf320a7 100644
--- a/extensions/machine-learning-services/src/test/packageManagement/packageManagementService.test.ts
+++ b/extensions/machine-learning-services/src/test/packageManagement/packageManagementService.test.ts
@@ -31,20 +31,6 @@ describe('Package Management Service', () => {
should.equal(await serverConfigManager.openDocuments(), true);
});
- it('openOdbcDriverDocuments should open document in browser successfully', async function (): Promise {
- const context = createContext();
- context.apiWrapper.setup(x => x.openExternal(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
- let serverConfigManager = new PackageManagementService(context.apiWrapper.object, context.queryRunner.object);
- should.equal(await serverConfigManager.openOdbcDriverDocuments(), true);
- });
-
- it('openInstallDocuments should open document in browser successfully', async function (): Promise {
- const context = createContext();
- context.apiWrapper.setup(x => x.openExternal(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
- let serverConfigManager = new PackageManagementService(context.apiWrapper.object, context.queryRunner.object);
- should.equal(await serverConfigManager.openInstallDocuments(), true);
- });
-
it('isMachineLearningServiceEnabled should return true if external script is enabled', async function (): Promise {
const context = createContext();
context.queryRunner.setup(x => x.isMachineLearningServiceEnabled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
diff --git a/extensions/machine-learning-services/src/test/views/dashboardWidget.test.ts b/extensions/machine-learning-services/src/test/views/dashboardWidget.test.ts
new file mode 100644
index 0000000000..de1fe94b28
--- /dev/null
+++ b/extensions/machine-learning-services/src/test/views/dashboardWidget.test.ts
@@ -0,0 +1,39 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as azdata from 'azdata';
+import * as vscode from 'vscode';
+import * as TypeMoq from 'typemoq';
+import { ApiWrapper } from '../../common/apiWrapper';
+import { createViewContext } from './utils';
+import { DashboardWidget } from '../../views/widgets/dashboardWidget';
+
+interface TestContext {
+ apiWrapper: TypeMoq.IMock;
+ view: azdata.ModelView;
+ onClick: vscode.EventEmitter;
+}
+
+
+function createContext(): TestContext {
+
+ let viewTestContext = createViewContext();
+
+ return {
+ apiWrapper: viewTestContext.apiWrapper,
+ view: viewTestContext.view,
+ onClick: viewTestContext.onClick
+ };
+}
+
+describe('Dashboard widget', () => {
+ it('Should create view components successfully ', async function (): Promise {
+ let testContext = createContext();
+ const dashboard = new DashboardWidget(testContext.apiWrapper.object, '');
+ dashboard.register();
+ testContext.onClick.fire();
+ testContext.apiWrapper.verify(x => x.executeCommand(TypeMoq.It.isAny()), TypeMoq.Times.atMostOnce());
+ });
+});
diff --git a/extensions/machine-learning-services/src/test/views/utils.ts b/extensions/machine-learning-services/src/test/views/utils.ts
index e55c545cba..ede30eca63 100644
--- a/extensions/machine-learning-services/src/test/views/utils.ts
+++ b/extensions/machine-learning-services/src/test/views/utils.ts
@@ -52,6 +52,9 @@ export function createViewContext(): ViewTestContext {
});
let flex: azdata.FlexContainer = Object.assign({}, componentBase, container, {
});
+ let div: azdata.DivContainer = Object.assign({}, componentBase, container, {
+ onDidClick: onClick.event
+ });
let buttonBuilder: azdata.ComponentBuilder = {
component: () => button,
@@ -134,6 +137,13 @@ export function createViewContext(): ViewTestContext {
withItems: () => flexBuilder,
withLayout: () => flexBuilder
});
+ let divBuilder: azdata.DivBuilder = Object.assign({}, {
+ component: () => div,
+ withProperties: () => divBuilder,
+ withValidation: () => divBuilder,
+ withItems: () => divBuilder,
+ withLayout: () => divBuilder
+ });
let inputBoxBuilder: azdata.ComponentBuilder = {
component: () => {
@@ -180,7 +190,7 @@ export function createViewContext(): ViewTestContext {
modelBuilder: {
radioCardGroup: undefined!,
navContainer: undefined!,
- divContainer: undefined!,
+ divContainer: () => divBuilder,
flexContainer: () => flexBuilder,
splitViewContainer: undefined!,
dom: undefined!,
@@ -295,6 +305,13 @@ export function createViewContext(): ViewTestContext {
apiWrapper.setup(x => x.createWizardPage(TypeMoq.It.isAny())).returns(() => wizardPage);
apiWrapper.setup(x => x.createModelViewDialog(TypeMoq.It.isAny())).returns(() => dialog);
apiWrapper.setup(x => x.openDialog(TypeMoq.It.isAny())).returns(() => { });
+ apiWrapper.setup(x => x.registerWidget(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(async (id, handler) => {
+ if (id) {
+ return await handler(view);
+ } else {
+ Promise.reject();
+ }
+ });
return {
apiWrapper: apiWrapper,
diff --git a/extensions/machine-learning-services/src/views/models/registerModels/registeredModelsDialog.ts b/extensions/machine-learning-services/src/views/models/registerModels/registeredModelsDialog.ts
index 9ec3665aba..af45cec44b 100644
--- a/extensions/machine-learning-services/src/views/models/registerModels/registeredModelsDialog.ts
+++ b/extensions/machine-learning-services/src/views/models/registerModels/registeredModelsDialog.ts
@@ -31,7 +31,7 @@ export class RegisteredModelsDialog extends ModelViewBase {
this.currentLanguagesTab = new CurrentModelsPage(this._apiWrapper, this);
- let registerModelButton = this._apiWrapper.createButton(constants.deployModelTitle);
+ let registerModelButton = this._apiWrapper.createButton(constants.importModelTitle);
registerModelButton.onClick(async () => {
await this.sendDataRequest(RegisterModelEventName);
});
diff --git a/extensions/machine-learning-services/src/views/widgets/dashboardWidget.ts b/extensions/machine-learning-services/src/views/widgets/dashboardWidget.ts
new file mode 100644
index 0000000000..4570f8bf96
--- /dev/null
+++ b/extensions/machine-learning-services/src/views/widgets/dashboardWidget.ts
@@ -0,0 +1,486 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as azdata from 'azdata';
+import * as vscode from 'vscode';
+import { ApiWrapper } from '../../common/apiWrapper';
+import * as path from 'path';
+import * as constants from '../../common/constants';
+import * as utils from '../../common/utils';
+
+interface IActionMetadata {
+ title?: string,
+ description?: string,
+ link?: string,
+ iconPath?: { light: string | vscode.Uri; dark: string | vscode.Uri },
+ command?: string;
+}
+
+const maxWidth = 800;
+const headerMaxHeight = 200;
+export class DashboardWidget {
+
+ /**
+ * Creates new instance of dashboard
+ */
+ constructor(private _apiWrapper: ApiWrapper, private _root: string) {
+ }
+
+ public register(): void {
+ this._apiWrapper.registerWidget('mls.dashboard', async (view) => {
+ const container = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'column',
+ width: '100%',
+ height: '100%'
+ }).component();
+ const header = this.createHeader(view);
+ const tasksContainer = this.createTasks(view);
+ const footerContainer = this.createFooter(view);
+ container.addItem(header, {
+ CSSStyles: {
+ 'background-image': `url(${vscode.Uri.file(this.asAbsolutePath('images/background.svg'))})`,
+ 'background-repeat': 'no-repeat',
+ 'background-position': 'top',
+ 'width': `${maxWidth}px`,
+ 'height': '130px',
+ 'background-size': `${maxWidth}px ${headerMaxHeight}px`
+ }
+ });
+ container.addItem(tasksContainer, {
+ CSSStyles: {
+ 'width': `${maxWidth}px`,
+ 'height': '150px',
+ }
+ });
+ container.addItem(footerContainer, {
+ CSSStyles: {
+ 'width': `${maxWidth}px`,
+ 'height': '500px',
+ }
+ });
+ const mainContainer = view.modelBuilder.flexContainer()
+ .withLayout({
+ flexFlow: 'column',
+ width: '100%',
+ height: '100%',
+ position: 'absolute'
+ }).component();
+ mainContainer.addItem(container, {
+ CSSStyles: { 'padding-top': '25px', 'padding-left': '5px' }
+ });
+ await view.initializeModel(mainContainer);
+ });
+ }
+
+ private createHeader(view: azdata.ModelView): azdata.Component {
+ const header = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'column',
+ width: maxWidth,
+ height: headerMaxHeight,
+ }).component();
+ const titleComponent = view.modelBuilder.text().withProperties({
+ value: constants.dashboardTitle,
+ CSSStyles: {
+ 'font-size': '36px',
+ //'color': '#333333',
+ 'font-weight': 'bold',
+ 'margin': '0px'
+ }
+ }).component();
+ const descComponent = view.modelBuilder.text().withProperties({
+ value: constants.dashboardDesc,
+ CSSStyles: {
+ 'font-size': '14px',
+ //'color': '#888888',
+ 'font-weight': 'bold',
+ 'margin': '0px'
+ }
+ }).component();
+ header.addItems([titleComponent, descComponent], {
+ CSSStyles: {
+ 'width': `${maxWidth}px`,
+ 'padding': '10px'
+ }
+ });
+
+ return header;
+ }
+
+ private createFooter(view: azdata.ModelView): azdata.Component {
+ const footerContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'row',
+ width: maxWidth,
+ height: '500px',
+ justifyContent: 'flex-start'
+ }).component();
+ const linksContainer = this.createLinks(view);
+ const videoLinksContainer = this.createVideoLinks(view);
+ footerContainer.addItem(linksContainer);
+ footerContainer.addItem(videoLinksContainer, {
+ CSSStyles: {
+ 'padding-left': '50px',
+ }
+ });
+
+ return footerContainer;
+ }
+
+ private createVideoLinks(view: azdata.ModelView): azdata.Component {
+ const maxWidth = 400;
+ const linksContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'column',
+ width: maxWidth,
+ height: '500px',
+ justifyContent: 'flex-start'
+ }).component();
+ const titleComponent = view.modelBuilder.text().withProperties({
+ value: constants.dashboardVideoLinksTitle,
+ CSSStyles: {
+ 'font-size': '18px',
+ 'font-weight': 'bold',
+ 'margin': '0px'
+ }
+ }).component();
+ const videosContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'row',
+ width: maxWidth,
+ height: '500px',
+ }).component();
+ const video1Container = this.createVideoLink(view, {
+ iconPath: { light: 'images/video1.svg', dark: 'images/video1.svg' },
+ description: 'Visualize data using SandDance',
+ link: 'https://www.youtube.com/watch?v=e305wTAoLZs'
+ });
+ videosContainer.addItem(video1Container);
+ const video2Container = this.createVideoLink(view, {
+ iconPath: { light: 'images/video2.svg', dark: 'images/video2.svg' },
+ description: 'How to make the best out of Microsoft Azure'
+ });
+ videosContainer.addItem(video2Container);
+
+ linksContainer.addItems([titleComponent], {
+ CSSStyles: {
+ 'padding': '0px',
+ 'padding-right': '5px',
+ 'padding-top': '10px',
+ 'height': '10px',
+ 'margin': '0px'
+ }
+ });
+ linksContainer.addItems([videosContainer], {
+ CSSStyles: {
+ 'padding': '0px',
+ 'padding-right': '5px',
+ 'padding-top': '10px',
+ 'height': '10px',
+ 'margin': '0px'
+ }
+ });
+ return linksContainer;
+ }
+
+ private createVideoLink(view: azdata.ModelView, linkMetaData: IActionMetadata): azdata.Component {
+ const maxWidth = 200;
+ const videosContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'column',
+ width: maxWidth,
+ height: '200px',
+ justifyContent: 'flex-start'
+ }).component();
+ const video1Container = view.modelBuilder.divContainer().withProperties({
+ clickable: true,
+ width: maxWidth,
+ height: '100px'
+ }).component();
+ const descriptionComponent = view.modelBuilder.text().withProperties({
+ value: linkMetaData.description,
+ width: '200px',
+ height: '50px',
+ CSSStyles: {
+ //'color': '#605E5C',
+ 'font-size': '12px',
+ 'margin': '0px'
+ }
+ }).component();
+ video1Container.onDidClick(async () => {
+ if (linkMetaData.link) {
+ await this._apiWrapper.openExternal(vscode.Uri.parse(linkMetaData.link));
+ }
+ });
+ videosContainer.addItem(video1Container, {
+ CSSStyles: {
+ 'background-image': `url(${vscode.Uri.file(this.asAbsolutePath(linkMetaData.iconPath?.light || ''))})`,
+ 'background-repeat': 'no-repeat',
+ 'background-position': 'top',
+ 'width': `150px`,
+ 'height': '110px',
+ 'background-size': `150px 120px`
+ }
+ });
+ videosContainer.addItem(descriptionComponent);
+ return videosContainer;
+ }
+
+ private createLinks(view: azdata.ModelView): azdata.Component {
+ const maxWidth = 400;
+ const linksContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'column',
+ width: maxWidth,
+ height: '500px',
+ }).component();
+ const titleComponent = view.modelBuilder.text().withProperties({
+ value: constants.dashboardLinksTitle,
+ CSSStyles: {
+ 'font-size': '18px',
+ //'color': '#323130',
+ 'font-weight': 'bold',
+ 'margin': '0px'
+ }
+ }).component();
+ let mlsLink: string;
+ if (utils.isWindows()) {
+ mlsLink = constants.installMlsWindowsDocs;
+ } else {
+ mlsLink = constants.installMlsLinuxDocs;
+ }
+ const mlsDocs = this.createLink(view, {
+ title: constants.mlsInstallMlsDocTitle,
+ description: constants.mlsInstallMlsDocDesc,
+ link: mlsLink
+ });
+ let odbcLink: string;
+ if (utils.isWindows()) {
+ odbcLink = constants.odbcDriverWindowsDocuments;
+ } else {
+ odbcLink = constants.odbcDriverLinuxDocuments;
+ }
+ const rdbcDocs = this.createLink(view, {
+ title: constants.mlsInstallOdbcDocTitle,
+ description: constants.mlsInstallOdbcDocDesc,
+ link: odbcLink
+ });
+
+ linksContainer.addItems([titleComponent, mlsDocs, rdbcDocs], {
+ CSSStyles: {
+ 'padding': '10px'
+ }
+ });
+ return linksContainer;
+ }
+
+ private createLink(view: azdata.ModelView, linkMetaData: IActionMetadata): azdata.Component {
+ const maxHeight = 80;
+ const maxWidth = 400;
+ const labelsContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'column',
+ width: maxWidth,
+ height: maxHeight,
+ justifyContent: 'flex-start'
+ }).component();
+ const descriptionComponent = view.modelBuilder.text().withProperties({
+ value: linkMetaData.description,
+ width: maxWidth,
+ CSSStyles: {
+ //'color': '#605E5C',
+ 'font-size': '12px',
+ 'margin': '0px'
+ }
+ }).component();
+ const linkContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'row',
+ width: maxWidth,
+ justifyContent: 'flex-start'
+ }).component();
+ const linkComponent = view.modelBuilder.hyperlink().withProperties({
+ label: linkMetaData.title,
+ url: linkMetaData.link,
+ CSSStyles: {
+ 'color': '#3794ff',
+ 'font-size': '14px',
+ 'margin': '0px'
+ }
+ }).component();
+ const image = view.modelBuilder.image().withProperties({
+ width: '10px',
+ height: '10px',
+ iconPath: {
+ dark: this.asAbsolutePath('images/linkIcon.svg'),
+ light: this.asAbsolutePath('images/linkIcon.svg'),
+ },
+ iconHeight: '10px',
+ iconWidth: '10px'
+ }).component();
+ linkContainer.addItem(linkComponent, {
+ CSSStyles: {
+ 'padding': '0px',
+ 'padding-right': '5px',
+ 'height': '10px',
+ 'margin': '0px'
+ }
+ });
+ linkContainer.addItem(image, {
+ CSSStyles: {
+ 'padding': '0px',
+ 'padding-right': '5px',
+ 'padding-top': '5px',
+ 'height': '10px',
+ 'margin': '0px'
+ }
+ });
+ labelsContainer.addItems([linkContainer, descriptionComponent], {
+ CSSStyles: {
+ 'padding': '0px',
+ 'padding-top': '5px',
+ 'margin': '0px'
+ }
+ });
+
+ return labelsContainer;
+ }
+
+ private asAbsolutePath(filePath: string): string {
+ return path.join(this._root || '', filePath);
+ }
+
+ private createTasks(view: azdata.ModelView): azdata.Component {
+ const tasksContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'row',
+ width: '100%',
+ height: '50px',
+ }).component();
+ const predictionMetadata: IActionMetadata = {
+ title: constants.makePredictionTitle,
+ description: constants.makePredictionDesc,
+ iconPath: {
+ dark: this.asAbsolutePath('images/makePredictions.svg'),
+ light: this.asAbsolutePath('images/makePredictions.svg'),
+ },
+ link: '',
+ command: constants.mlsPredictModelCommand
+ };
+ const predictionButton = this.createTaskButton(view, predictionMetadata);
+ const importMetadata: IActionMetadata = {
+ title: constants.importModelTitle,
+ description: constants.importModelDesc,
+ iconPath: {
+ dark: this.asAbsolutePath('images/makePredictions.svg'),
+ light: this.asAbsolutePath('images/makePredictions.svg'),
+ },
+ link: '',
+ command: constants.mlImportModelCommand
+ };
+ const importModelsButton = this.createTaskButton(view, importMetadata);
+ const notebookMetadata: IActionMetadata = {
+ title: constants.createNotebookTitle,
+ description: constants.createNotebookDesc,
+ iconPath: {
+ dark: this.asAbsolutePath('images/createNotebook.svg'),
+ light: this.asAbsolutePath('images/createNotebook.svg'),
+ },
+ link: '',
+ command: constants.notebookCommandNew
+ };
+ const notebookModelsButton = this.createTaskButton(view, notebookMetadata);
+ tasksContainer.addItems([predictionButton, importModelsButton, notebookModelsButton], {
+ CSSStyles: {
+ 'padding': '10px'
+ }
+ });
+
+ return tasksContainer;
+ }
+
+ private createTaskButton(view: azdata.ModelView, taskMetaData: IActionMetadata): azdata.Component {
+ const maxHeight = 106;
+ const maxWidth = 250;
+ const mainContainer = view.modelBuilder.divContainer().withLayout({
+ width: maxWidth,
+ height: maxHeight
+ }).withProperties({
+ clickable: true,
+ ariaRole: taskMetaData.title
+ }).component();
+ const iconContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'row',
+ width: maxWidth,
+ height: maxHeight - 20,
+ alignItems: 'flex-start'
+ }).component();
+ const labelsContainer = view.modelBuilder.flexContainer().withLayout({
+ flexFlow: 'column',
+ width: maxWidth - 50,
+ height: maxHeight - 20,
+ justifyContent: 'space-between'
+ }).component();
+ const titleComponent = view.modelBuilder.text().withProperties({
+ value: taskMetaData.title,
+ CSSStyles: {
+ 'font-size': '14px',
+ //'color': '#323130',
+ 'font-weight': 'bold',
+ 'margin': '0px'
+ }
+ }).component();
+ const descriptionComponent = view.modelBuilder.text().withProperties({
+ value: taskMetaData.description,
+ CSSStyles: {
+ //'color': '#605E5C',
+ 'font-size': '13px',
+ 'margin': '0px'
+ }
+ }).component();
+ const linkComponent = view.modelBuilder.hyperlink().withProperties({
+ label: constants.learnMoreTitle,
+ url: taskMetaData.link,
+ CSSStyles: {
+ //'background-color': '#F2F2F2',
+ 'color': '#3794ff',
+ 'margin': '0px'
+ }
+ }).component();
+ const image = view.modelBuilder.image().withProperties({
+ width: '20px',
+ height: '20px',
+ iconPath: taskMetaData.iconPath,
+ iconHeight: '20px',
+ iconWidth: '20px'
+ }).component();
+ labelsContainer.addItems([titleComponent, descriptionComponent, linkComponent], {
+ CSSStyles: {
+ 'padding': '0px',
+ 'padding-bottom': '5px',
+ 'width': '180px',
+ 'margin': '0px'
+ }
+ });
+ iconContainer.addItem(image, {
+ CSSStyles: {
+ 'padding-top': '10px',
+ 'padding-right': '10px'
+ }
+ });
+ iconContainer.addItem(labelsContainer, {
+ CSSStyles: {
+ 'padding-top': '5px',
+ 'padding-right': '10px'
+ }
+ });
+ mainContainer.addItems([iconContainer], {
+ CSSStyles: {
+ //'background-color': '#f4f4f4',
+ 'padding': '10px',
+ 'border-radius': '5px',
+ 'border-color': '#f2f2f2',
+ 'border': '1px solid'
+ }
+ });
+ mainContainer.onDidClick(async () => {
+ if (taskMetaData.command) {
+ await this._apiWrapper.executeCommand(taskMetaData.command);
+ }
+ });
+ return mainContainer;
+ }
+}