ML- Added a radio button to enter new model table name (#10343)

This commit is contained in:
Leila Lali
2020-05-11 17:29:28 -07:00
committed by GitHub
parent 21e6ba92c2
commit 301ce1cf87
17 changed files with 231 additions and 94 deletions

View File

@@ -124,6 +124,8 @@ export const extLangUpdateFailedError = localize('extLang.updateFailedError', "F
export const modelUpdateFailedError = localize('models.modelUpdateFailedError', "Failed to update the model");
export const databaseName = localize('databaseName', "Models database");
export const tableName = localize('tableName', "Models table");
export const existingTableName = localize('existingTableName', "Existing table");
export const newTableName = localize('newTableName', "New table");
export const modelName = localize('models.name', "Name");
export const modelFileName = localize('models.fileName', "File");
export const modelDescription = localize('models.description', "Description");
@@ -211,20 +213,20 @@ export const sqlMlDocTitle = localize('sqlMlDocTitle', "SQL machine learning doc
export const sqlMlDocDesc = localize('sqlMlDocDesc', "Learn how to use machine learning in SQL Server and SQL on Azure, to run Python and R scripts on relational data.");
export const sqlMlsDocTitle = localize('sqlMlsDocTitle', "SQL Server Machine Learning Services (Python and R)");
export const sqlMlsDocDesc = localize('sqlMlsDocDesc', "Get started with Machine Learning Services on SQL Server and how to install it on Windows and Linux.");
export const sqlMlsAzureDocTitle = localize('sqlMlsAzureDocTitle', "Machine Learning Services in Azure SQL Managed Instance (preview)");
export const sqlMlsAzureDocDesc = localize('sqlMlsAzureDocDesc', "Get started with Machine Learning Services in Azure SQL Managed Instances.");
export const sqlMlsMIDocTitle = localize('sqlMlsMIDocTitle', "Machine Learning Services in Azure SQL Managed Instance (preview)");
export const sqlMlsMIDocDesc = localize('sqlMlsMIDocDesc', "Get started with Machine Learning Services in Azure SQL Managed Instances.");
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.");
export const onnxOnEdgeOdbcDocTitle = localize('onnxOnEdgeOdbcDocTitle', "Machine learning and AI with ONNX in SQL Database Edge Preview");
export const onnxOnEdgeOdbcDocDesc = localize('onnxOnEdgeOdbcDocDesc', "Get started with machine learning in Azure SQL Database Edge");
// Links
//
export const mlsDocuments = 'https://docs.microsoft.com/sql/advanced-analytics/?view=sql-server-ver15';
export const odbcDriverWindowsDocuments = 'https://docs.microsoft.com/sql/connect/odbc/windows/microsoft-odbc-driver-for-sql-server-on-windows?view=sql-server-ver15';
export const odbcDriverLinuxDocuments = 'https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15';
export const mlDocLink = 'https://docs.microsoft.com/sql/machine-learning/';
export const mlsDocLink = 'https://docs.microsoft.com/sql/machine-learning/what-is-sql-server-machine-learning';
export const mlsAzureDocLink = 'https://docs.microsoft.com/azure/sql-database/sql-database-managed-instance-machine-learning-services-overview';
export const installMlsWindowsDocs = 'https://docs.microsoft.com/sql/advanced-analytics/install/sql-machine-learning-services-windows-install?view=sql-server-ver15';
export const odbcDriverDocuments = 'https://go.microsoft.com/fwlink/?linkid=2129818';
export const mlDocLink = 'https://go.microsoft.com/fwlink/?linkid=2128671';
export const mlsDocLink = 'https://go.microsoft.com/fwlink/?linkid=2128672';
export const mlsAzureDocLink = 'https://go.microsoft.com/fwlink/?linkid=2128673';
export const onnxOnEdgeDocs = 'https://go.microsoft.com/fwlink/?linkid=2128882';
// CSS Styles
//

View File

@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as azdata from 'azdata';
import { QueryRunner } from '../common/queryRunner';
import * as constants from '../common/constants';
@@ -22,13 +21,6 @@ export class PackageManagementService {
) {
}
/**
* Opens server config documents
*/
public async openDocuments(): Promise<boolean> {
return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.mlsDocuments));
}
/**
* Returns true if mls is installed in the give SQL server instance
*/

View File

@@ -177,7 +177,7 @@ AS (
)
SELECT
${this.getPredictColumnNames(columns, 'predict_input')}, ${this.getPredictInputColumnNames(outputColumns, 'p')}
FROM PREDICT(MODEL = @model, DATA = predict_input)
FROM PREDICT(MODEL = @model, DATA = predict_input, runtime=onnx)
WITH (
${this.getOutputParameters(outputColumns)}
) AS p
@@ -198,7 +198,7 @@ AS (
)
SELECT
${this.getPredictColumnNames(columns, 'predict_input')}, ${this.getOutputColumnNames(outputColumns, 'p')}
FROM PREDICT(MODEL = ${modelBytes}, DATA = predict_input)
FROM PREDICT(MODEL = ${modelBytes}, DATA = predict_input, runtime=onnx)
WITH (
${this.getOutputParameters(outputColumns)}
) AS p

View File

@@ -24,13 +24,6 @@ function createContext(): TestContext {
}
describe('Package Management Service', () => {
it('openDocuments should open document in browser successfully', async function (): Promise<void> {
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.openDocuments(), true);
});
it('isMachineLearningServiceEnabled should return true if external script is enabled', async function (): Promise<void> {
const context = createContext();
context.queryRunner.setup(x => x.isMachineLearningServiceEnabled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));

View File

@@ -115,6 +115,10 @@ export function createViewContext(): ViewTestContext {
onCardSelectedChanged: onClick.event
});
let group: () => azdata.GroupContainer = () => Object.assign({}, componentBase, container, {
collapsed: false,
});
let declarativeTableBuilder: azdata.ComponentBuilder<azdata.DeclarativeTableComponent> = {
component: () => declarativeTable(),
withProperties: () => declarativeTableBuilder,
@@ -172,6 +176,15 @@ export function createViewContext(): ViewTestContext {
withProperties: () => cardBuilder,
withValidation: () => cardBuilder
};
let groupBuilder: azdata.GroupBuilder = {
component: () => {
return group();
},
withProperties: () => groupBuilder,
withValidation: () => groupBuilder,
withItems: () => groupBuilder,
withLayout: () => groupBuilder
};
let imageBuilder: azdata.ComponentBuilder<azdata.ImageComponent> = {
component: () => {
@@ -223,7 +236,7 @@ export function createViewContext(): ViewTestContext {
dashboardWidget: undefined!,
dashboardWebview: undefined!,
formContainer: () => formBuilder,
groupContainer: undefined!,
groupContainer: () => groupBuilder,
toolbarContainer: undefined!,
loadingComponent: () => loadingBuilder,
fileBrowserTree: undefined!,

View File

@@ -62,9 +62,13 @@ export class LocalModelsComponent extends ModelViewBase implements IDataComponen
.withLayout({
flexFlow: 'row',
justifyContent: 'space-between',
width: this.componentMaxLength + 200
width: this.componentMaxLength
}).withItems([
this._localPath, this._localBrowse]
this._localPath, this._localBrowse], {
CSSStyles: {
'padding-right': '5px'
}
}
).component();
this._form = modelBuilder.formContainer().withFormItems([{

View File

@@ -40,6 +40,7 @@ export class ModelSourcesComponent extends ModelViewBase implements IDataCompone
label: constants.localModelSource,
selected: this._sourceType === ModelSourceType.Local,
cardType: azdata.CardType.VerticalButton,
iconPath: { light: this.asAbsolutePath('images/fileUpload.svg'), dark: this.asAbsolutePath('images/fileUpload.svg') },
width: 50
}).component();
this._amlModel = modelBuilder.card()
@@ -49,6 +50,7 @@ export class ModelSourcesComponent extends ModelViewBase implements IDataCompone
label: constants.azureModelSource,
selected: this._sourceType === ModelSourceType.Azure,
cardType: azdata.CardType.VerticalButton,
iconPath: { light: this.asAbsolutePath('images/aml.svg'), dark: this.asAbsolutePath('images/aml.svg') },
width: 50
}).component();
@@ -59,6 +61,7 @@ export class ModelSourcesComponent extends ModelViewBase implements IDataCompone
label: constants.registeredModelsSource,
selected: this._sourceType === ModelSourceType.RegisteredModels,
cardType: azdata.CardType.VerticalButton,
iconPath: { light: this.asAbsolutePath('images/imported.svg'), dark: this.asAbsolutePath('images/imported.svg') },
width: 50
}).component();

View File

@@ -29,6 +29,7 @@ export class TableSelectionComponent extends ModelViewBase implements IDataCompo
private _dbTableComponent: azdata.FlexContainer | undefined;
private tableMaxLength = this.componentMaxLength * 2 + 70;
private _onSelectedChanged: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
private _existingTablesSelected: boolean = true;
public readonly onSelectedChanged: vscode.Event<void> = this._onSelectedChanged.event;
/**
@@ -45,24 +46,66 @@ export class TableSelectionComponent extends ModelViewBase implements IDataCompo
public registerComponent(modelBuilder: azdata.ModelBuilder, databaseTitle: string, tableTitle: string): azdata.Component {
this._databases = modelBuilder.dropDown().withProperties({
width: this.componentMaxLength,
editable: this._settings.editable,
fireOnTextChange: this._settings.editable
}).component();
this._tables = modelBuilder.dropDown().withProperties({
width: this.componentMaxLength,
editable: this._settings.editable,
fireOnTextChange: this._settings.editable
width: this.componentMaxLength - 10,
}).component();
this._databases.onValueChanged(async () => {
await this.onDatabaseSelected();
});
const existingTableButton = modelBuilder.radioButton().withProperties({
name: 'tableName',
value: 'existing',
label: 'Existing table',
checked: true
}).component();
const newTableButton = modelBuilder.radioButton().withProperties({
name: 'tableName',
value: 'new',
label: 'New table',
checked: false
}).component();
const newTableName = modelBuilder.inputBox().withProperties({
width: this.componentMaxLength - 10,
enabled: false
}).component();
const group = modelBuilder.groupContainer().withItems([
existingTableButton,
this._tables,
newTableButton,
newTableName
], {
CSSStyles: {
'padding-top': '5px'
}
}).component();
existingTableButton.onDidClick(() => {
if (this._tables) {
this._tables.enabled = existingTableButton.checked;
}
newTableName.enabled = !existingTableButton.checked;
this._existingTablesSelected = existingTableButton.checked || false;
});
newTableButton.onDidClick(() => {
if (this._tables) {
this._tables.enabled = !newTableButton.checked;
}
newTableName.enabled = newTableButton.checked;
this._existingTablesSelected = existingTableButton.checked || false;
});
newTableName.onTextChanged(async () => {
this._selectedTableName = newTableName.value || '';
await this.onTableSelected();
});
this._tables.onValueChanged(async (value) => {
// There's an issue with dropdown doesn't set the value in editable mode. this is the workaround
if (this._tables && value) {
this._selectedTableName = this._settings.editable ? value : value.selected;
this._selectedTableName = value.selected;
}
await this.onTableSelected();
});
@@ -73,22 +116,32 @@ export class TableSelectionComponent extends ModelViewBase implements IDataCompo
}], { info: databaseTitle }).withLayout({
padding: '0px'
}).component();
const tableForm = modelBuilder.formContainer().withFormItems([{
title: tableTitle,
component: this._tables
}], { info: tableTitle }).withLayout({
padding: '0px'
}).component();
const tableForm = modelBuilder.formContainer();
if (this._settings.editable) {
tableForm.addFormItem({
title: tableTitle,
component: group
}, { info: tableTitle });
} else {
tableForm.addFormItem({
title: tableTitle,
component: this._tables
}, { info: tableTitle });
}
this._dbTableComponent = modelBuilder.flexContainer().withItems([
databaseForm,
tableForm
tableForm.withLayout({
padding: '0px'
}).component()
], {
flex: '0 0 auto',
CSSStyles: {
'align-items': 'flex-start'
}
}).withLayout({
flexFlow: 'row',
flexFlow: this._settings.editable ? 'column' : 'row',
justifyContent: 'space-between',
width: this.tableMaxLength
}).component();
@@ -163,31 +216,33 @@ export class TableSelectionComponent extends ModelViewBase implements IDataCompo
}
private async onDatabaseSelected(): Promise<void> {
this._tableNames = await this.listTableNames(this.databaseName || '');
let tableNames = this._tableNames;
if (this._existingTablesSelected) {
this._tableNames = await this.listTableNames(this.databaseName || '');
let tableNames = this._tableNames;
if (this._tableNames && !this._settings.preSelected && !this._tableNames.find(x => x.tableName === constants.selectTableTitle)) {
const firstRow: DatabaseTable = { tableName: constants.selectTableTitle, databaseName: '', schema: '' };
tableNames = [firstRow].concat(this._tableNames);
}
if (this._tables && tableNames && tableNames.length > 0) {
this._tables.values = tableNames.map(t => this.getTableFullName(t));
if (this.importTable) {
const selectedTable = tableNames.find(t => t.tableName === this.importTable?.tableName && t.schema === this.importTable?.schema);
if (selectedTable) {
this._selectedTableName = this.getTableFullName(selectedTable);
this._tables.value = this.getTableFullName(selectedTable);
} else {
this._selectedTableName = this._settings.editable ? this.getTableFullName(this.importTable) : this.getTableFullName(tableNames[0]);
}
} else {
this._selectedTableName = this.getTableFullName(tableNames[0]);
if (this._tableNames && !this._settings.preSelected && !this._tableNames.find(x => x.tableName === constants.selectTableTitle)) {
const firstRow: DatabaseTable = { tableName: constants.selectTableTitle, databaseName: '', schema: '' };
tableNames = [firstRow].concat(this._tableNames);
}
if (this._tables && tableNames && tableNames.length > 0) {
this._tables.values = tableNames.map(t => this.getTableFullName(t));
if (this.importTable) {
const selectedTable = tableNames.find(t => t.tableName === this.importTable?.tableName && t.schema === this.importTable?.schema);
if (selectedTable) {
this._selectedTableName = this.getTableFullName(selectedTable);
this._tables.value = this.getTableFullName(selectedTable);
} else {
this._selectedTableName = this._settings.editable ? this.getTableFullName(this.importTable) : this.getTableFullName(tableNames[0]);
}
} else {
this._selectedTableName = this.getTableFullName(tableNames[0]);
}
this._tables.value = this._selectedTableName;
} else if (this._tables) {
this._tables.values = [];
this._tables.value = '';
}
this._tables.value = this._selectedTableName;
} else if (this._tables) {
this._tables.values = [];
this._tables.value = '';
}
await this.onTableSelected();
}

View File

@@ -8,7 +8,6 @@ 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';
import { PredictService } from '../../prediction/predictService';
interface IActionMetadata {
@@ -182,12 +181,12 @@ export class DashboardWidget {
});
const videosContainer = this.createVideoLinkContainers(view, [
{
iconPath: { light: 'images/video1.svg', dark: 'images/video1.svg' },
iconPath: { light: 'images/aiMlSqlServer.svg', dark: 'images/aiMlSqlServer.svg' },
description: 'Artificial intelligence and machine learning with SQL Server 2019',
link: 'https://www.youtube.com/watch?v=sE99cSoFOHs'
},
{
iconPath: { light: 'images/video2.svg', dark: 'images/video2.svg' },
iconPath: { light: 'images/sqlServerMl.svg', dark: 'images/sqlServerMl.svg' },
description: 'SQL Server Machine Learning Services',
link: 'https://www.youtube.com/watch?v=R4GCBoxADyQ'
}
@@ -199,7 +198,7 @@ export class DashboardWidget {
const moreVideosContainer = this.createVideoLinkContainers(view, [
{
iconPath: { light: 'images/video2.svg', dark: 'images/video2.svg' },
iconPath: { light: 'images/notebooksIntro.svg', dark: 'images/notebooksIntro.svg' },
description: 'Introduction to Azure Data Studio Notebooks',
link: 'https://www.youtube.com/watch?v=Nt4kIHQ0IOc'
}
@@ -316,7 +315,7 @@ export class DashboardWidget {
'background-position': 'top',
'width': `${maxWidth}px`,
'height': '110px',
'background-size': `{maxWidth}px 120px`
'background-size': `${maxWidth}px 120px`
}
});
videosContainer.addItem(descriptionComponent);
@@ -349,15 +348,15 @@ export class DashboardWidget {
link: constants.mlsDocLink
},
{
title: constants.sqlMlsAzureDocTitle,
description: constants.sqlMlsAzureDocDesc,
link: constants.mlsAzureDocLink
title: constants.onnxOnEdgeOdbcDocTitle,
description: constants.onnxOnEdgeOdbcDocDesc,
link: constants.onnxOnEdgeDocs
}];
const moreLink = {
title: constants.mlsInstallOdbcDocTitle,
description: constants.mlsInstallOdbcDocDesc,
link: utils.isWindows() ? constants.odbcDriverWindowsDocuments : constants.odbcDriverLinuxDocuments
link: constants.odbcDriverDocuments
};
const styles = {
'padding': '10px'
@@ -499,7 +498,7 @@ export class DashboardWidget {
}
private createTaskButton(view: azdata.ModelView, taskMetaData: IActionMetadata): azdata.Component {
const maxHeight = 106;
const maxHeight = 116;
const maxWidth = 250;
const mainContainer = view.modelBuilder.divContainer().withLayout({
width: maxWidth,