mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-08 01:28:26 -05:00
ML extension - View models styles / layout updates (#13091)
* Revised styles and added elements to view models stage of the View and import models. * Implemented Leilas DataInfoComponent, replacing my use of icon and title, description fields. Corrected some styles. * Fixed the issue with icon title and description * Fixed severla issues * Added method to output localized text for number of models shown. Added component to display models shown text. * Fixed the issues with order of components * Fixed showing number of models Co-authored-by: llali <llali@microsoft.com>
This commit is contained in:
@@ -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' };
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -132,11 +132,13 @@ export class AzureModelsTable extends ModelViewBase implements IDataComponent<Wo
|
||||
|
||||
if (this.isTableEmpty) {
|
||||
this._table.dataValues = [];
|
||||
this._table.data = [];
|
||||
} else {
|
||||
this._table.data = tableData;
|
||||
}
|
||||
} else {
|
||||
this._table.dataValues = [];
|
||||
this._table.data = [];
|
||||
}
|
||||
}
|
||||
this._onModelSelectionChanged.fire();
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import * as constants from '../../../common/constants';
|
||||
import { ModelViewBase } from '../modelViewBase';
|
||||
import { DataInfoComponent } from '../../dataInfoComponent';
|
||||
import { ModelActionType, ModelViewBase } from '../modelViewBase';
|
||||
import { CurrentModelsTable } from './currentModelsTable';
|
||||
import { ApiWrapper } from '../../../common/apiWrapper';
|
||||
import { IPageView, IComponentSettings } from '../../interfaces';
|
||||
@@ -17,11 +18,15 @@ import { ImportedModel } from '../../../modelManagement/interfaces';
|
||||
* View to render current registered models
|
||||
*/
|
||||
export class CurrentModelsComponent extends ModelViewBase implements IPageView {
|
||||
private _emptyModelsComponent: DataInfoComponent | undefined;
|
||||
private _dataTable: CurrentModelsTable | undefined;
|
||||
private _loader: azdata.LoadingComponent | undefined;
|
||||
private _tableSelectionComponent: TableSelectionComponent | undefined;
|
||||
private _labelComponent: azdata.TextComponent | undefined;
|
||||
private _descriptionComponent: azdata.TextComponent | undefined;
|
||||
private _tableDataCountContainer: azdata.FlexContainer | undefined;
|
||||
private _tableDataCountComponent: azdata.TextComponent | undefined;
|
||||
private _subheadingContainer: azdata.FlexContainer | undefined;
|
||||
private _subheadingTextComponent: azdata.TextComponent | undefined;
|
||||
private _subheadingLinkComponent: azdata.HyperlinkComponent | undefined;
|
||||
private _labelContainer: azdata.FlexContainer | undefined;
|
||||
private _formBuilder: azdata.FormBuilder | undefined;
|
||||
|
||||
@@ -45,7 +50,11 @@ export class CurrentModelsComponent extends ModelViewBase implements IPageView {
|
||||
databaseTitle: constants.databaseName,
|
||||
tableTitle: constants.tableName,
|
||||
databaseInfo: constants.modelDatabaseInfo,
|
||||
tableInfo: constants.modelTableInfo
|
||||
tableInfo: constants.modelTableInfo,
|
||||
layout: 'vertical',
|
||||
defaultDbName: constants.selectModelDatabaseTitle,
|
||||
defaultTableName: constants.selectModelTableTitle,
|
||||
useImportModelCache: true
|
||||
});
|
||||
this._tableSelectionComponent.registerComponent(modelBuilder);
|
||||
this._tableSelectionComponent.onSelectedChanged(async () => {
|
||||
@@ -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(<azdata.CheckBoxProperties>{
|
||||
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<void> {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class CurrentModelsTable extends ModelViewBase implements IDataComponent<
|
||||
private _downloadedFile: ModelArtifact | undefined;
|
||||
private _onModelSelectionChanged: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
|
||||
public readonly onModelSelectionChanged: vscode.Event<void> = 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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<void> {
|
||||
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<boolean> {
|
||||
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) {
|
||||
|
||||
@@ -77,7 +77,7 @@ export class ColumnsSelectionPage extends ModelViewBase implements IPageView, ID
|
||||
public async validate(): Promise<boolean> {
|
||||
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);
|
||||
|
||||
@@ -162,7 +162,7 @@ export class ColumnsTable extends ModelViewBase implements IDataComponent<Predic
|
||||
this._parameters = [];
|
||||
let tableData: any[][] = [];
|
||||
|
||||
if (this._table && table && table.tableName !== constants.selectTableTitle) {
|
||||
if (this._table && table) {
|
||||
if (this._forInput) {
|
||||
let columns: TableColumn[];
|
||||
try {
|
||||
|
||||
@@ -22,6 +22,7 @@ export class InputColumnsComponent extends ModelViewBase implements IDataCompone
|
||||
private _tableSelectionComponent: TableSelectionComponent | undefined;
|
||||
private _columns: ColumnsTable | undefined;
|
||||
private _modelParameters: ModelParameters | undefined;
|
||||
private _tableLoadingPromise: Promise<void> | 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<void> {
|
||||
await this.loadWithTable(this.databaseTable);
|
||||
if (this._tableSelectionComponent !== undefined && this._tableSelectionComponent.isDataValid) {
|
||||
await this.loadWithTable(this.databaseTable);
|
||||
}
|
||||
}
|
||||
|
||||
public async loadWithTable(table: DatabaseTable): Promise<void> {
|
||||
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 {
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
|
||||
Reference in New Issue
Block a user