diff --git a/extensions/machine-learning/images/emptyState.svg b/extensions/machine-learning/images/emptyState.svg
new file mode 100644
index 0000000000..15ce8973bc
--- /dev/null
+++ b/extensions/machine-learning/images/emptyState.svg
@@ -0,0 +1,30 @@
+
diff --git a/extensions/machine-learning/src/common/constants.ts b/extensions/machine-learning/src/common/constants.ts
index 018c340dab..dea4aecaac 100644
--- a/extensions/machine-learning/src/common/constants.ts
+++ b/extensions/machine-learning/src/common/constants.ts
@@ -149,14 +149,16 @@ export const extLangInstallFailedError = localize('extLang.installFailedError',
export const extLangUpdateFailedError = localize('extLang.updateFailedError', "Failed to update language");
export const modelUpdateFailedError = localize('models.modelUpdateFailedError', "Failed to update the model");
-export const modelsListEmptyMessage = localize('models.modelsListEmptyMessage', "No models yet");
+export const modelsListEmptyMessage = localize('models.modelsListEmptyMessage', "No models found");
+export const selectModelTableMessage = localize('models.selectModelTableMessage', "Select table");
+export const selectModelDatabaseMessage = localize('models.selectModelDatabaseMessage', "Select Database");
export const azureModelsListEmptyTitle = localize('models.azureModelsListEmptyTitle', "No models found");
export const azureModelsListEmptyDescription = localize('models.azureModelsListEmptyDescription', "Select another Azure ML workspace");
-export const modelsListEmptyDescription = localize('models.modelsListEmptyDescription', "Use import wizard to add models to this table");
-export const databaseName = localize('databaseName', "Models database");
+export const modelsListEmptyDescription = localize('models.modelsListEmptyDescription', "Select another database or table");
+export const databaseName = localize('databaseName', "Database");
export const databaseToStoreInfo = localize('databaseToStoreInfo', "Select a database to store the new model.");
export const tableToStoreInfo = localize('tableToStoreInfo', "Select an existing table that conforms the model schema or create a new one to store the imported model.");
-export const tableName = localize('tableName', "Models table");
+export const tableName = localize('tableName', "Table");
export const modelTableInfo = localize('modelTableInfo', "Select a model table to view the list of existing / imported models.");
export const modelDatabaseInfo = localize('modelDatabaseInfo', "Select a database where existing / imported models are stored.");
export const existingTableName = localize('existingTableName', "Existing table");
@@ -183,6 +185,8 @@ export const dataTypeName = localize('predict.dataTypeName', "Type");
export const displayName = localize('predict.displayName', "Display name");
export const inputName = localize('predict.inputName', "Model input");
export const selectColumnTitle = localize('predict.selectColumnTitle', "Select column...");
+export const selectModelDatabaseTitle = localize('models.selectModelDatabaseTitle', "Select database with models");
+export const selectModelTableTitle = localize('models.selectModelTableTitle', "Select tables with models");
export const selectDatabaseTitle = localize('predict.selectDatabaseTitle', "Select database");
export const selectTableTitle = localize('predict.selectTableTitle', "Select table");
export const outputName = localize('predict.outputName', "Name");
@@ -210,7 +214,13 @@ export const currentModelsTitle = localize('models.currentModelsTitle', "Models"
export const importModelDoneButton = localize('models.importModelDoneButton', "Import");
export const predictModel = localize('models.predictModel', "Predict");
export const registerModelTitle = localize('models.RegisterWizard', "Import models");
-export const importedModelTitle = localize('models.importedModelTitle', "Imported models");
+export const viewImportModelsTitle = localize('models.viewImportModelsTitle', "View and import models");
+export const viewImportModelsDesc = localize('models.viewImportModelsDesc',
+ "Machine Learning models can be stored in one or more databases and tables. Select the model database and table to view the models within them.");
+export const viewImportModeledForPredictDesc = localize('models.viewImportModeledForPredictDesc',
+ "The models are stored in one or more databases and tables. Select the model database and table to view models in them.");
+export const learnMoreLink = localize('models.learnMoreLink', "Learn more.");
+
export const importModelTitle = localize('models.importModelTitle', "Import or view models");
export const editModelTitle = localize('models.editModelTitle', "Edit model");
export const importModelDesc = localize('models.importModelDesc', "Import or view machine learning models stored in database");
@@ -268,6 +278,8 @@ export const mlsInstallOdbcDocDesc = localize('mlsInstallOdbcDocDesc', "This doc
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");
+export function getDataCount(dataCount: number): string { return localize('ml.dataCount', "Showing {0} model(s)", dataCount); }
+
// Links
//
export const odbcDriverDocuments = 'https://go.microsoft.com/fwlink/?linkid=2129818';
@@ -277,12 +289,13 @@ export const mlsDocLink = 'https://go.microsoft.com/fwlink/?linkid=2128672';
export const mlsMIDocLink = 'https://go.microsoft.com/fwlink/?linkid=2128673';
export const onnxOnEdgeDocs = 'https://go.microsoft.com/fwlink/?linkid=2128882';
export const managePackagesDocs = 'https://go.microsoft.com/fwlink/?linkid=2129919';
+export const importModelsDoc = 'https://go.microsoft.com/fwlink/?linkid=2129796';
// CSS Styles
//
export namespace cssStyles {
export const title = { 'font-size': '14px', 'font-weight': '600' };
- export const tableHeader = { 'text-align': 'left', 'font-weight': 'bold', 'text-transform': 'uppercase', 'font-size': '10px', 'user-select': 'text', 'border': 'none' };
+ export const tableHeader = { 'text-align': 'left', 'font-weight': 'bold', 'text-transform': 'capitalize', 'font-size': '10px', 'user-select': 'text', 'border': 'none', 'border-bottom': '1px solid #ccc' };
export const tableRow = { 'border-top': 'solid 1px #ccc', 'border-bottom': 'solid 1px #ccc', 'border-left': 'none', 'border-right': 'none' };
export const hyperlink = { 'user-select': 'text', 'color': '#0078d4', 'text-decoration': 'underline', 'cursor': 'pointer' };
export const text = { 'margin-block-start': '0px', 'margin-block-end': '0px' };
diff --git a/extensions/machine-learning/src/views/dataInfoComponent.ts b/extensions/machine-learning/src/views/dataInfoComponent.ts
index 6cf26047c1..5d4329072c 100644
--- a/extensions/machine-learning/src/views/dataInfoComponent.ts
+++ b/extensions/machine-learning/src/views/dataInfoComponent.ts
@@ -13,6 +13,8 @@ import { ViewBase } from './viewBase';
export interface iconSettings {
width?: number,
height?: number,
+ containerWidth?: number,
+ containerHeight?: number,
css?: { [key: string]: string },
path?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri }
}
@@ -24,12 +26,13 @@ export class DataInfoComponent extends ViewBase {
private _labelComponent: azdata.TextComponent | undefined;
private _descriptionComponent: azdata.TextComponent | undefined;
private _loadingComponent: azdata.LoadingComponent | undefined;
- private _width: number = 100;
- private _height: number = 100;
+ private _width: number = 200;
+ private _height: number = 200;
private _title: string = '';
private _description: string = '';
private _iconComponent: azdata.ImageComponent | undefined;
private _iconSettings: iconSettings | undefined;
+ private _defaultIconSize = 128;
constructor(apiWrapper: ApiWrapper, parent: ModelViewBase) {
@@ -38,9 +41,11 @@ export class DataInfoComponent extends ViewBase {
public registerComponent(modelBuilder: azdata.ModelBuilder): azdata.Component {
this._descriptionComponent = modelBuilder.text().withProperties({
+ value: this._description,
width: 200
}).component();
this._labelComponent = modelBuilder.text().withProperties({
+ value: this._title,
width: 200
}).component();
this._labelContainer = modelBuilder.flexContainer().withLayout({
@@ -55,26 +60,26 @@ export class DataInfoComponent extends ViewBase {
if (!this._iconSettings) {
this._iconSettings = {
css: {},
- height: 50,
- width: 50,
+ height: this._defaultIconSize,
+ width: this._defaultIconSize,
path: ''
,
};
}
this._iconComponent = modelBuilder.image().withProperties({
- width: 100,
- height: 100,
- iconWidth: this._iconSettings.width,
- iconHeight: this._iconSettings.height,
+ width: this._iconSettings?.containerWidth ?? this._defaultIconSize,
+ height: this._iconSettings?.containerHeight ?? this._defaultIconSize,
+ iconWidth: this._iconSettings?.width ?? this._defaultIconSize,
+ iconHeight: this._iconSettings?.height ?? this._defaultIconSize,
title: this._title
}).component();
let iconContainer = modelBuilder.flexContainer().withLayout({
- width: 100,
+ width: this._iconSettings?.containerWidth ?? this._defaultIconSize,
}).component();
iconContainer.addItem(this._iconComponent, {
- CSSStyles: this._iconSettings.css
+ CSSStyles: this._iconSettings?.css ?? {}
});
this._labelContainer.addItem(iconContainer);
diff --git a/extensions/machine-learning/src/views/models/azureModelsComponent.ts b/extensions/machine-learning/src/views/models/azureModelsComponent.ts
index 8ac45799a2..b34375e308 100644
--- a/extensions/machine-learning/src/views/models/azureModelsComponent.ts
+++ b/extensions/machine-learning/src/views/models/azureModelsComponent.ts
@@ -57,8 +57,8 @@ export class AzureModelsComponent extends ModelViewBase implements IDataComponen
this._emptyModelsComponent.height = 250;
this._emptyModelsComponent.iconSettings = {
css: { 'padding-top': '20px' },
- width: 100,
- height: 100
+ width: 128,
+ height: 128
};
this._emptyModelsComponent.registerComponent(modelBuilder);
diff --git a/extensions/machine-learning/src/views/models/azureModelsTable.ts b/extensions/machine-learning/src/views/models/azureModelsTable.ts
index 1dd41a47aa..b9cb25d031 100644
--- a/extensions/machine-learning/src/views/models/azureModelsTable.ts
+++ b/extensions/machine-learning/src/views/models/azureModelsTable.ts
@@ -132,11 +132,13 @@ export class AzureModelsTable extends ModelViewBase implements IDataComponent {
@@ -54,47 +63,76 @@ export class CurrentModelsComponent extends ModelViewBase implements IPageView {
this._dataTable = new CurrentModelsTable(this._apiWrapper, this, this._settings);
this._dataTable.registerComponent(modelBuilder);
+ let dataCountString: string = constants.getDataCount(0);
+
+ this._tableDataCountContainer = modelBuilder.flexContainer().component();
+ this._tableDataCountComponent = modelBuilder.text().withProperties({
+ value: dataCountString,
+ margin: '0'
+ }).component();
+ this._tableDataCountContainer.addItem(this._tableDataCountComponent,
+ {
+ CSSStyles: {
+ 'font-size': '13px',
+ 'margin': '0'
+ }
+ });
+
+
let formModelBuilder = modelBuilder.formContainer();
this._loader = modelBuilder.loadingComponent()
.withItem(formModelBuilder.component())
.withProperties({
loading: true
}).component();
- this._labelComponent = modelBuilder.text().withProperties({
- width: 200,
- value: constants.modelsListEmptyMessage
- }).component();
- this._descriptionComponent = modelBuilder.text().withProperties({
- width: 200,
- value: constants.modelsListEmptyDescription
- }).component();
+ this._emptyModelsComponent = new DataInfoComponent(this._apiWrapper, this);
+ this._emptyModelsComponent.width = 200;
+ this._emptyModelsComponent.height = 250;
+ this._emptyModelsComponent.title = constants.modelsListEmptyMessage;
+ this._emptyModelsComponent.description = constants.modelsListEmptyDescription;
+ this._emptyModelsComponent.iconSettings = {
+ css: { 'padding-top': '30px' },
+ path: this.asAbsolutePath('images/emptyTable.svg'),
+ width: 128,
+ height: 128
+ };
+ this._emptyModelsComponent.registerComponent(modelBuilder);
this._labelContainer = modelBuilder.flexContainer().withLayout({
flexFlow: 'column',
- width: 800,
+ width: '750px',
height: '400px',
- justifyContent: 'center'
+ justifyContent: 'flex-start',
+ textAlign: 'center'
}).component();
+ this._subheadingContainer = modelBuilder.flexContainer().withLayout({
+ flexFlow: 'column',
+ width: '452px'
+ }).component();
+ this._subheadingTextComponent = modelBuilder.text().withProperties({
+ value: this.modelActionType === ModelActionType.Import ? constants.viewImportModelsDesc : constants.viewImportModeledForPredictDesc,
+ CSSStyles: {
+ 'font-size': '13px'
+ }
+ }).component();
+ this._subheadingLinkComponent = modelBuilder.hyperlink().withProperties({
+ label: constants.learnMoreLink,
+ url: constants.importModelsDoc,
+ CSSStyles: {
+ 'font-size': '13px'
+ }
+ }).component();
+ if (this._emptyModelsComponent.component) {
+ this._labelContainer.addItem(this._emptyModelsComponent.component
+ , {
+ CSSStyles: {
+ 'margin': '0 auto'
+ }
+ });
- this._labelContainer.addItem(
- this._labelComponent
- , {
- CSSStyles: {
- 'align-items': 'center',
- 'padding-top': '30px',
- 'padding-left': `${this.componentMaxLength}px`,
- 'font-size': '16px'
- }
- });
- this._labelContainer.addItem(
- this._descriptionComponent
- , {
- CSSStyles: {
- 'align-items': 'center',
- 'padding-top': '10px',
- 'padding-left': `${this.componentMaxLength - 50}px`,
- 'font-size': '13px'
- }
- });
+ }
+ this._subheadingContainer.addItems(
+ [this._subheadingTextComponent, this._subheadingLinkComponent]
+ );
this.addComponents(formModelBuilder);
return this._loader;
@@ -102,20 +140,28 @@ export class CurrentModelsComponent extends ModelViewBase implements IPageView {
public addComponents(formBuilder: azdata.FormBuilder) {
this._formBuilder = formBuilder;
- if (this._tableSelectionComponent && this._dataTable && this._labelContainer) {
+ if (this._tableSelectionComponent && this._dataTable && this._tableDataCountContainer && this._labelContainer && this._subheadingContainer) {
+ formBuilder.addFormItem({ title: '', component: this._subheadingContainer });
this._tableSelectionComponent.addComponents(formBuilder);
+ formBuilder.addFormItem({ title: '', component: this._tableDataCountContainer });
this._dataTable.addComponents(formBuilder);
+
if (this._dataTable.isEmpty) {
formBuilder.addFormItem({ title: '', component: this._labelContainer });
}
+ if (this._tableDataCountComponent) {
+ this._tableDataCountComponent.value = constants.getDataCount(this._dataTable.modelCounts);
+ }
}
}
public removeComponents(formBuilder: azdata.FormBuilder) {
- if (this._tableSelectionComponent && this._dataTable && this._labelContainer) {
+ if (this._tableSelectionComponent && this._dataTable && this._labelContainer && this._tableDataCountContainer && this._subheadingContainer) {
this._tableSelectionComponent.removeComponents(formBuilder);
this._dataTable.removeComponents(formBuilder);
formBuilder.removeFormItem({ title: '', component: this._labelContainer });
+ formBuilder.removeFormItem({ title: '', component: this._tableDataCountContainer });
+ formBuilder.removeFormItem({ title: '', component: this._subheadingContainer });
}
}
@@ -130,6 +176,9 @@ export class CurrentModelsComponent extends ModelViewBase implements IPageView {
* Refreshes the view
*/
public async refresh(): Promise {
+ if (this._emptyModelsComponent) {
+ await this._emptyModelsComponent.refresh();
+ }
await this.onLoading();
try {
@@ -161,6 +210,24 @@ export class CurrentModelsComponent extends ModelViewBase implements IPageView {
await this.storeImportConfigTable();
if (this._dataTable) {
await this._dataTable.refresh();
+ if (this._emptyModelsComponent) {
+ if (this._tableSelectionComponent?.defaultDbNameIsSelected) {
+ this._emptyModelsComponent.title = constants.selectModelDatabaseMessage;
+ this._emptyModelsComponent.description = '';
+ } else if (this._tableSelectionComponent?.defaultTableNameIsSelected) {
+ this._emptyModelsComponent.title = constants.selectModelTableMessage;
+ this._emptyModelsComponent.description = '';
+
+ } else {
+ this._emptyModelsComponent.title = constants.modelsListEmptyMessage;
+ this._emptyModelsComponent.description = constants.modelsListEmptyDescription;
+ }
+ await this._emptyModelsComponent.refresh();
+ }
+
+ if (this._tableDataCountComponent) {
+ this._tableDataCountComponent.value = constants.getDataCount(this._dataTable.modelCounts);
+ }
}
this.refreshComponents();
}
diff --git a/extensions/machine-learning/src/views/models/manageModels/currentModelsTable.ts b/extensions/machine-learning/src/views/models/manageModels/currentModelsTable.ts
index ea831d3b4d..203ab6316c 100644
--- a/extensions/machine-learning/src/views/models/manageModels/currentModelsTable.ts
+++ b/extensions/machine-learning/src/views/models/manageModels/currentModelsTable.ts
@@ -25,7 +25,7 @@ export class CurrentModelsTable extends ModelViewBase implements IDataComponent<
private _downloadedFile: ModelArtifact | undefined;
private _onModelSelectionChanged: vscode.EventEmitter = new vscode.EventEmitter();
public readonly onModelSelectionChanged: vscode.Event = this._onModelSelectionChanged.event;
- public isEmpty: boolean = false;
+ public modelCounts: number = 0;
/**
* Creates new view
@@ -40,7 +40,23 @@ export class CurrentModelsTable extends ModelViewBase implements IDataComponent<
*/
public registerComponent(modelBuilder: azdata.ModelBuilder): azdata.Component {
this._modelBuilder = modelBuilder;
- let columns = [
+ let columns: azdata.DeclarativeTableColumn[] = [];
+ if (this._settings.selectable) {
+ columns.push(
+ { // Action
+ displayName: '',
+ valueType: azdata.DeclarativeDataType.component,
+ isReadOnly: true,
+ width: 50,
+ headerCssStyles: {
+ ...constants.cssStyles.tableHeader
+ },
+ rowCssStyles: {
+ ...constants.cssStyles.tableRow
+ },
+ });
+ }
+ columns.push(...[
{ // Name
displayName: constants.modelName,
ariaLabel: constants.modelName,
@@ -92,6 +108,20 @@ export class CurrentModelsTable extends ModelViewBase implements IDataComponent<
rowCssStyles: {
...constants.cssStyles.tableRow
},
+ }
+ ]);
+ if (this._settings.editable) {
+ columns.push(...[{ // Action
+ displayName: '',
+ valueType: azdata.DeclarativeDataType.component,
+ isReadOnly: true,
+ width: 50,
+ headerCssStyles: {
+ ...constants.cssStyles.tableHeader
+ },
+ rowCssStyles: {
+ ...constants.cssStyles.tableRow
+ },
},
{ // Action
displayName: '',
@@ -105,22 +135,7 @@ export class CurrentModelsTable extends ModelViewBase implements IDataComponent<
...constants.cssStyles.tableRow
},
}
- ];
- if (this._settings.editable) {
- columns.push(
- { // Action
- displayName: '',
- valueType: azdata.DeclarativeDataType.component,
- isReadOnly: true,
- width: 50,
- headerCssStyles: {
- ...constants.cssStyles.tableHeader
- },
- rowCssStyles: {
- ...constants.cssStyles.tableRow
- },
- }
- );
+ ]);
columns.push(
{ // Action
displayName: '',
@@ -192,9 +207,10 @@ export class CurrentModelsTable extends ModelViewBase implements IDataComponent<
tableData = tableData.concat(models.map(model => this.createTableRow(model)));
}
- this.isEmpty = models === undefined || models.length === 0;
-
+ this.modelCounts = models === undefined || models.length === 0 ? 0 : models.length;
this._table.data = tableData;
+ } else {
+ this.modelCounts = 0;
}
this.onModelSelected();
await this.onLoaded();
@@ -212,12 +228,16 @@ export class CurrentModelsTable extends ModelViewBase implements IDataComponent<
}
}
+ public get isEmpty(): boolean {
+ return this.modelCounts === 0;
+ }
+
private createTableRow(model: ImportedModel): any[] {
let row: any[] = [model.modelName, model.created, model.version, model.framework];
if (this._modelBuilder) {
const selectButton = this.createSelectButton(model);
if (selectButton) {
- row.push(selectButton);
+ row = [selectButton, model.modelName, model.created, model.version, model.framework];
}
const editButtons = this.createEditButtons(model);
if (editButtons && editButtons.length > 0) {
diff --git a/extensions/machine-learning/src/views/models/manageModels/manageModelsDialog.ts b/extensions/machine-learning/src/views/models/manageModels/manageModelsDialog.ts
index 02616a7ea6..704524783a 100644
--- a/extensions/machine-learning/src/views/models/manageModels/manageModelsDialog.ts
+++ b/extensions/machine-learning/src/views/models/manageModels/manageModelsDialog.ts
@@ -39,7 +39,7 @@ export class ManageModelsDialog extends ModelViewBase {
await this.sendDataRequest(RegisterModelEventName, this.currentLanguagesTab?.modelTable?.importTable);
});
- let dialog = this.dialogView.createDialog(constants.importedModelTitle, [this.currentLanguagesTab]);
+ let dialog = this.dialogView.createDialog(constants.viewImportModelsTitle, [this.currentLanguagesTab]);
dialog.isWide = true;
dialog.customButtons = [registerModelButton];
this.mainViewPanel = dialog;
diff --git a/extensions/machine-learning/src/views/models/manageModels/modelImportLocationPage.ts b/extensions/machine-learning/src/views/models/manageModels/modelImportLocationPage.ts
index e1eb10a6a9..3288db96c8 100644
--- a/extensions/machine-learning/src/views/models/manageModels/modelImportLocationPage.ts
+++ b/extensions/machine-learning/src/views/models/manageModels/modelImportLocationPage.ts
@@ -39,17 +39,23 @@ export class ModelImportLocationPage extends ModelViewBase implements IPageView,
databaseTitle: constants.databaseName,
tableTitle: constants.tableName,
databaseInfo: constants.databaseToStoreInfo,
- tableInfo: constants.tableToStoreInfo
+ tableInfo: constants.tableToStoreInfo,
+ defaultDbName: constants.selectModelDatabaseTitle,
+ defaultTableName: constants.selectModelTableTitle,
+ useImportModelCache: true
});
this._dataInfoComponent = new DataInfoComponent(this._apiWrapper, this);
- this._dataInfoComponent.width = 300;
- this._dataInfoComponent.height = 300;
+ this._dataInfoComponent.width = 350;
this._dataInfoComponent.iconSettings = {
css: {
'border': 'solid',
- 'margin': '5px'
- }
+ 'margin': '5px',
+ },
+ width: 50,
+ height: 50,
+ containerHeight: 100,
+ containerWidth: 100
};
this._dataInfoComponent.registerComponent(modelBuilder);
@@ -70,8 +76,12 @@ export class ModelImportLocationPage extends ModelViewBase implements IPageView,
}
private async onTableSelected(): Promise {
+ let importTableIsValid = false;
if (this.tableSelectionComponent?.data) {
this.importTable = this.tableSelectionComponent?.data;
+ if (this.tableSelectionComponent !== undefined && this.tableSelectionComponent.isDataValid) {
+ importTableIsValid = true;
+ }
}
if (this.importTable && this._dataInfoComponent) {
@@ -81,7 +91,7 @@ export class ModelImportLocationPage extends ModelViewBase implements IPageView,
// Since Table name is picked last as per new flow this hasn't been set yet.
this.modelsViewData?.forEach(x => x.targetImportTable = this.importTable);
- if (!this.validateImportTableName()) {
+ if (!importTableIsValid) {
this._dataInfoComponent.title = constants.selectModelsTableMessage;
this._dataInfoComponent.iconSettings.path = 'noicon';
} else {
@@ -99,11 +109,6 @@ export class ModelImportLocationPage extends ModelViewBase implements IPageView,
}
}
- private validateImportTableName(): boolean {
- return this.importTable?.databaseName !== undefined && this.importTable?.databaseName !== constants.selectDatabaseTitle
- && this.importTable?.tableName !== undefined && this.importTable?.tableName !== constants.selectTableTitle;
- }
-
/**
* Returns selected data
*/
@@ -144,7 +149,7 @@ export class ModelImportLocationPage extends ModelViewBase implements IPageView,
public async validate(): Promise {
let validated = false;
- if (this.data && this.validateImportTableName()) {
+ if (this.data && this.tableSelectionComponent !== undefined && this.tableSelectionComponent.isDataValid) {
validated = true;
validated = await this.verifyImportConfigTable(this.data);
if (!validated) {
diff --git a/extensions/machine-learning/src/views/models/prediction/columnsSelectionPage.ts b/extensions/machine-learning/src/views/models/prediction/columnsSelectionPage.ts
index abb2c899e7..7226536213 100644
--- a/extensions/machine-learning/src/views/models/prediction/columnsSelectionPage.ts
+++ b/extensions/machine-learning/src/views/models/prediction/columnsSelectionPage.ts
@@ -77,7 +77,7 @@ export class ColumnsSelectionPage extends ModelViewBase implements IPageView, ID
public async validate(): Promise {
const data = this.data;
const validated = data !== undefined && data.databaseName !== undefined && data.inputColumns !== undefined && data.outputColumns !== undefined
- && data.tableName !== undefined && data.databaseName !== constants.selectDatabaseTitle && data.tableName !== constants.selectTableTitle
+ && data.tableName !== undefined && this.inputColumnsComponent !== undefined && this.inputColumnsComponent?.isDataValue
&& !data.inputColumns.find(x => (x.columnName === constants.selectColumnTitle) || !x.columnName);
if (!validated) {
this.showErrorMessage(constants.invalidModelParametersError);
diff --git a/extensions/machine-learning/src/views/models/prediction/columnsTable.ts b/extensions/machine-learning/src/views/models/prediction/columnsTable.ts
index bf359f9970..f49215b22f 100644
--- a/extensions/machine-learning/src/views/models/prediction/columnsTable.ts
+++ b/extensions/machine-learning/src/views/models/prediction/columnsTable.ts
@@ -162,7 +162,7 @@ export class ColumnsTable extends ModelViewBase implements IDataComponent | undefined;
/**
* Creates a new view
@@ -42,7 +43,10 @@ export class InputColumnsComponent extends ModelViewBase implements IDataCompone
databaseTitle: constants.columnDatabase,
tableTitle: constants.columnTable,
databaseInfo: constants.columnDatabaseInfo,
- tableInfo: constants.columnTableInfo
+ tableInfo: constants.columnTableInfo,
+ defaultDbName: constants.selectDatabaseTitle,
+ defaultTableName: constants.selectTableTitle,
+ useImportModelCache: false
});
this._tableSelectionComponent.registerComponent(modelBuilder);
this._tableSelectionComponent.onSelectedChanged(async () => {
@@ -99,6 +103,10 @@ export class InputColumnsComponent extends ModelViewBase implements IDataCompone
});
}
+ public get isDataValue(): boolean {
+ return this._tableSelectionComponent !== undefined && this._tableSelectionComponent?.isDataValid;
+ }
+
/**
* loads data in the components
*/
@@ -132,11 +140,21 @@ export class InputColumnsComponent extends ModelViewBase implements IDataCompone
}
private async onTableSelected(): Promise {
- await this.loadWithTable(this.databaseTable);
+ if (this._tableSelectionComponent !== undefined && this._tableSelectionComponent.isDataValid) {
+ await this.loadWithTable(this.databaseTable);
+ }
}
public async loadWithTable(table: DatabaseTable): Promise {
- await this._columns?.loadInputs(this._modelParameters, table);
+ if (this._tableLoadingPromise) {
+ try {
+ await this._tableLoadingPromise;
+ } catch {
+ }
+ }
+ this._tableLoadingPromise = this._columns?.loadInputs(this._modelParameters, table);
+ await this._tableLoadingPromise;
+ this._tableLoadingPromise = undefined;
}
private get databaseTable(): DatabaseTable {
diff --git a/extensions/machine-learning/src/views/models/tableSelectionComponent.ts b/extensions/machine-learning/src/views/models/tableSelectionComponent.ts
index a95dd66c25..e8532d7a58 100644
--- a/extensions/machine-learning/src/views/models/tableSelectionComponent.ts
+++ b/extensions/machine-learning/src/views/models/tableSelectionComponent.ts
@@ -9,7 +9,6 @@ import { ModelViewBase } from './modelViewBase';
import { ApiWrapper } from '../../common/apiWrapper';
import { IDataComponent } from '../interfaces';
import { DatabaseTable } from '../../prediction/interfaces';
-import * as constants from '../../common/constants';
export interface ITableSelectionSettings {
editable: boolean,
@@ -17,7 +16,11 @@ export interface ITableSelectionSettings {
databaseTitle: string,
tableTitle: string,
databaseInfo: string,
- tableInfo: string
+ tableInfo: string,
+ layout?: string,
+ defaultTableName: string;
+ defaultDbName: string;
+ useImportModelCache: boolean
}
/**
* View to render filters to pick an azure resource
@@ -53,10 +56,10 @@ export class TableSelectionComponent extends ModelViewBase implements IDataCompo
*/
public registerComponent(modelBuilder: azdata.ModelBuilder): azdata.Component {
this._databases = modelBuilder.dropDown().withProperties({
- width: this.componentMaxLength,
+ width: '221px'
}).component();
this._tables = modelBuilder.dropDown().withProperties({
- width: this.componentMaxLength - 10,
+ width: '221px'
}).component();
this._databases.onValueChanged(async () => {
@@ -142,10 +145,10 @@ export class TableSelectionComponent extends ModelViewBase implements IDataCompo
], {
flex: '0 0 auto',
CSSStyles: {
- 'align-items': 'flex-start'
+ 'align-items': 'flex-start',
}
}).withLayout({
- flexFlow: this._settings.editable ? 'column' : 'row',
+ flexFlow: this._settings.layout === 'horizontal' ? 'row' : 'column',
justifyContent: 'space-between',
width: this.tableMaxLength
}).component();
@@ -190,14 +193,32 @@ export class TableSelectionComponent extends ModelViewBase implements IDataCompo
return this.databaseTable;
}
+ /**
+ * Returns selected data
+ */
+ public get defaultDbNameIsSelected(): boolean {
+ return this.data === undefined || this.data.databaseName === this._settings.defaultDbName;
+ }
+
+ /**
+ * Returns selected data
+ */
+ public get defaultTableNameIsSelected(): boolean {
+ return this.data === undefined || this.data.tableName === this._settings.defaultTableName;
+ }
+
+ public get isDataValid(): boolean {
+ return this.data !== undefined && this.data.databaseName !== this._settings.defaultDbName && this.data.tableName !== this._settings.defaultTableName;
+ }
+
/**
* loads data in the components
*/
public async loadData(): Promise {
this._dbNames = await this.listDatabaseNames();
let dbNames = this._dbNames;
- if (!this._dbNames.find(x => x === constants.selectDatabaseTitle)) {
- dbNames = [constants.selectDatabaseTitle].concat(this._dbNames);
+ if (!this._dbNames.find(x => x === this._settings.defaultDbName)) {
+ dbNames = [this._settings.defaultDbName].concat(this._dbNames);
}
if (this._databases && dbNames && dbNames.length > 0) {
this._databases.values = dbNames;
@@ -230,14 +251,14 @@ export class TableSelectionComponent extends ModelViewBase implements IDataCompo
this.refreshTableComponent();
- if (this._tableNames && !this._tableNames.find(x => x.tableName === constants.selectTableTitle)) {
- const firstRow: DatabaseTable = { tableName: constants.selectTableTitle, databaseName: '', schema: '' };
+ if (this._tableNames && !this._tableNames.find(x => x.tableName === this._settings.defaultTableName)) {
+ const firstRow: DatabaseTable = { tableName: this._settings.defaultTableName, 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 && this.importTable.databaseName === this._databases?.value) {
+ if (this._settings.useImportModelCache && this.importTable && this.importTable.databaseName === this._databases?.value) {
const selectedTable = tableNames.find(t => t.tableName === this.importTable?.tableName && t.schema === this.importTable?.schema);
if (selectedTable) {
this._selectedTableName = this.getTableFullName(selectedTable);
@@ -266,7 +287,7 @@ export class TableSelectionComponent extends ModelViewBase implements IDataCompo
}
private getTableFullName(table: DatabaseTable): string {
- return table.tableName === constants.selectTableTitle ? table.tableName : `${table.schema}.${table.tableName}`;
+ return table.tableName === this._settings.defaultTableName ? table.tableName : `${table.schema}.${table.tableName}`;
}
private async onTableSelected(): Promise {