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:
Hale Rankin
2020-11-04 12:07:54 -08:00
committed by GitHub
parent 06a4b0d1a2
commit 4af67c9734
13 changed files with 286 additions and 105 deletions

View File

@@ -0,0 +1,30 @@
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M122.755 48.0749C120.066 45.3853 116.584 43.6294 112.823 43.0652C109.061 42.5009 105.218 43.158 101.858 44.94L93.746 32.559C94.2713 32.1476 94.7733 31.7072 95.2496 31.2399C97.5707 28.9172 99.2027 25.9967 99.9645 22.8025C100.726 19.6084 100.588 16.2657 99.5647 13.1454C98.5416 10.0252 96.6738 7.2495 94.1686 5.12655C91.6635 3.00361 88.619 1.61645 85.3732 1.11907C82.1274 0.621688 78.8073 1.03355 75.7814 2.30894C72.7555 3.58432 70.1422 5.67333 68.2317 8.34404C66.3212 11.0148 65.1882 14.1627 64.9585 17.4383C64.7288 20.714 65.4114 23.9892 66.9305 26.9004L44.5997 41.3172L46.3584 44.0607L68.6936 29.6395C71.2657 32.9265 74.9145 35.2012 78.9982 36.0633C83.0819 36.9253 87.3388 36.3197 91.02 34.3528L99.1363 46.7383C95.5114 49.6155 93.154 53.795 92.5667 58.3855C91.9795 62.976 93.2088 67.6144 95.9927 71.3114L68.9662 95.8054C65.8733 92.9442 61.8643 91.2752 57.6547 91.0964C53.4451 90.9176 49.309 92.2406 45.9847 94.8293L24.9553 72.4326L22.5723 74.6705L43.6017 97.0673C41.3751 99.5816 39.9068 102.675 39.367 105.99C38.8271 109.305 39.2378 112.705 40.5515 115.796C41.8651 118.887 44.0279 121.542 46.7891 123.454C49.5503 125.365 52.7968 126.455 56.1523 126.597C59.5077 126.739 62.8346 125.927 65.7474 124.255C68.6601 122.584 71.0394 120.12 72.6093 117.152C74.1793 114.183 74.8756 110.83 74.6175 107.481C74.3595 104.132 73.1576 100.926 71.1513 98.2324L98.1866 73.734C101.608 76.8893 106.13 78.5764 110.782 78.4326C115.433 78.2888 119.843 76.3256 123.062 72.965C126.282 69.6044 128.054 65.1148 127.998 60.4612C127.943 55.8077 126.063 51.3618 122.764 48.0793L122.755 48.0749Z" fill="#808080" fill-opacity="0.25"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.4335 82.216H0V26.8L17.4329 10.4883H55.4335V82.216Z" fill="#F2F2F2"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.4329 26.8H0L17.4329 10.4883V26.8Z" fill="#808080" fill-opacity="0.15"/>
<path d="M47.5068 33.623H7.92773V37.435H47.5068V33.623Z" fill="#808080" fill-opacity="0.15"/>
<path d="M47.5068 40.7637H7.92773V44.5756H47.5068V40.7637Z" fill="#808080" fill-opacity="0.15"/>
<path d="M47.5068 47.9043H7.92773V51.7162H47.5068V47.9043Z" fill="#808080" fill-opacity="0.15"/>
<path d="M47.5068 55.043H7.92773V58.8549H47.5068V55.043Z" fill="#808080" fill-opacity="0.15"/>
<path d="M47.5068 61.8809H7.92773V65.6928H47.5068V61.8809Z" fill="#808080" fill-opacity="0.15"/>
<path d="M56.7433 56.0593C63.6818 49.1208 63.6818 37.8714 56.7433 30.9329C49.8048 23.9944 38.5554 23.9944 31.6169 30.9329C24.6784 37.8714 24.6784 49.1208 31.6169 56.0593C38.5554 62.9977 49.8048 62.9977 56.7433 56.0593Z" fill="#E6E6E6"/>
<path d="M57.0739 50.7266H27.9502C28.8351 52.7128 30.0781 54.5191 31.617 56.0554L31.7138 56.1477H56.6474L56.7441 56.0554C56.8585 55.941 56.964 55.8223 57.0739 55.708V50.7266Z" fill="#D2D2D2"/>
<path d="M57.0739 36.2571V31.2756C56.964 31.1613 56.8585 31.0426 56.7441 30.9283L56.6474 30.8359H31.7138L31.617 30.9283C30.0781 32.4645 28.8351 34.2708 27.9502 36.2571H57.0739Z" fill="#D2D2D2"/>
<path d="M57.0729 40.9961H26.5862C26.3338 42.7962 26.3575 44.6243 26.6566 46.4172H57.0729V40.9961Z" fill="#D2D2D2"/>
<path d="M66.7375 62.8126L60.0062 56.0812C63.2213 52.0385 64.7678 46.9189 64.3285 41.7723C63.8892 36.6257 61.4974 31.8422 57.6436 28.4029C53.7898 24.9636 48.7662 23.1291 43.603 23.2757C38.4397 23.4223 33.5283 25.5389 29.8759 29.1913C26.2234 32.8437 24.1069 37.7552 23.9603 42.9184C23.8137 48.0816 25.6482 53.1053 29.0875 56.959C32.5268 60.8128 37.3103 63.2046 42.4569 63.6439C47.6035 64.0832 52.7231 62.5367 56.7658 59.3216L63.4971 66.0529L66.7375 62.8126ZM32.8829 54.7974C30.6463 52.5612 29.123 49.7119 28.5057 46.6099C27.8885 43.5079 28.205 40.2925 29.4152 37.3704C30.6254 34.4483 32.675 31.9507 35.3047 30.1935C37.9345 28.4363 41.0262 27.4983 44.189 27.4983C47.3519 27.4983 50.4436 28.4363 53.0734 30.1935C55.7031 31.9507 57.7527 34.4483 58.9629 37.3704C60.1731 40.2925 60.4896 43.5079 59.8723 46.6099C59.2551 49.7119 57.7318 52.5612 55.4951 54.7974C54.0101 56.2833 52.2469 57.462 50.3062 58.2662C48.3655 59.0704 46.2854 59.4843 44.1846 59.4843C42.0839 59.4843 40.0038 59.0704 38.0631 58.2662C36.1224 57.462 34.3592 56.2833 32.8741 54.7974H32.8829Z" fill="#A6A6A6"/>
<path d="M78.5765 82.692L83.3798 77.8887C83.7919 77.4766 83.7919 76.8085 83.3798 76.3964L66.3087 59.3253C65.8966 58.9132 65.2285 58.9132 64.8164 59.3253L60.0131 64.1286C59.601 64.5407 59.601 65.2088 60.0131 65.6209L77.0842 82.692C77.4963 83.1041 78.1644 83.1041 78.5765 82.692Z" fill="#A6A6A6"/>
<path d="M41.9916 38.9331C43.6331 37.2916 43.6331 34.6303 41.9916 32.9888C40.3501 31.3473 37.6888 31.3473 36.0473 32.9888C34.4059 34.6303 34.4059 37.2916 36.0473 38.9331C37.6888 40.5745 40.3501 40.5745 41.9916 38.9331Z" fill="white"/>
<path d="M60.0152 64.1311L62.4158 61.7305L79.0661 78.3808C79.107 78.4226 79.1559 78.4559 79.2099 78.4786C79.2639 78.5013 79.3219 78.513 79.3804 78.513C79.439 78.513 79.497 78.5013 79.551 78.4786C79.605 78.4559 79.6539 78.4226 79.6948 78.3808L82.5263 75.5493L83.4276 76.4506C83.6112 76.6352 83.7143 76.8849 83.7143 77.1453C83.7143 77.4056 83.6112 77.6554 83.4276 77.84L78.5253 82.7423C78.3407 82.9259 78.091 83.029 77.8306 83.029C77.5702 83.029 77.3205 82.9259 77.1359 82.7423L60.0152 65.6215C59.8179 65.4237 59.707 65.1557 59.707 64.8763C59.707 64.5969 59.8179 64.3289 60.0152 64.1311Z" fill="#808080" fill-opacity="0.25"/>
<path d="M82.6671 18.6523L70.6201 23.1853C71.3799 25.1996 72.6326 26.9913 74.2636 28.3965C75.8946 29.8018 77.8518 30.7757 79.9564 31.2292C82.0609 31.6827 84.2456 31.6014 86.3106 30.9927C88.3756 30.384 90.255 29.2673 91.777 27.7447L82.6671 18.6523Z" fill="#A6A6A6"/>
<path d="M73.5614 9.5293C72.3638 10.7237 71.414 12.143 70.7667 13.7056C70.1193 15.2683 69.787 16.9434 69.7891 18.6348H82.667L73.5614 9.5293Z" fill="#808080" fill-opacity="0.25"/>
<path d="M69.7891 18.6523C69.7891 20.2008 70.0691 21.7365 70.6156 23.1853L82.667 18.6523H69.7891Z" fill="#F2F2F2"/>
<path d="M91.7726 27.7446C94.1455 25.321 95.4665 22.0591 95.4485 18.6673C95.4306 15.2755 94.0753 12.0277 91.6769 9.62928C89.2785 7.23087 86.0307 5.87553 82.6389 5.8576C79.2471 5.83967 75.9851 7.1606 73.5615 9.53351L82.6671 18.6523L91.7726 27.7446Z" fill="#F2F2F2"/>
<path d="M105.415 62.5938H100.711V69.114H105.415V62.5938Z" fill="#A6A6A6"/>
<path d="M112.542 55.3125H107.838V69.1137H112.542V55.3125Z" fill="#A6A6A6"/>
<path d="M119.673 49.332H114.969V69.1128H119.673V49.332Z" fill="#A6A6A6"/>
<path d="M67.0313 113.93H47.0527V117.051H67.0313V113.93Z" fill="#A6A6A6"/>
<path d="M67.0313 109.832H47.0527V112.941H67.0313V109.832Z" fill="#A6A6A6"/>
<path d="M67.0313 101.641H47.0527V104.762H67.0313V101.641Z" fill="#A6A6A6"/>
<path d="M67.0313 105.738H47.0527V108.847H67.0313V105.738Z" fill="#A6A6A6"/>
<path d="M65.7255 104.986L61.6058 106.09L62.6126 107.092L60.0098 109.7L57.0728 106.763L52.0738 111.766L49.7215 109.414L47.0527 112.087V114.101L49.7215 111.432L52.0738 113.78L57.0728 108.781L60.0098 111.713L63.6195 108.104L64.6219 109.106L65.7255 104.986Z" fill="#F2F2F2"/>
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -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' };

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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> {