mirror of
https://github.com/clearml/clearml-web
synced 2025-06-26 18:27:02 +00:00
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@
|
||||
/out-tsc
|
||||
/gen-code
|
||||
/src/__ngcc_entry_points__.json
|
||||
.angular/
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"assets": [
|
||||
"src/assets",
|
||||
"src/favicon.ico",
|
||||
"src/env.js",
|
||||
"src/app/webapp-common/assets",
|
||||
{
|
||||
"glob": "**/*",
|
||||
@@ -54,8 +55,9 @@
|
||||
"fast-xml-parser",
|
||||
"url",
|
||||
"@aws-crypto/sha256-browser",
|
||||
"@aws-crypto/crc32"
|
||||
|
||||
"@aws-crypto/crc32",
|
||||
"@aws-crypto/sha1-browser",
|
||||
"@aws-crypto/crc32c"
|
||||
],
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
|
||||
4943
package-lock.json
generated
4943
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
69
package.json
69
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ClearML-webapp",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"license": "",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
@@ -20,26 +20,27 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^13.1.1",
|
||||
"@angular/cdk": "^13.1.1",
|
||||
"@angular/common": "^13.1.1",
|
||||
"@angular/compiler": "^13.1.1",
|
||||
"@angular/core": "^13.1.1",
|
||||
"@angular/forms": "^13.1.1",
|
||||
"@angular/material": "^13.1.1",
|
||||
"@angular/platform-browser": "^13.1.1",
|
||||
"@angular/platform-browser-dynamic": "^13.1.1",
|
||||
"@angular/platform-server": "^13.1.1",
|
||||
"@angular/router": "^13.1.1",
|
||||
"@angular/service-worker": "^13.1.1",
|
||||
"@aws-sdk/client-s3": "^3.45.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.45.0",
|
||||
"@angular/animations": "^13.2.2",
|
||||
"@angular/cdk": "^13.2.2",
|
||||
"@angular/common": "^13.2.2",
|
||||
"@angular/compiler": "^13.2.2",
|
||||
"@angular/core": "^13.2.2",
|
||||
"@angular/forms": "^13.2.2",
|
||||
"@angular/material": "^13.2.2",
|
||||
"@angular/platform-browser": "^13.2.2",
|
||||
"@angular/platform-browser-dynamic": "^13.2.2",
|
||||
"@angular/platform-server": "^13.2.2",
|
||||
"@angular/router": "^13.2.2",
|
||||
"@angular/service-worker": "^13.2.2",
|
||||
"@angular/youtube-player": "^13.2.2",
|
||||
"@aws-sdk/client-s3": "^3.53.1",
|
||||
"@aws-sdk/s3-request-presigner": "^3.53.1",
|
||||
"@ngneat/dag": "^1.1.0",
|
||||
"@ngrx/effects": "^13.0.2",
|
||||
"@ngrx/entity": "^13.0.2",
|
||||
"@ngrx/router-store": "^13.0.2",
|
||||
"@ngrx/store": "^13.0.2",
|
||||
"ace-builds": "^1.4.13",
|
||||
"ace-builds": "^1.4.14",
|
||||
"angular-google-tag-manager": "^1.5.0",
|
||||
"angular-resizable-element": "^5.0.0",
|
||||
"angular-split": "^13.1.0",
|
||||
@@ -49,7 +50,7 @@
|
||||
"curved-arrows": "^0.1.0",
|
||||
"d3-selection": "^1.4.2",
|
||||
"diff": "^5.0.0",
|
||||
"filesize": "^8.0.6",
|
||||
"filesize": "^8.0.7",
|
||||
"has-ansi": "^5.0.1",
|
||||
"hocon-parser": "^1.0.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
@@ -63,7 +64,7 @@
|
||||
"primeicons": "^5.0.0",
|
||||
"primeng": "^13.0.4",
|
||||
"process": "^0.11.10",
|
||||
"rxjs": "^7.5.1",
|
||||
"rxjs": "^7.5.5",
|
||||
"string-to-color": "^2.2.2",
|
||||
"tslib": "^2.3.1",
|
||||
"url": "^0.11.0",
|
||||
@@ -71,19 +72,19 @@
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^13.1.2",
|
||||
"@angular-devkit/core": "^13.1.2",
|
||||
"@angular-devkit/schematics": "^13.1.2",
|
||||
"@angular-devkit/schematics-cli": "^13.1.2",
|
||||
"@angular-eslint/builder": "^13.0.1",
|
||||
"@angular-eslint/eslint-plugin": "^13.0.1",
|
||||
"@angular-eslint/eslint-plugin-template": "^13.0.1",
|
||||
"@angular-eslint/schematics": "13.0.1",
|
||||
"@angular-eslint/template-parser": "^13.0.1",
|
||||
"@angular/cli": "^13.1.2",
|
||||
"@angular/compiler-cli": "^13.1.1",
|
||||
"@angular/language-service": "^13.1.1",
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"@angular-devkit/build-angular": "^13.2.3",
|
||||
"@angular-devkit/core": "^13.2.3",
|
||||
"@angular-devkit/schematics": "^13.2.3",
|
||||
"@angular-devkit/schematics-cli": "^13.2.3",
|
||||
"@angular-eslint/builder": "^13.1.0",
|
||||
"@angular-eslint/eslint-plugin": "^13.1.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^13.1.0",
|
||||
"@angular-eslint/schematics": "13.1.0",
|
||||
"@angular-eslint/template-parser": "^13.1.0",
|
||||
"@angular/cli": "^13.2.3",
|
||||
"@angular/compiler-cli": "^13.2.2",
|
||||
"@angular/language-service": "^13.2.2",
|
||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||
"@ngrx/schematics": "^13.0.2",
|
||||
"@ngrx/store-devtools": "^13.0.2",
|
||||
"@types/d3-selection": "^1.4.3",
|
||||
@@ -94,10 +95,10 @@
|
||||
"@typescript-eslint/eslint-plugin": "5.9.0",
|
||||
"@typescript-eslint/parser": "5.9.0",
|
||||
"codelyzer": "^6.0.2",
|
||||
"eslint": "^8.6.0",
|
||||
"eslint": "^8.9.0",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eslint-plugin-jsdoc": "37.5.1",
|
||||
"eslint-plugin-jsdoc": "37.9.1",
|
||||
"eslint-plugin-prefer-arrow": "1.2.3",
|
||||
"typescript": "^4.5.4"
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
loadExternalLibrary(this.store, this.environment.plotlyURL, plotlyReady);
|
||||
loadExternalLibrary(this.store, '/assets/ace-builds/ace.js', aceReady);
|
||||
loadExternalLibrary(this.store, 'assets/ace-builds/ace.js', aceReady);
|
||||
}
|
||||
|
||||
private setScale() {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {Action} from '@ngrx/store';
|
||||
import {ConfigurationService} from './webapp-common/shared/services/configuration.service';
|
||||
const environment = ConfigurationService.globalEnvironment;
|
||||
import {Environment} from '../environments/base';
|
||||
|
||||
export const NA = 'N/A';
|
||||
export const ALLEGRO_TUTORIAL_BUCKET = 'allegro-tutorials';
|
||||
@@ -62,7 +61,7 @@ export const RECENT_TASKS_ACTIONS = {
|
||||
|
||||
export const VIEW_PREFIX = 'VIEW_';
|
||||
|
||||
export type MediaContentTypeEnum = 'image/bmp' | 'image/jpeg' | 'image/png' | 'video/mp4' | 'image/jpeg';
|
||||
export type MediaContentTypeEnum = 'image/bmp' | 'image/jpeg' | 'image/png' | 'video/mp4';
|
||||
|
||||
export const MEDIA_VIDEO_EXTENSIONS = ['flv', 'avi', 'mp4', 'mov', 'mpg', 'wmv', '3gp', 'mkv'];
|
||||
|
||||
@@ -94,7 +93,7 @@ export const NAVIGATION_ACTIONS = {
|
||||
};
|
||||
|
||||
|
||||
export function guessAPIServerURL() {
|
||||
export const guessAPIServerURL = () => {
|
||||
const url = window.location.origin;
|
||||
if (/https?:\/\/(demo|)app\./.test(url)) {
|
||||
return url.replace(/(https?):\/\/(demo|)app/, '$1://$2api');
|
||||
@@ -102,39 +101,48 @@ export function guessAPIServerURL() {
|
||||
return url.replace(/:\d+/, '') + ':30008';
|
||||
}
|
||||
return url.replace(/:\d+/, '') + ':8008';
|
||||
}
|
||||
};
|
||||
|
||||
export const ENVIRONMENT = {API_VERSION: '/v999.0'};
|
||||
const url = window.location.origin;
|
||||
let apiBaseUrl: string;
|
||||
if (environment.apiBaseUrl) {
|
||||
apiBaseUrl = environment.apiBaseUrl;
|
||||
} else {
|
||||
apiBaseUrl = guessAPIServerURL();
|
||||
}
|
||||
const apiBaseUrlNoVersion = apiBaseUrl;
|
||||
|
||||
let fileBaseUrl;
|
||||
if (environment.fileBaseUrl) {
|
||||
fileBaseUrl = environment.fileBaseUrl;
|
||||
} else if (/https?:\/\/(demo|)app\./.test(url)) {
|
||||
fileBaseUrl = url.replace(/(https?):\/\/(demo|)app/, '$1://$2files');
|
||||
} else if (window.location.port === '30080') {
|
||||
fileBaseUrl = url.replace(/:\d+/, '') + ':30081';
|
||||
} else if (window.location.port === '8080') {
|
||||
fileBaseUrl = url.replace(/:\d+/, '') + ':8081';
|
||||
}
|
||||
export let HTTP = {
|
||||
API_BASE_URL: '',
|
||||
API_BASE_URL_NO_VERSION : '',
|
||||
FILE_BASE_URL: '',
|
||||
ALT_FILES: null
|
||||
};
|
||||
|
||||
export const updateHttpUrlBaseConstant = (_environment: Environment) => {
|
||||
|
||||
let apiBaseUrl: string;
|
||||
if (_environment.apiBaseUrl) {
|
||||
apiBaseUrl = _environment.apiBaseUrl;
|
||||
} else {
|
||||
apiBaseUrl = guessAPIServerURL();
|
||||
}
|
||||
const apiBaseUrlNoVersion = apiBaseUrl;
|
||||
apiBaseUrl += ENVIRONMENT.API_VERSION;
|
||||
|
||||
const url = window.location.origin;
|
||||
let fileBaseUrl;
|
||||
if (_environment.fileBaseUrl) {
|
||||
fileBaseUrl = _environment.fileBaseUrl;
|
||||
} else if (/https?:\/\/(demo|)app\./.test(url)) {
|
||||
fileBaseUrl = url.replace(/(https?):\/\/(demo|)app/, '$1://$2files');
|
||||
} else if (window.location.port === '30080') {
|
||||
fileBaseUrl = url.replace(/:\d+/, '') + ':30081';
|
||||
} else if (window.location.port === '8080') {
|
||||
fileBaseUrl = url.replace(/:\d+/, '') + ':8081';
|
||||
}
|
||||
|
||||
HTTP.API_BASE_URL = apiBaseUrl; // <-- DIRECT CALL DOESN'T WORK
|
||||
HTTP.API_BASE_URL_NO_VERSION = apiBaseUrlNoVersion;
|
||||
HTTP.FILE_BASE_URL = fileBaseUrl;
|
||||
};
|
||||
|
||||
apiBaseUrl += ENVIRONMENT.API_VERSION;
|
||||
|
||||
export const HTTP_PREFIX = 'HTTP_';
|
||||
|
||||
export const HTTP = {
|
||||
API_BASE_URL : apiBaseUrl, // <-- DIRECT CALL DOESN'T WORK
|
||||
API_BASE_URL_NO_VERSION: apiBaseUrlNoVersion,
|
||||
FILE_BASE_URL: fileBaseUrl,
|
||||
};
|
||||
|
||||
export class EmptyAction implements Action {
|
||||
readonly type = 'EMPTY_ACTION';
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import {ConfigurationService} from '@common/shared/services/configuration.servic
|
||||
import {ProjectsSharedModule} from './features/projects/shared/projects-shared.module';
|
||||
import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field';
|
||||
import {LoginService} from '~/shared/services/login.service';
|
||||
import {SettingsModule} from '~/features/settings/settings.module';
|
||||
|
||||
@NgModule({
|
||||
declarations : [AppComponent],
|
||||
@@ -42,7 +41,6 @@ import {SettingsModule} from '~/features/settings/settings.module';
|
||||
onSameUrlNavigation: 'reload',
|
||||
relativeLinkResolution: 'legacy'
|
||||
}),
|
||||
SettingsModule,
|
||||
NotifierModule.withConfig({
|
||||
theme: 'material',
|
||||
behaviour: {
|
||||
@@ -74,7 +72,7 @@ import {SettingsModule} from '~/features/settings/settings.module';
|
||||
deps: [ConfigurationService],
|
||||
useFactory: (confService: ConfigurationService) =>
|
||||
confService.getStaticEnvironment().GTM_ID
|
||||
}
|
||||
},
|
||||
],
|
||||
bootstrap : [AppComponent],
|
||||
exports : []
|
||||
|
||||
@@ -7,7 +7,6 @@ import {ProjectRedirectGuardGuard} from '@common/shared/guards/project-redirect.
|
||||
|
||||
export const routes: Routes = [
|
||||
{path: '', redirectTo: 'dashboard', pathMatch: 'full'},
|
||||
{path: 'admin', redirectTo: 'settings', pathMatch: 'full'},
|
||||
{
|
||||
path: 'dashboard',
|
||||
loadChildren: () => import('./features/dashboard/dashboard.module').then(m => m.DashboardModule),
|
||||
|
||||
@@ -63,6 +63,8 @@ import {ProjectsGetModelMetadataKeysResponse} from '~/business-logic/model/proje
|
||||
import {ProjectsGetModelMetadataKeysRequest} from '~/business-logic/model/projects/projectsGetModelMetadataKeysRequest';
|
||||
import {ProjectsGetProjectTagsResponse} from '~/business-logic/model/projects/projectsGetProjectTagsResponse';
|
||||
import {ProjectsGetProjectTagsRequest} from '~/business-logic/model/projects/projectsGetProjectTagsRequest';
|
||||
import {ProjectsGetModelMetadataValuesRequest} from '~/business-logic/model/projects/projectsGetModelMetadataValuesRequest';
|
||||
import {ProjectsGetModelMetadataValuesResponse} from '~/business-logic/model/projects/projectsGetModelMetadataValuesResponse';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@@ -98,7 +100,7 @@ export class ApiProjectsService {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Create a new project
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -143,7 +145,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Deletes a project
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -188,7 +190,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get all the company\'s projects and all public projects
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -233,7 +235,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get all the company\'s projects and all public projects
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -278,8 +280,8 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
* @param reportProgress flag to report request and response progress.
|
||||
@@ -323,7 +325,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get a list of all hyper parameter sections and names used in tasks within the given project.
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -368,7 +370,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get a list of distinct values for the chosen hyperparameter
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -458,12 +460,57 @@ export class ApiProjectsService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Get a list of distinct values for the chosen model metadata key
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
* @param reportProgress flag to report request and response progress.
|
||||
*/
|
||||
public projectsGetModelMetadataValues(request: ProjectsGetModelMetadataValuesRequest, options?: any, observe: any = 'body', reportProgress: boolean = false ): Observable<any> {
|
||||
if (request === null || request === undefined) {
|
||||
throw new Error('Required parameter request was null or undefined when calling projectsGetModelMetadataValues.');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Get user and system tags used for the models under the specified projects
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
let headers = this.defaultHeaders;
|
||||
if (options && options.async_enable) {
|
||||
headers = headers.set(this.configuration.asyncHeader, '1');
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
const httpHeaderAccepts: string[] = [
|
||||
'application/json'
|
||||
];
|
||||
const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
|
||||
if (httpHeaderAcceptSelected != undefined) {
|
||||
headers = headers.set("Accept", httpHeaderAcceptSelected);
|
||||
}
|
||||
|
||||
// to determine the Content-Type header
|
||||
const consumes: string[] = [
|
||||
];
|
||||
const httpContentTypeSelected:string | undefined = this.configuration.selectHeaderContentType(consumes);
|
||||
if (httpContentTypeSelected != undefined) {
|
||||
headers = headers.set("Content-Type", httpContentTypeSelected);
|
||||
}
|
||||
|
||||
return this.apiRequest.post<ProjectsGetModelMetadataValuesResponse>(`${this.basePath}/projects.get_model_metadata_values`,
|
||||
request,
|
||||
{
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: headers,
|
||||
observe: observe,
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Get user and system tags used for the models under the specified projects
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
* @param reportProgress flag to report request and response progress.
|
||||
*/
|
||||
public projectsGetModelTags(request: ProjectsGetModelTagsRequest, options?: any, observe: any = 'body', reportProgress: boolean = false ): Observable<any> {
|
||||
@@ -550,7 +597,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get unique parent tasks for the tasks in the specified projects
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -595,7 +642,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get user and system tags used for the tasks under the specified projects
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -640,7 +687,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get all metric/variant pairs reported for tasks in a specific project. If no project is specified, metrics/variant paris reported for all tasks will be returned. If the project does not exist, an empty list will be returned.
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -685,7 +732,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Convert public projects to private
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -730,7 +777,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Convert company projects to public
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -775,7 +822,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Moves all the source project\'s contents to the destination project and remove the source project
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -820,7 +867,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Moves a project and all of its subprojects under the different location
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -865,7 +912,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Update project information
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -910,7 +957,7 @@ export class ApiProjectsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Validates that the project existis and can be deleted
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* projects
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* OpenAPI spec version: 999.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface ProjectsGetModelMetadataValuesRequest {
|
||||
/**
|
||||
* Project IDs
|
||||
*/
|
||||
projects?: Array<string>;
|
||||
/**
|
||||
* Metadata key
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* If set to \'true\' then collect values from both company and public models otherwise company modeels only. The default is \'true\'
|
||||
*/
|
||||
allow_public?: boolean;
|
||||
/**
|
||||
* If set to \'true\' and the project field is set then the result includes metadata values from the subproject models
|
||||
*/
|
||||
include_subprojects?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* projects
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* OpenAPI spec version: 999.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface ProjectsGetModelMetadataValuesResponse {
|
||||
/**
|
||||
* Total number of distinct values
|
||||
*/
|
||||
total?: number;
|
||||
/**
|
||||
* The list of the unique values
|
||||
*/
|
||||
values?: Array<string>;
|
||||
}
|
||||
@@ -2,17 +2,17 @@ import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {selectShowOnlyUserWork} from '../../webapp-common/core/reducers/users-reducer';
|
||||
import {GetCurrentUserResponseUserObjectCompany} from '../../business-logic/model/users/getCurrentUserResponseUserObjectCompany';
|
||||
import {selectShowOnlyUserWork} from '@common/core/reducers/users-reducer';
|
||||
import {GetCurrentUserResponseUserObjectCompany} from '~/business-logic/model/users/getCurrentUserResponseUserObjectCompany';
|
||||
import {filter, skip, take} from 'rxjs/operators';
|
||||
import {setDeep} from '../../webapp-common/core/actions/projects.actions';
|
||||
import {getRecentProjects, getRecentExperiments} from '../../webapp-common/dashboard/common-dashboard.actions';
|
||||
import {selectActiveSearch} from '../../webapp-common/common-search/common-search.reducer';
|
||||
import {selectFirstLogin} from '../../webapp-common/core/reducers/view.reducer';
|
||||
import {setDeep} from '@common/core/actions/projects.actions';
|
||||
import {getRecentProjects, getRecentExperiments} from '@common/dashboard/common-dashboard.actions';
|
||||
import {selectActiveSearch} from '@common/common-search/common-search.reducer';
|
||||
import {selectFirstLogin} from '@common/core/reducers/view.reducer';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {WelcomeMessageComponent} from '../../webapp-common/dashboard/dumb/welcome-message/welcome-message.component';
|
||||
import {firstLogin} from '../../webapp-common/core/actions/layout.actions';
|
||||
import {IRecentTask, selectRecentTasks} from '../../webapp-common/dashboard/common-dashboard.reducer';
|
||||
import {WelcomeMessageComponent} from '@common/layout/welcome-message/welcome-message.component';
|
||||
import {firstLogin} from '@common/core/actions/layout.actions';
|
||||
import {IRecentTask, selectRecentTasks} from '@common/dashboard/common-dashboard.reducer';
|
||||
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -4,18 +4,19 @@ import {Store} from '@ngrx/store';
|
||||
import {get} from 'lodash/fp';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {debounceTime, distinctUntilChanged, filter, map, tap} from 'rxjs/operators';
|
||||
import {MESSAGES_SEVERITY} from '../../../../app.constants';
|
||||
import {MESSAGES_SEVERITY} from '~/app.constants';
|
||||
import {IExperimentInfoState} from '../../reducers/experiment-info.reducer';
|
||||
import {selectExperimentInfoData, selectIsExperimentEditable, selectSelectedExperiment} from '../../reducers';
|
||||
import {selectBackdropActive} from '../../../../webapp-common/core/reducers/view.reducer';
|
||||
import {isReadOnly} from '../../../../webapp-common/shared/utils/shared-utils';
|
||||
import {selectRouterConfig, selectRouterParams, selectRouterQueryParams} from '../../../../webapp-common/core/reducers/router-reducer';
|
||||
import {selectBackdropActive} from '@common/core/reducers/view.reducer';
|
||||
import {isReadOnly} from '@common/shared/utils/shared-utils';
|
||||
import {selectRouterConfig, selectRouterParams, selectRouterQueryParams} from '@common/core/reducers/router-reducer';
|
||||
import * as commonInfoActions from '../../../../webapp-common/experiments/actions/common-experiments-info.actions';
|
||||
import {ExperimentDetailsUpdated} from '../../../../webapp-common/experiments/actions/common-experiments-info.actions';
|
||||
import {addMessage} from '../../../../webapp-common/core/actions/layout.actions';
|
||||
import {ExperimentDetailsUpdated} from '@common/experiments/actions/common-experiments-info.actions';
|
||||
import {addMessage} from '@common/core/actions/layout.actions';
|
||||
import {IExperimentInfo} from '../../shared/experiment-info.model';
|
||||
import {selectSelectedTableExperiment} from '../../../../webapp-common/experiments/reducers';
|
||||
import {ITableExperiment} from '../../../../webapp-common/experiments/shared/common-experiment-model.model';
|
||||
import {selectSelectedTableExperiment} from '@common/experiments/reducers';
|
||||
import {ITableExperiment} from '@common/experiments/shared/common-experiment-model.model';
|
||||
import {setTableMode} from '@common/experiments/actions/common-experiments-view.actions';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -97,7 +98,7 @@ export class ExperimentInfoComponent implements OnInit, OnDestroy {
|
||||
|
||||
updateExperimentName(name) {
|
||||
if (name.trim().length > 2) {
|
||||
this.store.dispatch(new ExperimentDetailsUpdated({id: this.selectedExperiment.id, changes: {name: name}}));
|
||||
this.store.dispatch(new ExperimentDetailsUpdated({id: this.selectedExperiment.id, changes: {name}}));
|
||||
} else {
|
||||
this.store.dispatch(addMessage(MESSAGES_SEVERITY.ERROR, 'Name must be more than three letters long'));
|
||||
}
|
||||
@@ -108,6 +109,7 @@ export class ExperimentInfoComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
navigateAfterExperimentSelectionChanged() {
|
||||
this.store.dispatch(setTableMode({mode: 'table'}));
|
||||
this.router.navigate([`projects/${this.projectId}/experiments`], {queryParamsHandling: 'merge'});
|
||||
}
|
||||
|
||||
|
||||
@@ -63,8 +63,6 @@
|
||||
<sm-refresh-button
|
||||
*ngIf=" ! minimized"
|
||||
class="light-theme"
|
||||
[autoRefreshState]="autoRefreshState$ | async"
|
||||
(refreshList)="refresh(false)"
|
||||
(setAutoRefresh)="setAutoRefresh($event)"
|
||||
>
|
||||
</sm-refresh-button>
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {InjectionToken, NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ExperimentSharedModule} from './shared/experiment-shared.module';
|
||||
import {SMSharedModule} from '../../webapp-common/shared/shared.module';
|
||||
import {SMSharedModule} from '@common/shared/shared.module';
|
||||
import {ExperimentRouterModule} from './experiments-routing.module';
|
||||
import {ExperimentsComponent} from './experiments.component';
|
||||
import {EffectsModule} from '@ngrx/effects';
|
||||
import {StoreModule} from '@ngrx/store';
|
||||
import {experimentsReducers} from './reducers';
|
||||
import {StoreConfig, StoreModule} from '@ngrx/store';
|
||||
import {experimentsReducers, ExperimentState} from './reducers';
|
||||
import {AdminService} from '~/shared/services/admin.service';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {SelectModelModule} from '../../webapp-common/select-model/select-model.module';
|
||||
import {SmSyncStateSelectorService} from '../../webapp-common/core/services/sync-state-selector.service';
|
||||
import {SelectModelModule} from '@common/select-model/select-model.module';
|
||||
import {SmSyncStateSelectorService} from '@common/core/services/sync-state-selector.service';
|
||||
import {ExperimentOutputEffects} from './effects/experiment-output.effects';
|
||||
import {ExperimentsMenuEffects} from './effects/experiments-menu.effects';
|
||||
import {LayoutModule} from '../../layout/layout.module';
|
||||
import {ExperimentGraphsModule} from '../../webapp-common/shared/experiment-graphs/experiment-graphs.module';
|
||||
import {ExperimentCompareSharedModule} from '../../webapp-common/experiments-compare/shared/experiment-compare-shared.module';
|
||||
import {LayoutModule} from '~/layout/layout.module';
|
||||
import {ExperimentGraphsModule} from '@common/shared/experiment-graphs/experiment-graphs.module';
|
||||
import {ExperimentCompareSharedModule} from '@common/experiments-compare/shared/experiment-compare-shared.module';
|
||||
import {AngularSplitModule} from 'angular-split';
|
||||
import {SMMaterialModule} from '../../webapp-common/shared/material/material.module';
|
||||
import {ExperimentsCommonModule} from '../../webapp-common/experiments/common-experiments.module';
|
||||
import {CommonLayoutModule} from '../../webapp-common/layout/layout.module';
|
||||
import {EXPERIMENTS_STORE_KEY} from '../../webapp-common/experiments/shared/common-experiments.const';
|
||||
import {SMMaterialModule} from '@common/shared/material/material.module';
|
||||
import {ExperimentsCommonModule} from '@common/experiments/common-experiments.module';
|
||||
import {CommonLayoutModule} from '@common/layout/layout.module';
|
||||
import {EXPERIMENTS_STORE_KEY} from '@common/experiments/shared/common-experiments.const';
|
||||
import {ExperimentInfoComponent} from './containers/experiment-info/experiment-info.component';
|
||||
import {DebugImagesModule} from '../../webapp-common/debug-images/debug-images.module';
|
||||
import {ExperimentInfoExecutionComponent} from '../../webapp-common/experiments/containers/experiment-info-execution/experiment-info-execution.component';
|
||||
import {DebugImagesModule} from '@common/debug-images/debug-images.module';
|
||||
import {ExperimentInfoExecutionComponent} from '@common/experiments/containers/experiment-info-execution/experiment-info-execution.component';
|
||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||
import {MatListModule} from '@angular/material/list';
|
||||
import {ExperimentOutputComponent} from './containers/experiment-ouptut/experiment-output.component';
|
||||
import {merge, pick} from 'lodash/fp';
|
||||
import {EXPERIMENTS_PREFIX} from '@common/experiments/actions/common-experiments-view.actions';
|
||||
|
||||
|
||||
export const experimentSyncedKeys = [
|
||||
@@ -40,6 +42,30 @@ export const experimentSyncedKeys = [
|
||||
'output.settingsList',
|
||||
];
|
||||
|
||||
export const EXPERIMENT_CONFIG_TOKEN =
|
||||
new InjectionToken<StoreConfig<ExperimentState, any>>('ExperimentConfigToken');
|
||||
|
||||
const localStorageKey = '_saved_experiment_state_';
|
||||
|
||||
const getExperimentsConfig = () => ({
|
||||
metaReducers: [reducer => {
|
||||
let onInit = true;
|
||||
return (state, action) => {
|
||||
const nextState = reducer(state, action);
|
||||
if (onInit) {
|
||||
onInit = false;
|
||||
const savedState = JSON.parse(localStorage.getItem(localStorageKey));
|
||||
return merge(nextState, savedState);
|
||||
}
|
||||
if (action.type.startsWith(EXPERIMENTS_PREFIX)) {
|
||||
localStorage.setItem(localStorageKey, JSON.stringify(pick(['view.tableMode'], nextState)));
|
||||
}
|
||||
return nextState;
|
||||
};
|
||||
}]
|
||||
});
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
SMMaterialModule,
|
||||
@@ -59,7 +85,7 @@ export const experimentSyncedKeys = [
|
||||
MatSidenavModule,
|
||||
MatListModule,
|
||||
AngularSplitModule,
|
||||
StoreModule.forFeature(EXPERIMENTS_STORE_KEY, experimentsReducers),
|
||||
StoreModule.forFeature(EXPERIMENTS_STORE_KEY, experimentsReducers, EXPERIMENT_CONFIG_TOKEN),
|
||||
EffectsModule.forFeature([ExperimentOutputEffects, ExperimentsMenuEffects]),
|
||||
],
|
||||
declarations: [
|
||||
@@ -70,7 +96,8 @@ export const experimentSyncedKeys = [
|
||||
],
|
||||
providers: [
|
||||
AdminService,
|
||||
SmSyncStateSelectorService
|
||||
SmSyncStateSelectorService,
|
||||
{provide: EXPERIMENT_CONFIG_TOKEN, useFactory: getExperimentsConfig},
|
||||
]
|
||||
})
|
||||
export class ExperimentsModule {
|
||||
|
||||
@@ -1,46 +1,43 @@
|
||||
import {ActionReducerMap, createSelector} from '@ngrx/store';
|
||||
import {experimentsViewReducer, IExperimentsViewState} from './experiments-view.reducer';
|
||||
import {experimentInfoReducer, IExperimentInfoState} from './experiment-info.reducer';
|
||||
import {experimentOutputReducer} from './experiment-output.reducer';
|
||||
import {experimentsViewReducer, IExperimentsViewState, initialState as viewInitialState} from './experiments-view.reducer';
|
||||
import {experimentInfoReducer, IExperimentInfoState, initialState as infoInitialState} from './experiment-info.reducer';
|
||||
import {experimentOutputReducer, ExperimentOutputState, initialState as outputInitialState} from './experiment-output.reducer';
|
||||
import {IExperimentInfo} from '../shared/experiment-info.model';
|
||||
import {TaskStatusEnum} from '../../../business-logic/model/tasks/taskStatusEnum';
|
||||
import {isReadOnly, isSharedAndNotOwner} from '../../../webapp-common/shared/utils/shared-utils';
|
||||
import {EXPERIMENTS_STORE_KEY} from '../../../webapp-common/experiments/shared/common-experiments.const';
|
||||
import {CommonExperimentOutputState} from '../../../webapp-common/experiments/reducers/common-experiment-output.reducer';
|
||||
import {selectSelectedModel} from "../../../webapp-common/models/reducers";
|
||||
import {TaskStatusEnum} from '~/business-logic/model/tasks/taskStatusEnum';
|
||||
import {isReadOnly, isSharedAndNotOwner} from '@common/shared/utils/shared-utils';
|
||||
import {selectSelectedModel} from '@common/models/reducers';
|
||||
import {selectCurrentUser} from '@common/core/reducers/users-reducer';
|
||||
|
||||
export const experimentsReducers: ActionReducerMap<any, any> = {
|
||||
export interface ExperimentState {
|
||||
view: IExperimentsViewState;
|
||||
info: IExperimentInfoState;
|
||||
output: ExperimentOutputState;
|
||||
}
|
||||
|
||||
export const experimentsReducers: ActionReducerMap<ExperimentState, any> = {
|
||||
view: experimentsViewReducer,
|
||||
info: experimentInfoReducer,
|
||||
output: experimentOutputReducer,
|
||||
};
|
||||
|
||||
/**
|
||||
* The createFeatureSelector function selects a piece of state from the root of the state object.
|
||||
* This is used for selecting feature states that are loaded eagerly or lazily.
|
||||
*/
|
||||
export function experiments(state) {
|
||||
return state[EXPERIMENTS_STORE_KEY];
|
||||
}
|
||||
export const experiments = state => state.experiments ?? {} as ExperimentState;
|
||||
|
||||
// view selectors.
|
||||
export const experimentsView = createSelector(experiments, (state): IExperimentsViewState => state ? state.view : {});
|
||||
export const experimentsView = createSelector(experiments, state => (state?.view ?? viewInitialState) as IExperimentsViewState);
|
||||
export const selectExperimentsMetricsCols = createSelector(experimentsView, state => state.metricsCols);
|
||||
export const selectMetricVariants = createSelector(experimentsView, state => state.metricVariants);
|
||||
export const selectMetricsLoading = createSelector(experimentsView, state => state.metricsLoading);
|
||||
|
||||
|
||||
// info selectors
|
||||
export const experimentInfo = createSelector(experiments, (state): IExperimentInfoState => state ? state.info : {});
|
||||
export const selectSelectedExperiment = createSelector(experimentInfo, state => state.selectedExperiment);
|
||||
export const experimentInfo = createSelector(experiments, state => (state?.info ?? infoInitialState) as IExperimentInfoState);
|
||||
export const selectSelectedExperiment = createSelector(experimentInfo, state => state?.selectedExperiment);
|
||||
export const selectExperimentInfoData = createSelector(experimentInfo, state => state.infoData);
|
||||
export const selectShowExtraDataSpinner = createSelector(experimentInfo, state => state.showExtraDataSpinner);
|
||||
|
||||
|
||||
// output selectors
|
||||
export const experimentOutput = createSelector(experiments, (state): CommonExperimentOutputState => state ? state.output : {});
|
||||
|
||||
export const experimentOutput = createSelector(experiments, state => (state.output ?? outputInitialState) as ExperimentOutputState);
|
||||
export const selectIsExperimentEditable = createSelector(selectSelectedExperiment, selectCurrentUser,
|
||||
(experiment, user): boolean => experiment && experiment.status === TaskStatusEnum.Created && !isReadOnly(experiment) && !isSharedAndNotOwner(experiment, user.company));
|
||||
export const selectIsSharedAndNotOwner = createSelector(selectSelectedExperiment, selectSelectedModel, selectCurrentUser,
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<div class="d-flex justify-content-between header-container align-items-center"
|
||||
[ngClass]="{'archive-mode': isArchived}">
|
||||
<sm-toggle-archive *ngIf="!hideArchiveToggle"
|
||||
[showArchived]="isArchived"
|
||||
[class.hide-item]="sharedView"
|
||||
(toggleArchived)="archivedChanged($event)"></sm-toggle-archive>
|
||||
<sm-project-context-navbar *ngIf="!hideCreateNewButton"
|
||||
[class.hide-item]="sharedView"
|
||||
activeFeature="models" [archivedMode]="isArchived"></sm-project-context-navbar>
|
||||
<div class="d-flex justify-content-end align-items-center right-buttons">
|
||||
<sm-clear-filters-button
|
||||
[tableFilters]="tableFilters"
|
||||
(clearTableFilters)="clearTableFilters.emit(tableFilters)"
|
||||
></sm-clear-filters-button>
|
||||
<sm-model-custom-cols-menu
|
||||
[disabled]="minimizedView || sharedView"
|
||||
[isLoading]="isLoadingMetadataKeys"
|
||||
[metadataKeys]="metadataKeys"
|
||||
[tableCols]="tableCols"
|
||||
(removeColFromList)="removeColFromList.emit($event)"
|
||||
(selectedTableColsChanged)="selectedTableColsChanged.emit($event)"
|
||||
(selectMetadataKeysActiveChanged)="selectMetadataKeysActiveChanged.emit($event)"
|
||||
(addOrRemoveMetadataKeyFromColumns)="addOrRemoveMetadataKeyFromColumns.emit($event)"
|
||||
></sm-model-custom-cols-menu>
|
||||
<sm-refresh-button
|
||||
[autoRefreshState]="autoRefreshState"
|
||||
[allowAutoRefresh]="true"
|
||||
(refreshList)="refreshListClicked.emit()"
|
||||
(setAutoRefresh)="setAutoRefresh.emit($event)"
|
||||
>
|
||||
</sm-refresh-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,36 +1,34 @@
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {CheckProjectForDeletion, SetProjectReadyForDeletion} from '../../webapp-common/projects/common-projects.actions';
|
||||
import {PROJECTS_ACTIONS} from '../../webapp-common/projects/common-projects.consts';
|
||||
import {checkProjectForDeletion, setProjectReadyForDeletion} from '@common/projects/common-projects.actions';
|
||||
import {mergeMap, switchMap} from 'rxjs/operators';
|
||||
import {ProjectsValidateDeleteResponse} from '../../business-logic/model/projects/projectsValidateDeleteResponse';
|
||||
import {ProjectsValidateDeleteResponse} from '~/business-logic/model/projects/projectsValidateDeleteResponse';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {ApiProjectsService} from '../../business-logic/api-services/projects.service';
|
||||
import {ApiTasksService} from '../../business-logic/api-services/tasks.service';
|
||||
import {ApiModelsService} from '../../business-logic/api-services/models.service';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {ApiProjectsService} from '~/business-logic/api-services/projects.service';
|
||||
import {ApiModelsService} from '~/business-logic/api-services/models.service';
|
||||
|
||||
@Injectable()
|
||||
export class ProjectsEffects {
|
||||
|
||||
constructor(
|
||||
private actions: Actions, public projectsApi: ApiProjectsService,
|
||||
public experimentsApi: ApiTasksService, public modelsApi: ApiModelsService,
|
||||
private store: Store<any>
|
||||
public modelsApi: ApiModelsService,
|
||||
) {}
|
||||
checkIfProjectExperiments = createEffect(() => this.actions.pipe(
|
||||
ofType<CheckProjectForDeletion>(PROJECTS_ACTIONS.CHECK_PROJECT_FOR_DELETION),
|
||||
switchMap((action) => this.projectsApi.projectsValidateDelete({project: action.payload.project.id})),
|
||||
ofType(checkProjectForDeletion),
|
||||
switchMap((action) => this.projectsApi.projectsValidateDelete({project: action.project.id})),
|
||||
mergeMap((projectsValidateDeleteResponse: ProjectsValidateDeleteResponse) => [
|
||||
new SetProjectReadyForDeletion({
|
||||
experiments: {
|
||||
total: projectsValidateDeleteResponse.tasks,
|
||||
archived: projectsValidateDeleteResponse.tasks - projectsValidateDeleteResponse.non_archived_tasks,
|
||||
unarchived: projectsValidateDeleteResponse.non_archived_tasks
|
||||
},
|
||||
models: {
|
||||
total: projectsValidateDeleteResponse.models,
|
||||
archived: projectsValidateDeleteResponse.models - projectsValidateDeleteResponse.non_archived_models,
|
||||
unarchived: projectsValidateDeleteResponse.non_archived_models
|
||||
setProjectReadyForDeletion({
|
||||
readyForDeletion: {
|
||||
experiments: {
|
||||
total: projectsValidateDeleteResponse.tasks,
|
||||
archived: projectsValidateDeleteResponse.tasks - projectsValidateDeleteResponse.non_archived_tasks,
|
||||
unarchived: projectsValidateDeleteResponse.non_archived_tasks
|
||||
},
|
||||
models: {
|
||||
total: projectsValidateDeleteResponse.models,
|
||||
archived: projectsValidateDeleteResponse.models - projectsValidateDeleteResponse.non_archived_models,
|
||||
unarchived: projectsValidateDeleteResponse.non_archived_models
|
||||
}
|
||||
}
|
||||
})
|
||||
])
|
||||
|
||||
@@ -7,7 +7,7 @@ import {projectsReducer} from './projects.reducer';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {CommonProjectsModule} from '../../webapp-common/projects/common-projects.module';
|
||||
|
||||
export const projectSyncedKeys = ['showHidden'];
|
||||
export const projectSyncedKeys = ['showHidden', 'tableModeAwareness'];
|
||||
|
||||
@NgModule({
|
||||
imports : [
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import {createFeatureSelector, createSelector} from '@ngrx/store';
|
||||
import {on, createReducer, createSelector} from '@ngrx/store';
|
||||
import {
|
||||
CommonProjectReadyForDeletion,
|
||||
commonProjectsInitState,
|
||||
commonProjectsReducer,
|
||||
commonProjectsReducers,
|
||||
ICommonProjectsState
|
||||
} from '@common/projects/common-projects.reducer';
|
||||
import {PROJECTS_ACTIONS} from '@common/projects/common-projects.consts';
|
||||
import {selectSelectedProject} from '@common/core/reducers/projects.reducer';
|
||||
import {checkProjectForDeletion, resetReadyToDelete, setProjectReadyForDeletion} from '@common/projects/common-projects.actions';
|
||||
|
||||
export type IProjectReadyForDeletion = CommonProjectReadyForDeletion;
|
||||
|
||||
@@ -16,25 +15,33 @@ export interface IProjectsState extends ICommonProjectsState {
|
||||
}
|
||||
|
||||
const projectsInitState: IProjectsState = {
|
||||
...commonProjectsInitState,
|
||||
projectReadyForDeletion: {
|
||||
project: null, experiments: null, models: null
|
||||
}
|
||||
};
|
||||
|
||||
export const projectsReducer = (state: IProjectsState = projectsInitState, action): IProjectsState => {
|
||||
switch (action.type) {
|
||||
case PROJECTS_ACTIONS.CHECK_PROJECT_FOR_DELETION:
|
||||
return {...state, projectReadyForDeletion: {...projectsInitState.projectReadyForDeletion, project: action.payload.project}};
|
||||
case PROJECTS_ACTIONS.RESET_READY_TO_DELETE:
|
||||
return {...state, projectReadyForDeletion: projectsInitState.projectReadyForDeletion};
|
||||
case PROJECTS_ACTIONS.SET_PROJECT_READY_FOR_DELETION:
|
||||
return {...state, projectReadyForDeletion: {...state.projectReadyForDeletion, ...action.payload.readyForDeletion}};
|
||||
default:
|
||||
return commonProjectsReducer(state, action);
|
||||
...commonProjectsInitState,
|
||||
projectReadyForDeletion: {
|
||||
project: null, experiments: null, models: null
|
||||
}
|
||||
};
|
||||
|
||||
export const projectsReducer = createReducer(
|
||||
projectsInitState,
|
||||
on(checkProjectForDeletion, (state, action) => ({
|
||||
...state,
|
||||
projectReadyForDeletion: {
|
||||
...projectsInitState.projectReadyForDeletion,
|
||||
project: action.project
|
||||
}
|
||||
})),
|
||||
on(resetReadyToDelete, state => ({...state, projectReadyForDeletion: projectsInitState.projectReadyForDeletion})),
|
||||
on(setProjectReadyForDeletion, (state, action) => ({
|
||||
...state,
|
||||
projectReadyForDeletion: {
|
||||
...state.projectReadyForDeletion,
|
||||
...action.readyForDeletion
|
||||
}
|
||||
})),
|
||||
...commonProjectsReducers
|
||||
);
|
||||
|
||||
export const projects = state => state.projects as IProjectsState;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const selectShowHidden = createSelector(projects, (state) => false);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {ProfileNameComponent} from '../../webapp-common/settings/admin/profile-name/profile-name.component';
|
||||
import {WebappConfigurationComponent} from '../../webapp-common/settings/webapp-configuration/webapp-configuration.component';
|
||||
import {WorkspaceConfigurationComponent} from '../../webapp-common/settings/workspace-configuration/workspace-configuration.component';
|
||||
import {ProfileNameComponent} from '@common/settings/admin/profile-name/profile-name.component';
|
||||
import {WebappConfigurationComponent} from '@common/settings/webapp-configuration/webapp-configuration.component';
|
||||
import {WorkspaceConfigurationComponent} from '@common/settings/workspace-configuration/workspace-configuration.component';
|
||||
import {SettingsComponent} from './settings.component';
|
||||
|
||||
const routes: Routes = [
|
||||
@@ -34,5 +34,5 @@ const routes: Routes = [
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class SettingsRoutingModule { }
|
||||
export class SettingsRoutingModule {}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SettingsRoutingModule } from './settings-routing.module';
|
||||
import { SettingsComponent } from '../settings/settings.component';
|
||||
import {SMMaterialModule} from '../../webapp-common/shared/material/material.module';
|
||||
import {SMMaterialModule} from '@common/shared/material/material.module';
|
||||
import {SMSharedModule} from '@common/shared/shared.module';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {SharedModule} from '~/shared/shared.module';
|
||||
|
||||
@@ -21,7 +21,7 @@ export const selectBreadcrumbsStringsBase = createSelector(
|
||||
(project, experiment, model, projects) =>
|
||||
({project, experiment, model, projects}) as IBreadcrumbs);
|
||||
|
||||
export function prepareNames(data: IBreadcrumbs, noSubProjects?: boolean) {
|
||||
export const prepareNames = (data: IBreadcrumbs, noSubProjects?: boolean) => {
|
||||
const project = prepareLinkData(data.project, true);
|
||||
if (data.project) {
|
||||
const subProjects = [];
|
||||
@@ -39,42 +39,33 @@ export function prepareNames(data: IBreadcrumbs, noSubProjects?: boolean) {
|
||||
].find(proj => currentName === proj.name);
|
||||
subProjects.push(foundProject);
|
||||
});
|
||||
const subProjectsLinks = subProjects.map(proj => ({
|
||||
name: proj?.name.substring(proj?.name.lastIndexOf('/') + 1),
|
||||
url: (noSubProjects || (proj?.name === data.project?.name && data.project?.sub_projects?.length===0)) ? '' : `projects/${proj?.id}/projects`
|
||||
const subProjectsLinks = subProjects.map(subProject => ({
|
||||
name: subProject?.name.substring(subProject?.name.lastIndexOf('/') + 1),
|
||||
url: (noSubProjects || (subProject?.name === data.project?.name && data.project?.sub_projects?.length === 0)) ?
|
||||
`projects/${subProject?.id}` :
|
||||
`projects/${subProject?.id}/projects`
|
||||
})) as { name: string; url: string }[];
|
||||
project.name = project?.name.substring(project.name.lastIndexOf('/') + 1);
|
||||
project.subCrumbs = subProjectsLinks;
|
||||
}
|
||||
const task = prepareLinkData(data.task);
|
||||
const experiment = (data.experiment) ? prepareLinkData(data.experiment, true) : {};
|
||||
const model = prepareLinkData(data.model, true);
|
||||
const overview = formatStaticCrumb('overview');
|
||||
const output = formatStaticCrumb('');
|
||||
const accountAdministration = formatStaticCrumb('account-administration');
|
||||
const experiments = formatStaticCrumb('experiments');
|
||||
const models = formatStaticCrumb('models');
|
||||
const compare = formatStaticCrumb('compare-experiments');
|
||||
|
||||
|
||||
return {
|
||||
':projectId' : project,
|
||||
':experimentId' : experiment,
|
||||
':modelId' : model,
|
||||
...(project.url !=='*' && {':projectId': project}),
|
||||
':taskId' : task,
|
||||
':controllerId': experiment,
|
||||
'compare-experiments': compare,
|
||||
output,
|
||||
experiments,
|
||||
overview,
|
||||
models,
|
||||
execution: formatStaticCrumb('execution'),
|
||||
'hyper-params' : formatStaticCrumb('hyper-params'),
|
||||
artifacts: formatStaticCrumb('artifacts'),
|
||||
general: formatStaticCrumb('general'),
|
||||
log: formatStaticCrumb('logs'),
|
||||
scalar: formatStaticCrumb('scalars'),
|
||||
plots: formatStaticCrumb('plots'),
|
||||
accountAdministration,
|
||||
debugImages: formatStaticCrumb('Debug Samples'),
|
||||
profile: {url: 'profile', name: 'Profile'},
|
||||
'webapp-configuration': {url: 'webapp-configuration', name: 'Configuration'},
|
||||
'workspace-configuration': {url: 'workspace-configuration', name: 'Workspace'},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="side-nav">
|
||||
<div class="item logo">
|
||||
<div class="item-icon">
|
||||
<img src="../../../assets/c-logo.svg?v=1" class="logo-a">
|
||||
<img src="assets/c-logo.svg?v=1" class="logo-a" alt="logo">
|
||||
</div>
|
||||
<div class="caption">
|
||||
<!-- <img src="../../../assets/logo-white.png" class="logo-full">-->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@font-face {
|
||||
font-family: '#{$icomoon-font-family}';
|
||||
src: url('./#{$icomoon-font-family}.ttf?dn2wcr') format('truetype');
|
||||
src: url('./#{$icomoon-font-family}.ttf?medlz4') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
@@ -413,11 +413,6 @@
|
||||
content: $al-ico-zoom-to-fit;
|
||||
}
|
||||
}
|
||||
.al-ico-kibana-code {
|
||||
&:before {
|
||||
content: $al-ico-kibana-code;
|
||||
}
|
||||
}
|
||||
.al-ico-reset {
|
||||
&:before {
|
||||
content: $al-ico-reset;
|
||||
@@ -1136,6 +1131,46 @@
|
||||
content: $al-ico-run;
|
||||
}
|
||||
}
|
||||
.al-ico-table-view {
|
||||
&:before {
|
||||
content: $al-ico-table-view;
|
||||
}
|
||||
}
|
||||
.al-ico-experiment-view {
|
||||
&:before {
|
||||
content: $al-ico-experiment-view;
|
||||
}
|
||||
}
|
||||
.al-ico-code-file {
|
||||
&:before {
|
||||
content: $al-ico-code-file;
|
||||
}
|
||||
}
|
||||
.al-ico-code-square {
|
||||
&:before {
|
||||
content: $al-ico-code-square;
|
||||
}
|
||||
}
|
||||
.al-ico-video {
|
||||
&:before {
|
||||
content: $al-ico-video;
|
||||
}
|
||||
}
|
||||
.al-ico-less-than {
|
||||
&:before {
|
||||
content: $al-ico-less-than;
|
||||
}
|
||||
}
|
||||
.al-ico-greater-than {
|
||||
&:before {
|
||||
content: $al-ico-greater-than;
|
||||
}
|
||||
}
|
||||
.al-ico-toogle-graph {
|
||||
&:before {
|
||||
content: $al-ico-toogle-graph;
|
||||
}
|
||||
}
|
||||
.al-ico-status-draft {
|
||||
&:before {
|
||||
content: $al-ico-status-draft;
|
||||
|
||||
Binary file not shown.
@@ -79,7 +79,6 @@ $al-ico-zoom-in: "\ea26";
|
||||
$al-ico-zoom-out: "\ea27";
|
||||
$al-ico-search: "\e9e1";
|
||||
$al-ico-zoom-to-fit: "\ea28";
|
||||
$al-ico-kibana-code: "\e9a4";
|
||||
$al-ico-reset: "\e9dd";
|
||||
$al-ico-import: "\e99e";
|
||||
$al-ico-export: "\e9b6";
|
||||
@@ -219,6 +218,14 @@ $al-ico-console: "\e9bc";
|
||||
$al-ico-link-arrow: "\e9bf";
|
||||
$al-ico-broken-file: "\e9c0";
|
||||
$al-ico-run: "\e9c1";
|
||||
$al-ico-table-view: "\e9c2";
|
||||
$al-ico-experiment-view: "\e9c3";
|
||||
$al-ico-code-file: "\e9a4";
|
||||
$al-ico-code-square: "\e9c4";
|
||||
$al-ico-video: "\e9c5";
|
||||
$al-ico-less-than: "\e9c6";
|
||||
$al-ico-greater-than: "\e9c7";
|
||||
$al-ico-toogle-graph: "\e9c8";
|
||||
$al-ico-status-draft: "\e902";
|
||||
$al-ico-status-published: "\e906";
|
||||
$al-ico-status-aborted-sec: "\e918";
|
||||
|
||||
3
src/app/webapp-common/assets/icons/checkers.svg
Normal file
3
src/app/webapp-common/assets/icons/checkers.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="10" height="10" viewBox="0 0 10 10">
|
||||
<path id="checkers-2" d="M8,10V8h2v2ZM4,10V8H6v2ZM0,10V8H2v2ZM6,8V6H8V8ZM2,8V6H4V8ZM8,6V4H6V2H8V4h2V6ZM4,6V4H6V6ZM0,6V4H2V6ZM2,4V2H0V0H2V2H4V4ZM8,2V0h2V2ZM4,2V0H6V2Z" fill="#ccc"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 319 B |
BIN
src/app/webapp-common/assets/icons/new-experiment-table.png
Normal file
BIN
src/app/webapp-common/assets/icons/new-experiment-table.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -1,28 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="88" height="88" viewBox="0 0 88 88">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-11,.cls-4,.cls-7{fill:#4d66ff}.cls-4{opacity:.692}.cls-12{fill:#5a658e}.cls-7{opacity:.246}.cls-10{fill:#d3ff00}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="icon-researcher" transform="translate(-.456)">
|
||||
<path id="icon-rec" fill="none" d="M0 0H88V88H0z" transform="translate(.456)"/>
|
||||
<circle id="e" cx="1.5" cy="1.5" r="1.5" fill="#4d66ff" opacity="0.56" transform="translate(64.456 46)"/>
|
||||
<circle id="e-2" cx="2.5" cy="2.5" r="2.5" fill="#4d66ff" opacity="0.584" transform="translate(60.456 39)"/>
|
||||
<circle id="e-3" cx="1" cy="1" r="1" class="cls-4" transform="translate(4.456 27)"/>
|
||||
<circle id="e-4" cx="1.5" cy="1.5" r="1.5" fill="#4d66ff" opacity="0.913" transform="translate(66.456 34)"/>
|
||||
<path id="r" fill="#5a658e" d="M0 0H3V1H0z" opacity="0.796" transform="scale(-1) rotate(79 -8.284 -23.223)"/>
|
||||
<circle id="e-5" cx="1" cy="1" r="1" class="cls-7" transform="translate(38.456 9)"/>
|
||||
<path id="p" fill="#4d66ff" d="M1 0l1 5H0z" opacity="0.717" transform="scale(-1) rotate(-57 -91.262 49.5)"/>
|
||||
<path id="p-2" fill="#5a658e" d="M1.5 0L3 3H0z" opacity="0.228" transform="rotate(-144 18.803 29.311)"/>
|
||||
<path id="p-3" d="M2 0l2 2H0z" class="cls-10" transform="rotate(-49.98 30.225 -10.01)"/>
|
||||
<path id="p-4" d="M1 0l1 4H0z" class="cls-10" transform="rotate(21 8.147 160.502)"/>
|
||||
<circle id="e-6" cx=".5" cy=".5" r=".5" class="cls-11" transform="translate(58.456 2)"/>
|
||||
<circle id="e-7" cx="1" cy="1" r="1" class="cls-4" transform="translate(18.456 40)"/>
|
||||
<circle id="e-8" cx="1" cy="1" r="1" class="cls-7" transform="translate(10.456 41)"/>
|
||||
<g id="researcher" transform="translate(20.456 17)">
|
||||
<path id="researcher-2" d="M34.828 10.422a9.217 9.217 0 0 0 1.489-2.195 1 1 0 0 0 0-.882L34.982 4.63a1.631 1.631 0 0 0-.239-.316C30.541 0 29.19 0 28.747 0a2.043 2.043 0 0 0-1.338.687 3.171 3.171 0 0 1-3.68.723c-3.3-1.116-8.283 1.524-10.707 3.261C10.4 6.555 10.4 7.45 10.4 7.786c0 1.078.008 2.922 2.11 3.519-.866 1.541-2.054 4.09.352 6.59a8.672 8.672 0 0 1 .7 1.087c.065 5.754 4.575 12.378 10.375 12.378 4.828 0 11.047-6.5 11.131-13.45.057-.057.145-.133.208-.188.119-.1.234-.2.33-.3 1.673-1.749.483-5.406-.77-7M12.4 7.9c.716-1.319 7.443-5.7 10.692-4.6a5.134 5.134 0 0 0 5.645-1.119c.06-.052.113-.1.164-.146.524.191 1.894 1.1 4.361 3.616L34.3 7.767a5.7 5.7 0 0 1-1.3 1.62 1.491 1.491 0 0 0-.48.112c-1.226 0-3.41-.063-4.388-1.057a5.84 5.84 0 0 0-4.717-1.656 2.2 2.2 0 0 0-2.129 1.375.921.921 0 0 1-.43.482l-1.5.763a.877.877 0 0 1-.392.094h-4.899c-1.62 0-1.669-.342-1.67-1.6m2.837 9.881V15.43a1.038 1.038 0 0 1 1.037-1.037h5.81a.164.164 0 0 1 .161.161v3.23a1.038 1.038 0 0 1-1.04 1.037h-4.931a1.022 1.022 0 0 1-.782-.376 2.973 2.973 0 0 0-.226-.514 1.194 1.194 0 0 1-.029-.147m8.7 11.576c-3.608 0-7.922-4.242-8.335-9.663a2.02 2.02 0 0 0 .675.124H21.2a2.04 2.04 0 0 0 2.037-2.037v-1.356h1.67v1.356a2.04 2.04 0 0 0 2.037 2.037h4.934a2.011 2.011 0 0 0 1.013-.281c-.893 5.191-5.458 9.82-8.965 9.82m8.989-11.576a1.038 1.038 0 0 1-1.037 1.037h-4.931a1.038 1.038 0 0 1-1.037-1.037v-3.23a.164.164 0 0 1 .161-.161h5.81a1.038 1.038 0 0 1 1.037 1.037zm1.235-1.749c-.055.058-.123.114-.19.172l-.045.041v-.818a2.039 2.039 0 0 0-2.037-2.037h-5.81a1.162 1.162 0 0 0-1.161 1.161v.874H23.24v-.874a1.163 1.163 0 0 0-1.161-1.161h-5.81a2.039 2.039 0 0 0-2.037 2.037v.992c-1.336-1.461-.766-2.78.2-4.464.093-.162.176-.311.257-.458h4.267a2.879 2.879 0 0 0 1.3-.312l1.5-.762a2.944 2.944 0 0 0 1.369-1.494c.037-.088.06-.146.283-.146A3.91 3.91 0 0 1 26.7 9.844c1.628 1.656 4.615 1.656 6.051 1.656a2.011 2.011 0 0 0 .348-.032 5.874 5.874 0 0 1 1.243 2.756 2.314 2.314 0 0 1-.189 1.811" class="cls-12"/>
|
||||
<path id="researcher-3" d="M36.274 33.916a23.283 23.283 0 0 0-4.488-2.209c-1.8-.656-3.428.718-4.872 1.927-1.039.87-2.114 1.77-2.984 1.77s-1.947-.9-2.987-1.77c-1.446-1.209-3.086-2.583-4.875-1.925C6.457 35.214 0 44.742 0 55.419a1 1 0 0 0 1 1h32.5a1 1 0 0 0 0-2H2.021a23.053 23.053 0 0 1 13.937-20.5l7.1 12.511a1 1 0 0 0 1.739 0l7.052-12.418a14.293 14.293 0 0 1 3.354 1.6 1 1 0 1 0 1.069-1.689M23.93 43.9l-5.525-9.732c.406.288.833.642 1.254.995 1.315 1.1 2.673 2.236 4.271 2.236s2.955-1.138 4.269-2.237c.426-.358.857-.718 1.267-1.01z" class="cls-12"/>
|
||||
<path id="bottle" d="M47.569 43.024V36.6h.723a1 1 0 0 0 1-1V34a1 1 0 0 0-1-1h-8.536a1 1 0 0 0-1 1v1.6a1 1 0 0 0 1 1h.722v6.373l-.039.089c-2.538 2.184-4.647 6.169-4.647 8.832a3.793 3.793 0 0 0 3.073 3.831 39.854 39.854 0 0 0 10.317 0 3.794 3.794 0 0 0 3.074-3.831c0-2.664-2.159-6.709-4.687-8.866m-5.676 1.425a1.017 1.017 0 0 0 .283-.373l.217-.495a.993.993 0 0 0 .085-.4V36.6h3.091v6.583a1.079 1.079 0 0 0 .025.226l.091.392a1.007 1.007 0 0 0 .357.561 11.942 11.942 0 0 1 2.612 3.092 9.635 9.635 0 0 0-4.539 1.383c-1.553.981-2.187.8-3.724.355-.48-.138-1.046-.295-1.7-.433a13.159 13.159 0 0 1 3.2-4.306m6.72 9.355a17.259 17.259 0 0 1-4.5.368h-.17a17.548 17.548 0 0 1-4.505-.368c-1.167-.347-1.642-.9-1.642-1.914a5.254 5.254 0 0 1 .193-1.241c.715.134 1.311.305 1.853.461a8.186 8.186 0 0 0 2.3.432 5.6 5.6 0 0 0 3.049-1.017 8.177 8.177 0 0 1 4.033-1.129c.134-.013.276-.031.412-.044a7.076 7.076 0 0 1 .622 2.531c0 1.013-.476 1.567-1.643 1.914" class="cls-11"/>
|
||||
</g>
|
||||
</g>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||
<circle cx="47.43" cy="34.51" r="1.5" fill="#4d66ff" opacity="0.56"/>
|
||||
<circle cx="45.15" cy="30.22" r="2.5" fill="#4d66ff" opacity="0.58"/>
|
||||
<circle cx="48.87" cy="25.9" r="1.5" fill="#4d66ff" opacity="0.91"/>
|
||||
<path d="M7.91,60.5a1,1,0,0,1-1-1c0-10.68,6.45-20.2,16.07-23.71,1.78-.66,3.43.72,4.87,1.92,1,.87,2.12,1.77,3,1.77s1.94-.9,3-1.77c1.45-1.2,3.08-2.58,4.87-1.92A22.83,22.83,0,0,1,43.18,38a1,1,0,0,1,.31,1.38h0a1,1,0,0,1-1.38.31h0a13.94,13.94,0,0,0-3.35-1.6l-7,12.41a1,1,0,0,1-1.74,0L22.87,38A23.06,23.06,0,0,0,8.93,58.5H40.41a1,1,0,0,1,0,2ZM30.84,48l5.53-9.75c-.41.29-.84.65-1.26,1-1.32,1.1-2.67,2.23-4.27,2.23s-3-1.13-4.27-2.23c-.42-.35-.85-.71-1.26-1ZM20.46,23.06A8.61,8.61,0,0,0,19.77,22c-2.41-2.5-1.22-5.05-.36-6.59-2.1-.6-2.11-2.44-2.11-3.52,0-.34,0-1.23,2.63-3.12C22.36,7,27.33,4.37,30.64,5.49a3.17,3.17,0,0,0,3.68-.72,2,2,0,0,1,1.34-.69c.44,0,1.79,0,6,4.31a1.72,1.72,0,0,1,.23.32l1.34,2.71a1,1,0,0,1,0,.89,9.11,9.11,0,0,1-1.49,2.19c1.25,1.59,2.44,5.25.77,7l-.33.31L42,22c-.08,7-6.3,13.45-11.13,13.45-5.8,0-10.31-6.62-10.38-12.38ZM30.84,33.44c3.5,0,8.07-4.63,9-9.82a2,2,0,0,1-1,.28H33.86a2,2,0,0,1-2-2h0V20.5H30.15v1.36a2,2,0,0,1-2,2H23.17a2,2,0,0,1-.67-.13c.42,5.43,4.73,9.67,8.34,9.67Zm2-14.8v3.23a1,1,0,0,0,1,1h4.93a1,1,0,0,0,1-1V19.51a1,1,0,0,0-1-1H33a.17.17,0,0,0-.16.17Zm-10.68.87v2.36l0,.14a3,3,0,0,1,.23.52,1,1,0,0,0,.78.37h4.93a1,1,0,0,0,1-1h0V18.64a.17.17,0,0,0-.16-.17H23.18a1,1,0,0,0-1,1ZM21.34,16c-1,1.68-1.54,3-.2,4.46v-1a2,2,0,0,1,2-2H29a1.18,1.18,0,0,1,1.16,1.17v.87h1.67v-.87A1.16,1.16,0,0,1,33,17.47h5.81a2.06,2.06,0,0,1,2,2v.82l0,0,.19-.17a2.29,2.29,0,0,0,.19-1.81A5.88,5.88,0,0,0,40,15.55a2.07,2.07,0,0,1-.35,0c-1.44,0-4.42,0-6-1.65a3.89,3.89,0,0,0-3.29-1.06c-.23,0-.25.06-.29.14a2.93,2.93,0,0,1-1.36,1.5l-1.5.76a2.84,2.84,0,0,1-1.3.32H21.6l-.26.45ZM35,12.52c1,1,3.17,1.06,4.39,1.06a1.6,1.6,0,0,1,.48-.11,5.8,5.8,0,0,0,1.3-1.62l-1-2.11C37.7,7.22,36.33,6.31,35.8,6.12l-.16.15A5.15,5.15,0,0,1,30,7.39C26.74,6.29,20,10.66,19.3,12c0,1.25.05,1.6,1.67,1.6h4.89a1,1,0,0,0,.39-.1l1.5-.76a.94.94,0,0,0,.43-.49,2.2,2.2,0,0,1,2.13-1.37A5.81,5.81,0,0,1,35,12.52Z" fill="#5a658e"/>
|
||||
<path d="M54.48,47.1V40.68h.72a1,1,0,0,0,1-1v-1.6a1,1,0,0,0-1-1H46.66a1,1,0,0,0-1,1v1.6a1,1,0,0,0,1,1h.73v6.37l0,.09C44.81,49.33,42.7,53.31,42.7,56a3.81,3.81,0,0,0,3.07,3.83,40.51,40.51,0,0,0,10.32,0A3.8,3.8,0,0,0,59.16,56c0-2.67-2.15-6.71-4.68-8.87M48.8,48.53a.93.93,0,0,0,.28-.37l.22-.49a1,1,0,0,0,.09-.4V40.68h3.09v6.58a1.64,1.64,0,0,0,0,.23l.09.39a1.05,1.05,0,0,0,.36.56,12.08,12.08,0,0,1,2.61,3.09A9.76,9.76,0,0,0,51,52.92c-1.55,1-2.18.8-3.72.35-.48-.14-1-.29-1.7-.43a13.19,13.19,0,0,1,3.2-4.31m6.72,9.36a17.45,17.45,0,0,1-4.5.37h-.17a17.52,17.52,0,0,1-4.51-.37C45.18,57.54,44.7,57,44.7,56a5.34,5.34,0,0,1,.2-1.24c.71.14,1.31.31,1.85.46a8.15,8.15,0,0,0,2.3.44,5.68,5.68,0,0,0,3.05-1,8.16,8.16,0,0,1,4-1.13l.41,0A6.87,6.87,0,0,1,57.16,56c0,1-.47,1.57-1.64,1.91" fill="#4d66ff"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -173,6 +173,7 @@ span.highlight-text {
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ export const ICONS = {
|
||||
PLUGIN: 'al-ico-plugin',
|
||||
ADD: 'fa-plus',
|
||||
TREE: 'fa-code-branch',
|
||||
TABLE: 'fa-table',
|
||||
TABLE: 'al-ico-table-view',
|
||||
DETAILS: 'al-ico-experiment-view',
|
||||
SELECTED: 'fa-check-square-o',
|
||||
PROJECT: 'fa-list-alt',
|
||||
FOCUS: 'fa-crosshairs',
|
||||
|
||||
@@ -79,6 +79,6 @@ export const neverShowPopupAgain = createAction(VIEW_PREFIX + 'NEVER_SHOW_POPUP_
|
||||
export const plotlyReady = createAction(VIEW_PREFIX + '[plotly ready]');
|
||||
export const aceReady = createAction(VIEW_PREFIX + '[ace ready]');
|
||||
export const openAppsAwarenessDialog = createAction(VIEW_PREFIX + '[apps awareness dialog]',
|
||||
props<{appsYouTubeIntroLink}>()
|
||||
props<{appsYouTubeIntroVideoId}>()
|
||||
);
|
||||
|
||||
|
||||
@@ -15,13 +15,6 @@ import {MetricColumn} from '@common/shared/utils/tableParamEncode';
|
||||
import {ProjectStatsGraphData} from '@common/core/reducers/projects.reducer';
|
||||
|
||||
export const PROJECTS_PREFIX = '[ROOT_PROJECTS] ';
|
||||
export const SET_PROJECTS = PROJECTS_PREFIX + 'SET_PROJECTS';
|
||||
export const RESET_PROJECTS = PROJECTS_PREFIX + 'RESET_PROJECTS';
|
||||
export const REFETCH_PROJECTS = PROJECTS_PREFIX + 'REFETCH_PROJECTS';
|
||||
export const SET_LAST_UPDATE = PROJECTS_PREFIX + 'SET_LAST_UPDATE';
|
||||
export const RESET_SELECTED_PROJECT = PROJECTS_PREFIX + 'RESET_SELECTED_PROJECT';
|
||||
export const RESET_PROJECT_SELECTION = PROJECTS_PREFIX + 'RESET_PROJECT_SELECTION';
|
||||
export const UPDATE_PROJECT = PROJECTS_PREFIX + 'UPDATE_PROJECT';
|
||||
|
||||
export interface TagColor {
|
||||
foreground: string;
|
||||
@@ -35,20 +28,20 @@ export const getAllSystemProjects = createAction(
|
||||
|
||||
|
||||
export const updateProject = createAction(
|
||||
UPDATE_PROJECT,
|
||||
PROJECTS_PREFIX + 'UPDATE_PROJECT',
|
||||
props<{ id: string; changes: Partial<ProjectsUpdateRequest> }>()
|
||||
);
|
||||
|
||||
export const setAllProjects = createAction(
|
||||
SET_PROJECTS,
|
||||
PROJECTS_PREFIX + 'SET_PROJECTS',
|
||||
props<{ projects: Project[]; updating?: boolean }>()
|
||||
);
|
||||
|
||||
export const resetProjects = createAction(RESET_PROJECTS);
|
||||
export const refetchProjects = createAction(REFETCH_PROJECTS);
|
||||
export const resetProjects = createAction(PROJECTS_PREFIX + 'RESET_PROJECTS');
|
||||
export const refetchProjects = createAction(PROJECTS_PREFIX + 'REFETCH_PROJECTS');
|
||||
|
||||
export const setLastUpdate = createAction(
|
||||
SET_LAST_UPDATE,
|
||||
PROJECTS_PREFIX + 'SET_LAST_UPDATE',
|
||||
props<{ lastUpdate: string }>());
|
||||
|
||||
export const updateProjectCompleted = createAction(
|
||||
@@ -114,12 +107,7 @@ export const setCompanyTags = createAction(
|
||||
props<{ tags: string[]; systemTags: string[] }>()
|
||||
);
|
||||
|
||||
export const setAllProjectTags = createAction(
|
||||
PROJECTS_PREFIX + '[set all projects tags]',
|
||||
props<{ tags: string[]; systemTags: string[] }>()
|
||||
);
|
||||
|
||||
export const addAllProjectTags = createAction(
|
||||
export const addProjectTags = createAction(
|
||||
PROJECTS_PREFIX + '[add all projects tags]',
|
||||
props<{ tags: string[]; systemTags: string[] }>()
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
openTagColorsMenu, refetchProjects,
|
||||
resetProjects, resetProjectSelection,
|
||||
setCompanyTags,
|
||||
setGraphData, setLastUpdate, setAllProjectTags,
|
||||
setGraphData, setLastUpdate,
|
||||
setTags
|
||||
} from '../actions/projects.actions';
|
||||
|
||||
@@ -140,7 +140,7 @@ export class ProjectsEffects {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
switchMap(() => this.projectsApi.projectsGetProjectTags({filter: {system_tags: ['pipeline']}})
|
||||
.pipe(
|
||||
map((res: OrganizationGetTagsResponse) => setAllProjectTags({tags: res.tags, systemTags: res.system_tags})),
|
||||
map((res: OrganizationGetTagsResponse) => setTags({tags: res.tags})),
|
||||
catchError(error => [requestFailed(error)])
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
import {createSelector} from '@ngrx/store';
|
||||
import {on, createReducer, createSelector} from '@ngrx/store';
|
||||
import * as projectsActions from '../actions/projects.actions';
|
||||
import {
|
||||
addAllProjectTags,
|
||||
setAllProjects,
|
||||
setCompanyTags,
|
||||
setGraphData,
|
||||
setLastUpdate,
|
||||
setMetricVariant, setAllProjectTags,
|
||||
setTagColors,
|
||||
setTags,
|
||||
setTagsFilterByProject,
|
||||
TagColor
|
||||
} from '../actions/projects.actions';
|
||||
import {TagColor} from '../actions/projects.actions';
|
||||
import {Project} from '~/business-logic/model/projects/project';
|
||||
import {getSystemTags} from '~/features/experiments/shared/experiments.utils';
|
||||
import {ITableExperiment} from '../../experiments/shared/common-experiment-model.model';
|
||||
@@ -36,7 +25,6 @@ export interface RootProjects {
|
||||
archive: boolean;
|
||||
deep: boolean;
|
||||
projectTags: string[];
|
||||
allProjectsTags: string[];
|
||||
companyTags: string[];
|
||||
systemTags: string[];
|
||||
tagsColors: { [tag: string]: TagColor };
|
||||
@@ -52,7 +40,6 @@ const initRootProjects: RootProjects = {
|
||||
archive: false,
|
||||
deep: false,
|
||||
projectTags: [],
|
||||
allProjectsTags: [],
|
||||
companyTags: [],
|
||||
systemTags: [],
|
||||
tagsColors: {},
|
||||
@@ -72,7 +59,6 @@ export const selectIsDeepMode = createSelector(projects, state => state.deep);
|
||||
export const selectTagsFilterByProject = createSelector(projects, state => state.tagsFilterByProject);
|
||||
export const selectProjectTags = createSelector(projects, state => state.projectTags);
|
||||
export const selectCompanyTags = createSelector(projects, state => state.companyTags);
|
||||
export const selectAllProjectsTagsTags = createSelector(projects, state => state.allProjectsTags);
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export const selectProjectSystemTags = createSelector(projects, state => getSystemTags({system_tags: state.systemTags} as ITableExperiment));
|
||||
export const selectTagsColors = createSelector(projects, state => state.tagsColors);
|
||||
@@ -80,85 +66,62 @@ export const selectLastUpdate = createSelector(projects, (state): string => stat
|
||||
export const selectTagColors = createSelector(selectTagsColors,
|
||||
(tagsColors, props: { tag: string }) => tagsColors[props.tag]);
|
||||
const selectSelectedProjectsMetricVariant = createSelector(projects, state => state.graphVariant);
|
||||
|
||||
export const selectSelectedMetricVariant = createSelector(selectSelectedProjectsMetricVariant,
|
||||
(projectsVariant, projectId: string) => projectsVariant[projectId]);
|
||||
|
||||
export const selectSelectedMetricVariantForCurrProject = createSelector(
|
||||
selectSelectedProjectsMetricVariant, selectSelectedProjectId,
|
||||
(projectsVariant, projectId) => projectsVariant[projectId]);
|
||||
|
||||
export const selectGraphData = createSelector(projects, state => state.graphData);
|
||||
|
||||
export const projectsReducer = createReducer(
|
||||
initRootProjects,
|
||||
on(projectsActions.resetProjects, state => ({...state, projects: [], lastUpdate: null})),
|
||||
on(projectsActions.setAllProjects, (state, action) => {
|
||||
let newProjects = state.projects;
|
||||
if (action.updating) {
|
||||
action.projects.forEach(proj => {
|
||||
const index = state.projects.findIndex(stateProject => stateProject.id === proj.id);
|
||||
if (index > -1) {
|
||||
newProjects = [...newProjects.slice(0, index), proj, ...newProjects.slice(index + 1)];
|
||||
} else {
|
||||
newProjects = [...newProjects, proj];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
newProjects = [...newProjects, ...action.projects];
|
||||
}
|
||||
return {...state, projects: sortByField(newProjects, 'name')};
|
||||
|
||||
export const projectsReducer = (state: RootProjects = initRootProjects, action) => {
|
||||
switch (action.type) {
|
||||
case projectsActions.resetProjects.type:
|
||||
return {...state, projects: [], lastUpdate: null};
|
||||
case projectsActions.setAllProjects.type:
|
||||
const payload = action as ReturnType<typeof setAllProjects>;
|
||||
let newProjects = state.projects;
|
||||
if (payload.updating) {
|
||||
payload.projects.forEach(proj => {
|
||||
const index = state.projects.findIndex(stateProject => stateProject.id === proj.id);
|
||||
if (index > -1) {
|
||||
newProjects = [...newProjects.slice(0, index), proj, ...newProjects.slice(index + 1)];
|
||||
} else {
|
||||
newProjects = [...newProjects, proj];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
newProjects = [...newProjects, ...payload.projects];
|
||||
}
|
||||
return {...state, projects: sortByField(newProjects, 'name')};
|
||||
case projectsActions.setSelectedProjectId.type: {
|
||||
const projectId = (action as ReturnType<typeof projectsActions.setSelectedProjectId>).projectId;
|
||||
return {
|
||||
...state,
|
||||
...(state.selectedProject?.id !== projectId && {archive: initRootProjects.archive}),
|
||||
graphData: initRootProjects.graphData,
|
||||
};
|
||||
}
|
||||
case projectsActions.setSelectedProject.type:
|
||||
return {...state, selectedProject: action.project};
|
||||
case projectsActions.deletedProjectFromRoot.type:
|
||||
const projectIdsToDelete = [action.project.id].concat(action.project.sub_projects.map(project=> project.id))
|
||||
return {...state, projects: state.projects.filter(project=> !projectIdsToDelete.includes(project.id))};
|
||||
case projectsActions.resetSelectedProject.type:
|
||||
return {...state, selectedProject: initRootProjects.selectedProject};
|
||||
case projectsActions.updateProjectCompleted.type: {
|
||||
const payload = action as ReturnType<typeof projectsActions.updateProjectCompleted>;
|
||||
return {
|
||||
...state,
|
||||
selectedProject: {...state.selectedProject, ...payload.changes},
|
||||
projects: state.projects.map(project => project.id === payload.id ? project : {...project, ...payload.changes})
|
||||
};
|
||||
}
|
||||
case projectsActions.setArchive.type:
|
||||
return {...state, archive: action.archive};
|
||||
case projectsActions.setDeep.type:
|
||||
return {...state, deep: action.deep};
|
||||
case setTags.type:
|
||||
return {...state, projectTags: action.tags};
|
||||
case setTagsFilterByProject.type:
|
||||
return {...state, tagsFilterByProject: action.tagsFilterByProject};
|
||||
case setCompanyTags.type:
|
||||
return {...state, companyTags: action.tags, systemTags: action.systemTags};
|
||||
case setAllProjectTags.type:
|
||||
return {...state, allProjectsTags: action.tags};
|
||||
case addAllProjectTags.type:
|
||||
return {...state, allProjectsTags: Array.from(new Set(state.allProjectsTags.concat(action.tags))).sort()};
|
||||
case setTagColors.type:
|
||||
return {...state, tagsColors: {...state.tagsColors, [action.tag]: action.colors}};
|
||||
case setMetricVariant.type: {
|
||||
const payLoad = action as ReturnType<typeof setMetricVariant>;
|
||||
return {...state, graphVariant: {...state.graphVariant, [payLoad.projectId]: payLoad.col}};
|
||||
}
|
||||
case setGraphData.type:
|
||||
return {...state, graphData: (action as ReturnType<typeof setGraphData>).stats};
|
||||
case setLastUpdate.type:
|
||||
return {...state, lastUpdate: (action as ReturnType<typeof setLastUpdate>).lastUpdate};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
}),
|
||||
on(projectsActions.setSelectedProjectId, (state, action) => {
|
||||
const projectId = action.projectId;
|
||||
return {
|
||||
...state,
|
||||
...(state.selectedProject?.id !== projectId && {archive: initRootProjects.archive}),
|
||||
graphData: initRootProjects.graphData,
|
||||
};
|
||||
}),
|
||||
on(projectsActions.setSelectedProject, (state, action) => ({...state, selectedProject: action.project})),
|
||||
on(projectsActions.deletedProjectFromRoot, (state, action) => {
|
||||
const projectIdsToDelete = [action.project.id].concat(action.project.sub_projects.map(project=> project.id))
|
||||
return {...state, projects: state.projects.filter(project=> !projectIdsToDelete.includes(project.id))};
|
||||
}),
|
||||
on(projectsActions.resetSelectedProject, state => ({...state, selectedProject: initRootProjects.selectedProject})),
|
||||
on(projectsActions.updateProjectCompleted, (state, action) => ({
|
||||
...state,
|
||||
selectedProject: {...state.selectedProject, ...action.changes},
|
||||
projects: state.projects.map(project => project.id === action.id ? project : {...project, ...action.changes})
|
||||
})),
|
||||
on(projectsActions.setArchive, (state, action) => ({...state, archive: action.archive})),
|
||||
on(projectsActions.setDeep, (state, action) => ({...state, deep: action.deep})),
|
||||
on(projectsActions.setTags, (state, action) => ({...state, projectTags: action.tags})),
|
||||
on(projectsActions.setTagsFilterByProject, (state, action) => ({...state, tagsFilterByProject: action.tagsFilterByProject})),
|
||||
on(projectsActions.setCompanyTags, (state, action) => ({...state, companyTags: action.tags, systemTags: action.systemTags})),
|
||||
on(projectsActions.addProjectTags, (state, action) => ({...state, projectTags: Array.from(new Set(state.projectTags.concat(action.tags))).sort()})),
|
||||
on(projectsActions.setTagColors, (state, action) => ({...state, tagsColors: {...state.tagsColors, [action.tag]: action.colors}})),
|
||||
on(projectsActions.setMetricVariant, (state, action) => ({
|
||||
...state, graphVariant: {...state.graphVariant, [action.projectId]: action.col}
|
||||
})),
|
||||
on(projectsActions.setGraphData, (state, action) => ({...state, graphData: action.stats})),
|
||||
on(projectsActions.setLastUpdate, (state, action) => ({...state, lastUpdate: action.lastUpdate})),
|
||||
);
|
||||
|
||||
33
src/app/webapp-common/core/services/refresh.service.ts
Normal file
33
src/app/webapp-common/core/services/refresh.service.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectAppVisible, selectAutoRefresh} from '@common/core/reducers/view.reducer';
|
||||
import {interval, Subject} from 'rxjs';
|
||||
import {AUTO_REFRESH_INTERVAL} from '~/app.constants';
|
||||
import {filter, withLatestFrom} from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RefreshService {
|
||||
private _tick = new Subject<boolean>();
|
||||
|
||||
get tick() {
|
||||
return this._tick;
|
||||
}
|
||||
|
||||
constructor(private store: Store) {
|
||||
interval(AUTO_REFRESH_INTERVAL)
|
||||
.pipe(
|
||||
withLatestFrom(
|
||||
this.store.select(selectAutoRefresh),
|
||||
this.store.select(selectAppVisible)
|
||||
),
|
||||
filter(([, auto, visible]) => auto && visible)
|
||||
)
|
||||
.subscribe(() => this._tick.next(null))
|
||||
}
|
||||
|
||||
trigger(auto = false) {
|
||||
this._tick.next(auto);
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,11 @@ import {ProjectsSharedModule} from '../../features/projects/shared/projects-shar
|
||||
import {CommonExperimentSharedModule} from '../experiments/shared/common-experiment-shared.module';
|
||||
import {CommonProjectsModule} from '../projects/common-projects.module';
|
||||
import {SharedModule} from '../../shared/shared.module';
|
||||
import { WelcomeMessageComponent } from './dumb/welcome-message/welcome-message.component';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
|
||||
@NgModule({
|
||||
declarations: [DashboardProjectsComponent, DashboardExperimentsComponent, RecentExperimentTableComponent, WelcomeMessageComponent],
|
||||
exports : [DashboardProjectsComponent, DashboardExperimentsComponent],
|
||||
declarations: [DashboardProjectsComponent, DashboardExperimentsComponent, RecentExperimentTableComponent],
|
||||
exports: [DashboardProjectsComponent, DashboardExperimentsComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SMSharedModule,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<button *ngIf="(recentProjectsList$ | async).length >= cardsInRow"
|
||||
class="btn btn-primary d-flex align-items-center"
|
||||
class="btn btn-cml-primary d-flex align-items-center"
|
||||
(click)="openCreateProjectDialog()">
|
||||
<i class="al-icon al-color sm blue-400 al-ico-add mr-2"></i>NEW PROJECT
|
||||
</button>
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
<sm-dialog-template [displayX]="true"
|
||||
[header]="queue? 'NO WORKERS ASSIGNED TO QUEUE': (step === 1 ? 'Welcome to ClearML' : 'GETTING STARTED')"
|
||||
(xClicked)="closeDialog()" [closeOnX]="false"
|
||||
[iconClass]="queue?'al-ico-queues': 'i-welcome-researcher'">
|
||||
|
||||
<div *ngIf="step === 1; else configStep" class="welcome-content">
|
||||
<div class="body">
|
||||
<div class="info">
|
||||
<span class="position-relative">
|
||||
<i class="al-ico-card-example foreground"></i>
|
||||
<span class="background"></span>
|
||||
</span>
|
||||
<div class="mt-2">See the pre-loaded examples to quickly get familiar with ClearML’s various capabilities.</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<i class="al-ico-help-outlined"></i>
|
||||
<div class="mt-2">Browse "Pro Tips" in the Help menu to jump start your work flow.</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<i class="al-ico-documentation"></i>
|
||||
<div class="mt-2">Check out the <a target="_blank" [href]="docsLink">ClearML docs</a> for advanced information and in depth how-to’s.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<button class="btn btn-neon" (click)="nextSteps($event)">GET STARTED</button>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #configStep>
|
||||
<div class="steps-content">
|
||||
<div class="text" *ngIf="queue">
|
||||
Tasks have been enqueued on the <b>{{queue?.name}}</b> queue, which is currently not serviced by any worker. They will remain in the 'pending' state until a ClearML worker services this queue.
|
||||
</div>
|
||||
<div *ngFor="let step of steps" class="step-container">
|
||||
<div class="step-header">{{step.header}}</div>
|
||||
<div class="step">{{step.title}}</div>
|
||||
<div class="code">
|
||||
<div class="content" #stepCode>{{step.code}}</div>
|
||||
<sm-copy-clipboard
|
||||
[hideBackground]="true"
|
||||
[label]="''"
|
||||
[copyIcon]="'far fa-lg fa-copy'"
|
||||
[clipboardText]="stepCode.innerHTML"></sm-copy-clipboard>
|
||||
</div>
|
||||
<div *ngIf="step.subNote" class="sub-note"><i class="mr-1 fas fa-info-circle info"></i>{{step.subNote}}</div>
|
||||
</div>
|
||||
<div class="step-container cred-step" [class.first-step]="!credentialsCreated" [class.has-label]="credentialsLabel">
|
||||
<div class="step-header">Complete the clearml configuration information as prompted.</div>
|
||||
<div *ngIf="!credentialsCreated" class="d-flex align-items-end">
|
||||
<mat-form-field appearance="outline"
|
||||
class="label-input"
|
||||
floatLabel="always">
|
||||
<mat-label>Label (optional)</mat-label>
|
||||
<input matInput [(ngModel)]="credentialsLabel" [disabled]="credentialsCreated" placeholder="Credentials label" name="credentials">
|
||||
</mat-form-field>
|
||||
<button class="mb-2 btn btn-neon create-cred-button" (click)="createCredentials()">CREATE NEW
|
||||
CREDENTIALS
|
||||
</button>
|
||||
</div>
|
||||
<div class="cred-visible" [class.invisible]="!accessKey">
|
||||
<div class="code">
|
||||
<div #content class="content"><span class="variable">api</span> {{ '{' }}<ng-container *ngIf="community && workspace.name">
|
||||
<span class="">{{'# ' + workspace.name}}</span></ng-container>
|
||||
<span class="variable">web_server</span><span class="operation">:</span> <span class="string">{{WEB_SERVER_URL}}</span>
|
||||
<span class="variable">api_server</span><span class="operation">:</span> <span class="string">{{API_BASE_URL}}</span>
|
||||
<ng-container *ngIf="fileBaseUrl"> <span class="variable">files_server</span><span class="operation">:</span> <span class="string">{{fileBaseUrl}}</span><ng-container *ngIf="credentialsLabel">
|
||||
<span>{{'# ' + credentialsLabel}}</span></ng-container>
|
||||
</ng-container> <span class="variable">credentials</span> {{ '{' }}
|
||||
<span class="string">"access_key"</span> <span class="operation">=</span> <span class="string">"{{accessKey}}"</span>
|
||||
<span class="string">"secret_key"</span> <span class="operation">=</span> <span class="string">"{{secretKey}}"</span>
|
||||
}
|
||||
}</div>
|
||||
<sm-copy-clipboard
|
||||
[hideBackground]="true"
|
||||
[label]="''"
|
||||
[copyIcon]="'far fa-lg fa-copy'"
|
||||
[clipboardText]="content.textContent"></sm-copy-clipboard>
|
||||
</div>
|
||||
<div class="sub-note"><i class="mr-1 fas fa-info-circle info"></i>Manage your app credentials in the <a target="_blank" href="settings/workspace-configuration">workspace settings page</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-container" *ngIf="!queue">
|
||||
<div class="step">3. Integrate</div>
|
||||
<div class="step sub-note">Add the following lines to your code</div>
|
||||
<div class="code">
|
||||
<div #content class="content"><span class="variable">from</span> {{gettingStartedContext?.packageName || 'clearml'}} <span class="variable">import</span> Task
|
||||
task <span class="operation">=</span> Task.<span class="variable">init</span>(project_name<span class="operation">=</span>"my project", task_name<span class="operation">=</span>"my task")</div>
|
||||
<sm-copy-clipboard
|
||||
[hideBackground]="true"
|
||||
[label]="''"
|
||||
[copyIcon]="'far fa-lg fa-copy'"
|
||||
[clipboardText]="content.textContent"></sm-copy-clipboard>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="queue" class="text">
|
||||
<sm-checkbox-control
|
||||
fieldName="orphanedQueue"
|
||||
[formData]="doNotShowAgain"
|
||||
(formDataChanged)="doNotShowThisAgain($event)"
|
||||
label="Don’t show again"></sm-checkbox-control>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</sm-dialog-template>
|
||||
@@ -1,8 +1,8 @@
|
||||
import {createAction, props} from '@ngrx/store';
|
||||
import {Task} from '../../business-logic/model/tasks/task';
|
||||
import {TaskMetric} from '../../business-logic/model/events/taskMetric';
|
||||
import {EventsDebugImagesResponse} from '../../business-logic/model/events/eventsDebugImagesResponse';
|
||||
import {EventsGetDebugImageIterationsResponse} from '../../business-logic/model/events/eventsGetDebugImageIterationsResponse';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {TaskMetric} from '~/business-logic/model/events/taskMetric';
|
||||
import {EventsDebugImagesResponse} from '~/business-logic/model/events/eventsDebugImagesResponse';
|
||||
import {EventsGetDebugImageIterationsResponse} from '~/business-logic/model/events/eventsGetDebugImageIterationsResponse';
|
||||
|
||||
export const DEBUG_IMAGES_PREFIX = 'DEBUG_IMAGES_';
|
||||
|
||||
@@ -20,7 +20,7 @@ export const getDebugImagesMetrics = createAction(
|
||||
|
||||
export const refreshDebugImagesMetrics = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'REFRESH_DEBUG_IMAGES_METRICS',
|
||||
props<{ tasks: string[] }>()
|
||||
props<{ tasks: string[], autoRefresh?: boolean }>()
|
||||
);
|
||||
|
||||
export const fetchExperiments = createAction(
|
||||
@@ -45,7 +45,7 @@ export const setSelectedMetric = createAction(
|
||||
|
||||
export const refreshMetric = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'REFRESH_IMAGES_SELECTED_METRIC',
|
||||
props<{ payload: TaskMetric }>()
|
||||
props<{ payload: TaskMetric; autoRefresh?: boolean }>()
|
||||
);
|
||||
|
||||
export const getNextBatch= createAction(
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {catchError, mergeMap, map, switchMap, withLatestFrom} from 'rxjs/operators';
|
||||
import {catchError, mergeMap, map, switchMap, withLatestFrom, filter} from 'rxjs/operators';
|
||||
import * as debugActions from './debug-images-actions';
|
||||
import {activeLoader, deactivateLoader} from '../core/actions/layout.actions';
|
||||
import {ApiTasksService} from '../../business-logic/api-services/tasks.service';
|
||||
import {ApiEventsService} from '../../business-logic/api-services/events.service';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
import {ApiEventsService} from '~/business-logic/api-services/events.service';
|
||||
import {requestFailed} from '../core/actions/http.actions';
|
||||
import {refreshExperiments} from '../experiments/actions/common-experiments-view.actions';
|
||||
import {setRefreshing} from '../experiments-compare/actions/compare-header.actions';
|
||||
import {Action, Store} from '@ngrx/store';
|
||||
import {selectDebugImages, selectImageViewerScrollId} from './debug-images-reducer';
|
||||
import {
|
||||
@@ -16,8 +15,9 @@ import {
|
||||
setDebugImageViewerScrollId,
|
||||
setDisplayerBeginningOfTime, setDisplayerEndOfTime
|
||||
} from './debug-images-actions';
|
||||
import {EventsDebugImagesResponse} from '../../business-logic/model/events/eventsDebugImagesResponse';
|
||||
import {EventsGetTaskMetricsResponse} from '../../business-logic/model/events/eventsGetTaskMetricsResponse';
|
||||
import {EventsDebugImagesResponse} from '~/business-logic/model/events/eventsDebugImagesResponse';
|
||||
import {EventsGetTaskMetricsResponse} from '~/business-logic/model/events/eventsGetTaskMetricsResponse';
|
||||
import {COMPARE_DEBUG_IMAGES_ONLY_FIELDS} from '../experiments-compare/experiments-compare.constants';
|
||||
|
||||
export const ALL_IMAGES = '-- All --';
|
||||
|
||||
@@ -48,7 +48,8 @@ export class DebugImagesEffects {
|
||||
) {}
|
||||
|
||||
activeLoader = createEffect(() => this.actions$.pipe(
|
||||
ofType(debugActions.fetchExperiments),
|
||||
ofType(debugActions.fetchExperiments, debugActions.refreshMetric, debugActions.refreshDebugImagesMetrics),
|
||||
filter(action => !(action as any).autoRefresh),
|
||||
map(action => activeLoader(action.type))
|
||||
));
|
||||
|
||||
@@ -68,7 +69,7 @@ export class DebugImagesEffects {
|
||||
})
|
||||
.pipe(
|
||||
mergeMap((res: EventsDebugImagesResponse ) => {
|
||||
const actionsToShoot = [deactivateLoader(action.type), setRefreshing({payload: false})] as Action[];
|
||||
const actionsToShoot = [deactivateLoader(action.type)] as Action[];
|
||||
if (res.metrics[0].iterations && res.metrics[0].iterations.length > 0) {
|
||||
actionsToShoot.push(debugActions.setDebugImages({res, task: action.payload.task}));
|
||||
switch (action.type) {
|
||||
@@ -102,7 +103,6 @@ export class DebugImagesEffects {
|
||||
}),
|
||||
catchError(error => [
|
||||
requestFailed(error),
|
||||
setRefreshing({payload: false}),
|
||||
deactivateLoader(action.type),
|
||||
deactivateLoader(refreshExperiments.type)
|
||||
])
|
||||
@@ -113,7 +113,7 @@ export class DebugImagesEffects {
|
||||
fetchExperiments$ = createEffect(() => this.actions$.pipe(
|
||||
ofType(debugActions.fetchExperiments),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
switchMap((action) => this.apiTasks.tasksGetAllEx({id: action.tasks, only_fields: ['id', 'name', 'status']})
|
||||
switchMap((action) => this.apiTasks.tasksGetAllEx({id: action.tasks, only_fields: COMPARE_DEBUG_IMAGES_ONLY_FIELDS})
|
||||
.pipe(
|
||||
mergeMap(res => [debugActions.setExperimentsNames({tasks: res.tasks}), deactivateLoader(action.type)]),
|
||||
catchError(error => [requestFailed(error), deactivateLoader(action.type)])
|
||||
|
||||
@@ -9,7 +9,7 @@ import {EventEmitter} from '@angular/core';
|
||||
export class DebugImagesViewComponent {
|
||||
|
||||
public trackKey = (index: number, item: any) => item.iter;
|
||||
public trackFrame = (index: number, item: any) => item.key;
|
||||
public trackFrame = (index: number, item: any) => `${item?.key} ${item?.timestamp}`;
|
||||
public iterationEvents;
|
||||
|
||||
@Input() experimentId;
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
<div class="p-3 images-container">
|
||||
<div class="single-debug-images-container" *ngFor="let experimentId of experimentIds; trackBy: trackExperiment">
|
||||
<div class="single-debug-images-container" *ngFor="let experimentId of experimentIds; trackBy: trackExperiment" [class.separator]="experimentIds?.length > 1">
|
||||
<header *ngIf="experimentIds?.length > 1">
|
||||
<div
|
||||
class="experiment-name">
|
||||
<span [smTooltip]="experimentNames[experimentId]" matTooltipPosition="above">{{modifiedExperimentsNames[experimentId]}}</span>
|
||||
</div>
|
||||
<sm-experiment-compare-general-data
|
||||
*ngIf="(experiments | itemById: experimentId).name"
|
||||
[experiment]="experiments | itemById: experimentId"
|
||||
[tags]="(experiments | itemById: experimentId)?.tags"
|
||||
(copyIdClicked)="copyIdToClipboard()"
|
||||
>
|
||||
</sm-experiment-compare-general-data>
|
||||
</header>
|
||||
<div class="d-flex">
|
||||
<div class="d-flex" >
|
||||
<div class="metric-bar" [class.minimized]="minimized" *ngIf="!thereAreNoMetrics(experimentId)">
|
||||
<label>Metric:</label>
|
||||
<mat-form-field appearance="outline" [ngClass]="{'dark thin': isDarkTheme}">
|
||||
<mat-select #metricSelect (selectionChange)="selectMetric($event, experimentId)" [panelClass]="isDarkTheme ? 'dark black dark-theme': 'light-theme'"
|
||||
[value]="selectedMetrics[experimentId]" name="selectedMetric">
|
||||
<mat-select
|
||||
#metricSelect
|
||||
(selectionChange)="selectMetric($event, experimentId)"
|
||||
[panelClass]="isDarkTheme ? 'dark black dark-theme': 'light-theme'"
|
||||
[value]="selectedMetrics[experimentId]"
|
||||
>
|
||||
<mat-option *ngIf="selectedMetrics[experimentId]" [value]="allImages">{{allImages}}</mat-option>
|
||||
<mat-option *ngFor="let metric of optionalMetrics[experimentId]" [value]="metric">
|
||||
{{metric}}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let metric of optionalMetrics[experimentId]" [value]="metric">{{metric}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<label>Iterations:</label>
|
||||
@@ -25,12 +30,9 @@
|
||||
class="al-icon al-ico-next-batch al-color light-grey-blue"
|
||||
smTooltip="Older images"></div>
|
||||
|
||||
<b
|
||||
class="text-right">{{debugImages && debugImages[experimentId] && debugImages[experimentId][0][debugImages[experimentId][0].length - 1].iter}}</b>
|
||||
|
||||
<b class="text-right">{{debugImages?.[experimentId]?.data.slice(-1)[0].iter}}</b>
|
||||
<div class="al-icon al-ico-between al-color light-blue-grey"></div>
|
||||
|
||||
<b>{{debugImages && debugImages[experimentId] && debugImages[experimentId][0][0].iter}}</b>
|
||||
<b>{{debugImages?.[experimentId]?.data?.[0].iter}}</b>
|
||||
|
||||
<div [ngClass]="{'disabled': (timeIsNow$| async)[experimentId]}"
|
||||
(click)="(!timeIsNow[experimentId]) && previousBatch({task: experimentId, metric: metricSelect.value})"
|
||||
@@ -51,8 +53,8 @@
|
||||
<h3>NO DEBUG SAMPLES</h3>
|
||||
</div>
|
||||
<sm-debug-images-view
|
||||
*ngFor="let debugImages of debugImages[experimentId]"
|
||||
[iterations]="debugImages"
|
||||
*ngIf="debugImages?.[experimentId]?.data"
|
||||
[iterations]="debugImages[experimentId].data"
|
||||
[experimentId]="experimentId"
|
||||
[title]="experimentNames && experimentNames[experimentId]"
|
||||
[isMergeIterations]="mergeIterations"
|
||||
@@ -62,5 +64,5 @@
|
||||
>
|
||||
</sm-debug-images-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -105,6 +105,10 @@ sm-debug-images-view {
|
||||
min-height: 100%;
|
||||
color: $blue-500;
|
||||
margin: 0 12px;
|
||||
&.separator:not(:last-child){
|
||||
border-right: 1px solid #f2f4fc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.bordered-experiments {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {ChangeDetectorRef, Component, ElementRef, Input, OnDestroy, OnInit} from '@angular/core';
|
||||
import {ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
|
||||
import {combineLatest, Observable, Subscription} from 'rxjs';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {IExperimentInfoState} from '../../features/experiments/reducers/experiment-info.reducer';
|
||||
import {IExperimentInfoState} from '~/features/experiments/reducers/experiment-info.reducer';
|
||||
import {AdminService} from '~/shared/services/admin.service';
|
||||
import {selectS3BucketCredentials} from '../core/reducers/common-auth-reducer';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
@@ -17,17 +17,43 @@ import {
|
||||
} from './debug-images-reducer';
|
||||
import {selectRouterParams} from '../core/reducers/router-reducer';
|
||||
import {distinctUntilChanged, filter, map, withLatestFrom} from 'rxjs/operators';
|
||||
import {Task} from '../../business-logic/model/tasks/task';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {TaskStatusEnum} from '../../business-logic/model/tasks/taskStatusEnum';
|
||||
import {TaskStatusEnum} from '~/business-logic/model/tasks/taskStatusEnum';
|
||||
import {ImageDisplayerComponent} from '../experiments/dumb/image-displayer/image-displayer.component';
|
||||
import {selectSelectedExperiment} from '../../features/experiments/reducers';
|
||||
import {selectRefreshing} from '../experiments-compare/reducers';
|
||||
import {TaskMetric} from '../../business-logic/model/events/taskMetric';
|
||||
import {selectSelectedExperiment} from '~/features/experiments/reducers';
|
||||
import {TaskMetric} from '~/business-logic/model/events/taskMetric';
|
||||
import {get, isEqual} from 'lodash/fp';
|
||||
import {ALL_IMAGES} from './debug-images-effects';
|
||||
import {MatSelectChange} from '@angular/material/select';
|
||||
import {getSignedUrl} from '../core/actions/common-auth.actions';
|
||||
import {addMessage} from '../core/actions/layout.actions';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
interface Event {
|
||||
timestamp: number;
|
||||
type?: string;
|
||||
task?: string;
|
||||
iter?: number;
|
||||
metric?: string;
|
||||
variant?: string;
|
||||
key?: string;
|
||||
url?: string;
|
||||
'@timestamp'?: string;
|
||||
worker?: string;
|
||||
}
|
||||
|
||||
interface Iteration {
|
||||
events: Event[];
|
||||
iter: number;
|
||||
}
|
||||
|
||||
interface DebugSamples {
|
||||
metrics: string[];
|
||||
metric: string;
|
||||
scrollId: string;
|
||||
data: Iteration[]
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'sm-debug-images',
|
||||
@@ -37,6 +63,8 @@ import {getSignedUrl} from '../core/actions/common-auth.actions';
|
||||
export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input() isDarkTheme = false;
|
||||
@Output() copyIdClicked = new EventEmitter();
|
||||
|
||||
private debugImagesSubscription: Subscription;
|
||||
private taskNamesSubscription: Subscription;
|
||||
private selectedExperimentSubscription: Subscription;
|
||||
@@ -51,7 +79,7 @@ export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
public beginningOfTime$: Observable<any>;
|
||||
|
||||
public mergeIterations: boolean;
|
||||
public debugImages: { [experimentId: string]: any };
|
||||
public debugImages: { [experimentId: string]: DebugSamples };
|
||||
public experimentNames: { [id: string]: string } = {};
|
||||
public experimentIds: string[];
|
||||
public allowAutorefresh: boolean = false;
|
||||
@@ -59,7 +87,7 @@ export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
public noMoreData$: Observable<boolean>;
|
||||
public optionalMetrics$: Observable<any>;
|
||||
public optionalMetrics: any;
|
||||
public selectedMetrics: {[taskId: string] : string} = {};
|
||||
public selectedMetrics: { [taskId: string]: string } = {};
|
||||
public beginningOfTime: any;
|
||||
private beginningOfTimeSubscription: Subscription;
|
||||
public timeIsNow: any;
|
||||
@@ -68,6 +96,7 @@ export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
readonly allImages = ALL_IMAGES;
|
||||
private selectedMetric: string;
|
||||
public modifiedExperimentsNames: { [id: string]: string } = {};
|
||||
public experiments: Partial<Task>[];
|
||||
|
||||
constructor(
|
||||
private store: Store<IExperimentInfoState>,
|
||||
@@ -75,7 +104,8 @@ export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
private dialog: MatDialog,
|
||||
private changeDetection: ChangeDetectorRef,
|
||||
private activeRoute: ActivatedRoute,
|
||||
private elRef: ElementRef
|
||||
private elRef: ElementRef,
|
||||
private refresh: RefreshService
|
||||
) {
|
||||
this.tasks$ = this.store.select(selectTaskNames);
|
||||
this.optionalMetrics$ = this.store.select(selectOptionalMetrics);
|
||||
@@ -87,20 +117,22 @@ export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
store.pipe(select(selectS3BucketCredentials)),
|
||||
store.pipe(select(selectDebugImages))]).pipe(
|
||||
map(([, debugImages]) => Object.entries(debugImages).reduce(((acc, val: any) => {
|
||||
acc[val[0]] = val[1].metrics.map(metric => metric.iterations.map(iteration => {
|
||||
const events = iteration.events.map(event => {
|
||||
this.store.dispatch(getSignedUrl({url: event.url, config: {disableCache: event.timestamp}}));
|
||||
return {
|
||||
...event,
|
||||
url: event.url,
|
||||
variantAndMetric: this.selectedMetric === ALL_IMAGES ? `${event.metric}/${event.variant}` : ''
|
||||
};
|
||||
});
|
||||
return {...iteration, events};
|
||||
}));
|
||||
acc[val[0]].metrics = val[1].metrics.map(metric => metric.metric || metric.iterations[0].events[0].metric);
|
||||
acc[val[0]].metric = acc[val[0]].metrics[0];
|
||||
acc[val[0]].scrollId = val[1].scroll_id;
|
||||
const id = val[0];
|
||||
const iterations = val[1].metrics.find(m => m.task === id).iterations;
|
||||
acc[id] = {data: iterations.map(iteration => ({
|
||||
iter: iteration.iter,
|
||||
events: iteration.events.map(event => {
|
||||
this.store.dispatch(getSignedUrl({url: event.url, config: {disableCache: event.timestamp}}));
|
||||
return {
|
||||
...event,
|
||||
url: event.url,
|
||||
variantAndMetric: this.selectedMetric === ALL_IMAGES ? `${event.metric}/${event.variant}` : ''
|
||||
};
|
||||
})
|
||||
}))};
|
||||
acc[id].metrics = val[1].metrics.map(metric => metric.metric || metric.iterations[0].events[0].metric);
|
||||
acc[id].metric = acc[id].metrics[0];
|
||||
acc[id].scrollId = val[1].scroll_id;
|
||||
return acc;
|
||||
}), {}))
|
||||
).subscribe(debugImages => {
|
||||
@@ -133,7 +165,6 @@ export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
this.timeIsNow = timeIsNow;
|
||||
});
|
||||
|
||||
let currentExperiment: string;
|
||||
|
||||
if (multipleExperiments) {
|
||||
this.routerParamsSubscription = this.routerParams$
|
||||
@@ -155,49 +186,55 @@ export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
this.store.dispatch(getDebugImagesMetrics({tasks: this.experimentIds}));
|
||||
}
|
||||
|
||||
this.experiments = tasks;
|
||||
this.experimentNames = tasks.reduce((acc, task) => ({
|
||||
...acc,
|
||||
[task.id]: task.name
|
||||
}), {}) as { [id: string]: string };
|
||||
tasks.forEach(task => {
|
||||
this.modifiedExperimentsNames[task.id] = Object.values(this.experimentNames).filter(name => name === task.name).length > 1 ? `${task.name}.${task.id.substr(0, 6)}` : task.name;
|
||||
this.modifiedExperimentsNames[task.id] = Object.values(this.experimentNames).filter(name => name === task.name).length > 1 ? `${task.name}.${task.id.slice(0, 6)}` : task.name;
|
||||
}
|
||||
);
|
||||
this.changeDetection.detectChanges();
|
||||
});
|
||||
|
||||
// auto refresh subscription for compare only.
|
||||
this.refreshingSubscription = this.store.select(selectRefreshing).pipe(
|
||||
filter(({refreshing}) => refreshing),
|
||||
withLatestFrom(
|
||||
this.store.select(selectTimeIsNow),
|
||||
)
|
||||
).subscribe(([, timeIsNow]) => {
|
||||
this.store.dispatch(debugActions.refreshDebugImagesMetrics({tasks: this.experimentIds}));
|
||||
this.experimentIds.forEach(experiment => {
|
||||
this.refresh(experiment, timeIsNow, this.debugImages);
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.selectedExperimentSubscription = this.store.select(selectSelectedExperiment)
|
||||
.pipe(
|
||||
filter(experiment => !!experiment),
|
||||
withLatestFrom(
|
||||
this.store.select(selectTimeIsNow),
|
||||
),
|
||||
).subscribe(([experiment, timeIsNow]) => {
|
||||
if (currentExperiment === experiment.id && Object.keys(this.debugImages || {}).length > 0) {
|
||||
this.refresh(experiment.id, timeIsNow, this.debugImages);
|
||||
} else {
|
||||
currentExperiment = experiment.id;
|
||||
this.experimentNames = {[experiment.id]: experiment.name};
|
||||
this.experimentIds = [experiment.id];
|
||||
this.store.dispatch(getDebugImagesMetrics({tasks: this.experimentIds}));
|
||||
}
|
||||
distinctUntilChanged((previous, current) => previous?.id === current?.id)
|
||||
).subscribe(experiment => {
|
||||
this.experimentNames = {[experiment.id]: experiment.name};
|
||||
this.experimentIds = [experiment.id];
|
||||
this.store.dispatch(getDebugImagesMetrics({tasks: this.experimentIds}));
|
||||
});
|
||||
}
|
||||
|
||||
// auto refresh subscription for compare only.
|
||||
this.refreshingSubscription = this.refresh.tick
|
||||
.pipe(
|
||||
filter(auto => !multipleExperiments || auto !== null),
|
||||
withLatestFrom(
|
||||
this.store.select(selectTimeIsNow),
|
||||
)
|
||||
)
|
||||
.subscribe(([auto, timeIsNow]) => {
|
||||
if (multipleExperiments) {
|
||||
this.store.dispatch(debugActions.refreshDebugImagesMetrics({tasks: this.experimentIds, autoRefresh: auto}));
|
||||
}
|
||||
this.experimentIds.forEach(experimentId => {
|
||||
if (experimentId && timeIsNow?.[experimentId] && this.debugImages[experimentId] && this.elRef.nativeElement.scrollTop < 40) {
|
||||
this.store.dispatch(debugActions.refreshMetric({
|
||||
payload: {
|
||||
task: experimentId,
|
||||
metric: this.debugImages[experimentId]?.metric,
|
||||
},
|
||||
autoRefresh: auto
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.optionalMetricsSubscription = this.optionalMetrics$.subscribe(optionalMetrics => {
|
||||
const optionalMetricsDic = {};
|
||||
optionalMetrics.forEach(experimentMetrics => optionalMetricsDic[experimentMetrics.task] = experimentMetrics.metrics);
|
||||
@@ -250,17 +287,6 @@ export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
refresh(experimentId: string, timeIsNow, debugImages) {
|
||||
if (experimentId && timeIsNow?.[experimentId] && debugImages[experimentId] && this.elRef.nativeElement.scrollTop < 40) {
|
||||
this.store.dispatch(debugActions.refreshMetric({
|
||||
payload: {
|
||||
task: experimentId,
|
||||
metric: debugImages[experimentId].metric
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private isTaskRunning(tasks: Partial<Task>[]) {
|
||||
return tasks.some(task => [TaskStatusEnum.InProgress, TaskStatusEnum.Queued].includes(task.status));
|
||||
}
|
||||
@@ -293,11 +319,19 @@ export class DebugImagesComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
thereAreNoDebugImages(experiment) {
|
||||
return !(this.debugImages && this.debugImages[experiment] && this.debugImages[experiment].length > 0);
|
||||
return !(this.debugImages && this.debugImages[experiment] && this.debugImages[experiment].data.length > 0);
|
||||
}
|
||||
|
||||
shouldShowNoImagesForExperiment(experiment: string) {
|
||||
return (this.thereAreNoMetrics(experiment) && this.optionalMetrics && this.optionalMetrics[experiment]) || (this.thereAreNoDebugImages(experiment) && this.debugImages && this.debugImages[experiment]);
|
||||
}
|
||||
|
||||
// buildUrl() {
|
||||
// return ['../../', 'experiments', ];
|
||||
// }
|
||||
|
||||
copyIdToClipboard() {
|
||||
this.store.dispatch(addMessage('success', 'Copied to clipboard'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { ExperimentCompareSharedModule } from '../experiments-compare/shared/experiment-compare-shared.module';
|
||||
import { ImageDisplayerComponent } from '../experiments/dumb/image-displayer/image-displayer.component';
|
||||
import { SMSharedModule } from '../shared/shared.module';
|
||||
import { UiComponentsModule } from '../shared/ui-components/ui-components.module';
|
||||
import { DebugImageSnippetComponent } from './debug-image-snippet/debug-image-snippet.component';
|
||||
import { DebugImagesEffects } from './debug-images-effects';
|
||||
import {ScrollingModule} from '@angular/cdk/scrolling';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {EffectsModule} from '@ngrx/effects';
|
||||
import {StoreModule} from '@ngrx/store';
|
||||
import {ExperimentCompareSharedModule} from '../experiments-compare/shared/experiment-compare-shared.module';
|
||||
import {ImageDisplayerComponent} from '../experiments/dumb/image-displayer/image-displayer.component';
|
||||
import {SMSharedModule} from '../shared/shared.module';
|
||||
import {UiComponentsModule} from '../shared/ui-components/ui-components.module';
|
||||
import {DebugImageSnippetComponent} from './debug-image-snippet/debug-image-snippet.component';
|
||||
import {DebugImagesEffects} from './debug-images-effects';
|
||||
import {debugSamplesReducer} from './debug-images-reducer';
|
||||
import { DebugImagesViewComponent } from './debug-images-view/debug-images-view.component';
|
||||
import { DebugImagesComponent } from './debug-images.component';
|
||||
import {DebugImagesViewComponent} from './debug-images-view/debug-images-view.component';
|
||||
import {DebugImagesComponent} from './debug-images.component';
|
||||
import {MatSliderModule} from '@angular/material/slider';
|
||||
import {ExperimentGraphsModule} from '../shared/experiment-graphs/experiment-graphs.module';
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import {createAction, props} from '@ngrx/store';
|
||||
import {Task} from '../../../business-logic/model/tasks/task';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {Params} from '@angular/router';
|
||||
import {ISmCol} from '../../shared/ui-components/data/table/table.consts';
|
||||
import {SortMeta} from 'primeng/api';
|
||||
import {TableFilter} from '../../shared/utils/tableParamEncode';
|
||||
import {EXPERIMENTS_PREFIX} from '../../experiments/actions/common-experiments-view.actions';
|
||||
|
||||
export const EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ = 'EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_';
|
||||
|
||||
@@ -22,7 +21,6 @@ export const SET_NAVIGATION_PREFERENCES = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_
|
||||
|
||||
|
||||
export const setHideIdenticalFields = createAction(SET_HIDE_IDENTICAL_ROWS, props<{payload: boolean}>());
|
||||
export const setRefreshing = createAction(SET_REFRESHING, props<{ payload: boolean; autoRefresh?: boolean }>());
|
||||
export const setExperimentsUpdateTime = createAction(SET_EXPERIMENTS_UPDATE_TIME, props<{ payload: {[key: string]: Date}}>());
|
||||
export const refreshIfNeeded = createAction(REFRESH_IF_NEEDED, props<{ payload: boolean; autoRefresh?: boolean }>());
|
||||
export const toggleShowScalarOptions = createAction(TOGGLE_SHOW_SACLARS_OPTIONS);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as detailsActions from '../actions/experiments-compare-details.actions';
|
||||
import * as paramsActions from '../actions/experiments-compare-params.actions';
|
||||
import {ExperimentCompareTree, ExperimentCompareTreeSection, IExperimentDetail} from '../../../features/experiments-compare/experiments-compare-models';
|
||||
import {ExperimentCompareTree, ExperimentCompareTreeSection, IExperimentDetail} from '~/features/experiments-compare/experiments-compare-models';
|
||||
import {get, has, isEmpty, isEqual} from 'lodash/fp';
|
||||
import {treeBuilderService} from '../services/tree-builder.service';
|
||||
import {isArrayOrderNotImportant} from '../jsonToDiffConvertor';
|
||||
@@ -8,20 +8,21 @@ import {ExperimentParams, TreeNode, TreeNodeMetadata} from '../shared/experiment
|
||||
import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';
|
||||
import {activeLoader, addMessage, deactivateLoader} from '../../core/actions/layout.actions';
|
||||
import {ChangeDetectorRef, OnDestroy, QueryList, ViewChildren, Directive} from '@angular/core';
|
||||
import {ExperimentCompareDetailsBase} from '../../../features/experiments-compare/experiments-compare-details.base';
|
||||
import {ExperimentCompareDetailsBase} from '~/features/experiments-compare/experiments-compare-details.base';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {IExperimentInfoState} from '../../../features/experiments/reducers/experiment-info.reducer';
|
||||
import {IExperimentInfoState} from '~/features/experiments/reducers/experiment-info.reducer';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {FlatTreeControl} from '@angular/cdk/tree';
|
||||
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
|
||||
import {selectRouterParams} from '../../core/reducers/router-reducer';
|
||||
import {distinctUntilChanged, filter, map, tap} from 'rxjs/operators';
|
||||
import {selectHideIdenticalFields, selectRefreshing} from '../reducers';
|
||||
import {selectHideIdenticalFields} from '../reducers';
|
||||
import {refetchExperimentRequested} from '../actions/compare-header.actions';
|
||||
import {RENAME_MAP} from '../experiments-compare.constants';
|
||||
import {selectHasDataFeature} from '../../../core/reducers/users.reducer';
|
||||
import {selectHasDataFeature} from '~/core/reducers/users.reducer';
|
||||
import {ListRange} from '@angular/cdk/collections';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
export type NextDiffDirectionEnum = 'down' | 'up';
|
||||
|
||||
@@ -78,10 +79,13 @@ export abstract class ExperimentCompareBase extends ExperimentCompareDetailsBase
|
||||
@ViewChildren('virtualScrollRef') virtualScrollRef: QueryList<CdkVirtualScrollViewport>;
|
||||
|
||||
|
||||
constructor(public router: Router,
|
||||
public store: Store<IExperimentInfoState>,
|
||||
public changeDetection: ChangeDetectorRef,
|
||||
public activeRoute: ActivatedRoute) {
|
||||
constructor (
|
||||
public router: Router,
|
||||
public store: Store<IExperimentInfoState>,
|
||||
public changeDetection: ChangeDetectorRef,
|
||||
public activeRoute: ActivatedRoute,
|
||||
public refresh: RefreshService
|
||||
) {
|
||||
super();
|
||||
this.hasDataFeature$ = this.store.pipe(select(selectHasDataFeature));
|
||||
|
||||
@@ -111,9 +115,9 @@ export abstract class ExperimentCompareBase extends ExperimentCompareDetailsBase
|
||||
this.find(this.searchText);
|
||||
});
|
||||
|
||||
this.refreshingSubscription = this.store.pipe(select(selectRefreshing))
|
||||
.pipe(filter(({refreshing}) => refreshing))
|
||||
.subscribe(({autoRefresh}) => this.store.dispatch(refetchExperimentRequested({autoRefresh})));
|
||||
this.refreshingSubscription = this.refresh.tick
|
||||
.pipe(filter(auto => auto !== null))
|
||||
.subscribe(auto => this.store.dispatch(refetchExperimentRequested({autoRefresh: auto})));
|
||||
|
||||
this.hideIdenticalFieldsSub.add(this.hasDataFeature$.subscribe(hasData => this.hasDataFeature = hasData));
|
||||
|
||||
@@ -395,7 +399,7 @@ export abstract class ExperimentCompareBase extends ExperimentCompareDetailsBase
|
||||
let tooltip;
|
||||
if (this.compareTabPage === 'hyper-params') {
|
||||
path[path.length - 1] = path[path.length - 1].trim();
|
||||
const hypeParamObject = get(path.join('.'), this.originalExperiments[comparedExperiment.id]);
|
||||
const hypeParamObject = get(path.join('.'), this.originalExperiments[comparedExperiment?.id]);
|
||||
if (hypeParamObject && has('name', hypeParamObject) && has('value', hypeParamObject) && hypeParamObject.type !== 'legacy') {
|
||||
tooltip = (hypeParamObject.type ? `Type: ${hypeParamObject.type}\n` : '') + (hypeParamObject.description || '');
|
||||
}
|
||||
@@ -415,7 +419,7 @@ export abstract class ExperimentCompareBase extends ExperimentCompareDetailsBase
|
||||
};
|
||||
};
|
||||
|
||||
metaDataTransformer = (data, key, path, extraParams): TreeNodeMetadata => {
|
||||
metaDataTransformer = (): TreeNodeMetadata => {
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {ExperimentCompareBase} from '../experiment-compare-base';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {IExperimentInfoState} from '../../../../features/experiments/reducers/experiment-info.reducer';
|
||||
import {ConfigurationItem} from '../../../../business-logic/model/tasks/configurationItem';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-experiment-compare-details',
|
||||
@@ -49,9 +50,10 @@ export class ExperimentCompareDetailsComponent extends ExperimentCompareBase imp
|
||||
public store: Store<IExperimentInfoState>,
|
||||
public changeDetection: ChangeDetectorRef,
|
||||
public activeRoute: ActivatedRoute,
|
||||
private cdr: ChangeDetectorRef
|
||||
private cdr: ChangeDetectorRef,
|
||||
public refresh: RefreshService
|
||||
) {
|
||||
super(router, store, changeDetection, activeRoute);
|
||||
super(router, store, changeDetection, activeRoute, refresh);
|
||||
}
|
||||
|
||||
experiments$ = this.store.pipe(select(selectExperimentsDetails));
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
|
||||
.metrics-container {
|
||||
padding: 20px 12px 0 32px;
|
||||
padding: 20px 0 0 32px;
|
||||
// dashboard-search icon
|
||||
i.fa {
|
||||
position: absolute;
|
||||
|
||||
@@ -6,12 +6,13 @@ import {distinctUntilChanged, filter, map} from 'rxjs/operators';
|
||||
import {selectRouterParams} from '@common/core/reducers/router-reducer';
|
||||
import {get, has} from 'lodash/fp';
|
||||
import {SetExperimentSettings, SetSelectedExperiments} from '../../actions/experiments-compare-charts.actions';
|
||||
import {selectRefreshing, selectScalarsGraphHyperParams, selectScalarsGraphMetrics, selectScalarsGraphShowIdenticalHyperParams, selectScalarsGraphTasks, selectMetricValueType, selectSelectedSettingsHyperParams, selectSelectedSettingsMetric} from '../../reducers';
|
||||
import {selectScalarsGraphHyperParams, selectScalarsGraphMetrics, selectScalarsGraphShowIdenticalHyperParams, selectScalarsGraphTasks, selectMetricValueType, selectSelectedSettingsHyperParams, selectSelectedSettingsMetric} from '../../reducers';
|
||||
import {getExperimentsHyperParams, setShowIdenticalHyperParams, setvalueType} from '../../actions/experiments-compare-scalars-graph.actions';
|
||||
import {GroupedHyperParams, MetricOption, MetricValueType, SelectedMetric, VariantOption} from '../../reducers/experiments-compare-charts.reducer';
|
||||
import {MatRadioChange} from '@angular/material/radio';
|
||||
import {selectPlotlyReady} from '@common/core/reducers/view.reducer';
|
||||
import {ExtFrame} from '@common/shared/experiment-graphs/single-graph/plotly-graph-base';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
|
||||
export const _filter = (opt: VariantOption[], value: string): VariantOption[] => {
|
||||
@@ -37,7 +38,6 @@ export class ExperimentCompareHyperParamsGraphComponent implements OnInit, OnDes
|
||||
public metrics$: Observable<MetricOption[]>;
|
||||
public selectedHyperParams$: Observable<string[]>;
|
||||
private selectedMetric$: Observable<SelectedMetric>;
|
||||
private selectRefreshing$: Observable<{ refreshing: boolean, autoRefresh: boolean }>;
|
||||
public experiments$: Observable<any[]>;
|
||||
|
||||
public graphs: { [key: string]: ExtFrame };
|
||||
@@ -66,13 +66,12 @@ export class ExperimentCompareHyperParamsGraphComponent implements OnInit, OnDes
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private store: Store<IExperimentInfoState>) {
|
||||
constructor(private store: Store<IExperimentInfoState>, private refresh: RefreshService) {
|
||||
this.metrics$ = this.store.pipe(select(selectScalarsGraphMetrics));
|
||||
this.hyperParams$ = this.store.pipe(select(selectScalarsGraphHyperParams));
|
||||
this.selectedHyperParams$ = this.store.pipe(select(selectSelectedSettingsHyperParams));
|
||||
this.selectedMetric$ = this.store.pipe(select(selectSelectedSettingsMetric));
|
||||
this.selectShowIdenticalHyperParams$ = this.store.pipe(select(selectScalarsGraphShowIdenticalHyperParams));
|
||||
this.selectRefreshing$ = this.store.select(selectRefreshing);
|
||||
this.experiments$ = this.store.pipe(select(selectScalarsGraphTasks));
|
||||
this.metricValueType$ = this.store.pipe(select(selectMetricValueType));
|
||||
|
||||
@@ -128,8 +127,11 @@ export class ExperimentCompareHyperParamsGraphComponent implements OnInit, OnDes
|
||||
this.store.dispatch(getExperimentsHyperParams({experimentsIds: this.taskIds}));
|
||||
});
|
||||
|
||||
this.refreshingSubscription = this.selectRefreshing$.pipe(filter(({refreshing}) => refreshing)).subscribe(({autoRefresh}) =>
|
||||
this.store.dispatch(getExperimentsHyperParams({experimentsIds: this.taskIds, autoRefresh})));
|
||||
this.refreshingSubscription = this.refresh.tick
|
||||
.pipe(filter(auto => auto !== null))
|
||||
.subscribe(autoRefresh =>
|
||||
this.store.dispatch(getExperimentsHyperParams({experimentsIds: this.taskIds, autoRefresh}))
|
||||
);
|
||||
|
||||
this.listOpen = true;
|
||||
window.setTimeout(() => {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {IExperimentInfoState} from '../../../../features/experiments/reducers/experiment-info.reducer';
|
||||
import {distinctUntilChanged, filter, map, tap} from 'rxjs/operators';
|
||||
import {selectRouterParams} from '../../../core/reducers/router-reducer';
|
||||
import {IExperimentInfoState} from '~/features/experiments/reducers/experiment-info.reducer';
|
||||
import {distinctUntilChanged, filter, map} from 'rxjs/operators';
|
||||
import {isEqual} from 'lodash/fp';
|
||||
import {mergeMultiMetrics, mergeMultiMetricsGroupedVariant} from '../../../tasks/tasks.utils';
|
||||
import {scrollToElement} from '../../../shared/utils/shared-utils';
|
||||
import {mergeMultiMetrics, mergeMultiMetricsGroupedVariant} from '@common/tasks/tasks.utils';
|
||||
import {scrollToElement} from '@common/shared/utils/shared-utils';
|
||||
import {GetMultiScalarCharts, ResetExperimentMetrics, SetExperimentMetricsSearchTerm, SetExperimentSettings, SetSelectedExperiments} from '../../actions/experiments-compare-charts.actions';
|
||||
import {selectCompareSelectedSettingsGroupBy, selectCompareSelectedSettingsSmoothWeight, selectCompareSelectedSettingsxAxisType, selectCompareTasksScalarCharts, selectExperimentMetricsSearchTerm, selectRefreshing, selectSelectedExperimentSettings, selectSelectedSettingsHiddenScalar, selectShowScalarsOptions} from '../../reducers';
|
||||
import {ScalarKeyEnum} from '../../../../business-logic/model/events/scalarKeyEnum';
|
||||
import {selectCompareSelectedSettingsGroupBy, selectCompareSelectedSettingsSmoothWeight, selectCompareSelectedSettingsxAxisType, selectCompareTasksScalarCharts, selectExperimentMetricsSearchTerm, selectSelectedExperimentSettings, selectSelectedSettingsHiddenScalar} from '../../reducers';
|
||||
import {ScalarKeyEnum} from '~/business-logic/model/events/scalarKeyEnum';
|
||||
import {toggleShowScalarOptions} from '../../actions/compare-header.actions';
|
||||
import {GroupByCharts} from '../../../experiments/reducers/common-experiment-output.reducer';
|
||||
import {GroupedList} from '../../../shared/ui-components/data/selectable-grouped-filter-list/selectable-grouped-filter-list.component';
|
||||
import {ExtFrame} from '../../../shared/experiment-graphs/single-graph/plotly-graph-base';
|
||||
import {GroupByCharts} from '@common/experiments/reducers/common-experiment-output.reducer';
|
||||
import {GroupedList} from '@common/shared/ui-components/data/selectable-grouped-filter-list/selectable-grouped-filter-list.component';
|
||||
import {ExtFrame} from '@common/shared/experiment-graphs/single-graph/plotly-graph-base';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
import {selectRouterParams} from '@common/core/reducers/router-reducer';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -26,8 +27,6 @@ export class ExperimentCompareScalarChartsComponent implements OnInit, OnDestroy
|
||||
public smoothWeight$: Observable<number>;
|
||||
public xAxisType$: Observable<ScalarKeyEnum>;
|
||||
public groupBy$: Observable<GroupByCharts>;
|
||||
public showSettingsBar$: Observable<boolean>;
|
||||
private selectRefreshing$: Observable<{ refreshing: boolean, autoRefresh: boolean }>;
|
||||
private routerParams$: Observable<any>;
|
||||
public metrics$: Observable<any>;
|
||||
public experimentSettings$: Observable<any>;
|
||||
@@ -45,8 +44,6 @@ export class ExperimentCompareScalarChartsComponent implements OnInit, OnDestroy
|
||||
public selectedGraph: string = null;
|
||||
private taskIds: Array<string>;
|
||||
public graphs: { [key: string]: ExtFrame[] };
|
||||
public refreshDisabled = false;
|
||||
public showSettingsBar: boolean = false;
|
||||
public groupBy: GroupByCharts;
|
||||
private metrics: GroupedList;
|
||||
|
||||
@@ -61,14 +58,16 @@ export class ExperimentCompareScalarChartsComponent implements OnInit, OnDestroy
|
||||
}
|
||||
];
|
||||
|
||||
constructor(private store: Store<IExperimentInfoState>, private changeDetection: ChangeDetectorRef) {
|
||||
constructor(
|
||||
private store: Store<IExperimentInfoState>,
|
||||
private changeDetection: ChangeDetectorRef,
|
||||
private refresh: RefreshService
|
||||
) {
|
||||
this.listOfHidden = this.store.pipe(select(selectSelectedSettingsHiddenScalar));
|
||||
this.searchTerm$ = this.store.pipe(select(selectExperimentMetricsSearchTerm));
|
||||
this.showSettingsBar$ = this.store.pipe(select(selectShowScalarsOptions));
|
||||
this.smoothWeight$ = this.store.select(selectCompareSelectedSettingsSmoothWeight);
|
||||
this.xAxisType$ = this.store.select(selectCompareSelectedSettingsxAxisType);
|
||||
this.groupBy$ = this.store.select(selectCompareSelectedSettingsGroupBy);
|
||||
this.selectRefreshing$ = this.store.select(selectRefreshing);
|
||||
this.metrics$ = this.store.pipe(
|
||||
select(selectCompareTasksScalarCharts),
|
||||
filter(metrics => !!metrics),
|
||||
@@ -84,13 +83,12 @@ export class ExperimentCompareScalarChartsComponent implements OnInit, OnDestroy
|
||||
this.routerParams$ = this.store.pipe(
|
||||
select(selectRouterParams),
|
||||
filter(params => !!params.ids),
|
||||
distinctUntilChanged(),
|
||||
tap(() => this.refreshDisabled = true)
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
this.xAxisSub = this.xAxisType$
|
||||
.pipe(filter((axis) => !!axis))
|
||||
.subscribe((axis) => this.store.dispatch(new GetMultiScalarCharts({taskIds: this.taskIds, cached: true})));
|
||||
.pipe(filter(axis => !!axis && this.taskIds?.length > 0))
|
||||
.subscribe(() => this.store.dispatch(new GetMultiScalarCharts({taskIds: this.taskIds, cached: true})));
|
||||
|
||||
this.groupBySub = this.groupBy$
|
||||
.subscribe(groupBy => {
|
||||
@@ -102,7 +100,6 @@ export class ExperimentCompareScalarChartsComponent implements OnInit, OnDestroy
|
||||
ngOnInit() {
|
||||
this.metricsSubscription = this.metrics$
|
||||
.subscribe((metricsWrapped) => {
|
||||
this.refreshDisabled = false;
|
||||
const metrics = metricsWrapped.metrics || {};
|
||||
this.metrics = metrics;
|
||||
this.prepareGraphsAndUpdate(metrics);
|
||||
@@ -123,8 +120,9 @@ export class ExperimentCompareScalarChartsComponent implements OnInit, OnDestroy
|
||||
}
|
||||
});
|
||||
|
||||
this.refreshingSubscription = this.selectRefreshing$.pipe(filter(({refreshing}) => refreshing))
|
||||
.subscribe(({autoRefresh}) => this.store.dispatch(new GetMultiScalarCharts({taskIds: this.taskIds, autoRefresh})));
|
||||
this.refreshingSubscription = this.refresh.tick
|
||||
.pipe(filter(auto => auto !== null && this.graphs !== null))
|
||||
.subscribe(autoRefresh => this.store.dispatch(new GetMultiScalarCharts({taskIds: this.taskIds, autoRefresh})));
|
||||
}
|
||||
|
||||
private prepareGraphsAndUpdate(metrics) {
|
||||
@@ -146,11 +144,11 @@ export class ExperimentCompareScalarChartsComponent implements OnInit, OnDestroy
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.metricsSubscription.unsubscribe();
|
||||
this.settingsSubscription.unsubscribe();
|
||||
this.routerParamsSubscription.unsubscribe();
|
||||
this.xAxisSub.unsubscribe();
|
||||
this.refreshingSubscription.unsubscribe();
|
||||
this.metricsSubscription?.unsubscribe();
|
||||
this.settingsSubscription?.unsubscribe();
|
||||
this.routerParamsSubscription?.unsubscribe();
|
||||
this.xAxisSub?.unsubscribe();
|
||||
this.refreshingSubscription?.unsubscribe();
|
||||
this.resetMetrics();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {selectRouterParams, selectRouterQueryParams} from '../../../core/reducers/router-reducer';
|
||||
import {selectRouterParams, selectRouterQueryParams} from '@common/core/reducers/router-reducer';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {distinctUntilChanged, filter, map, tap} from 'rxjs/operators';
|
||||
import {get, has} from 'lodash/fp';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import * as metricsValuesActions from '../../actions/experiments-compare-metrics-values.actions';
|
||||
import {selectCompareMetricsValuesExperiments, selectCompareMetricsValuesSortConfig, selectRefreshing} from '../../reducers';
|
||||
import {selectCompareMetricsValuesExperiments, selectCompareMetricsValuesSortConfig} from '../../reducers';
|
||||
import {Router} from '@angular/router';
|
||||
import {addMessage} from '../../../core/actions/layout.actions';
|
||||
import {addMessage} from '@common/core/actions/layout.actions';
|
||||
import {TreeNode} from '../../shared/experiments-compare-details.model';
|
||||
import {createDiffObjectScalars, getAllKeysEmptyObject} from '../../jsonToDiffConvertor';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
interface ValueMode {
|
||||
key: string;
|
||||
@@ -39,7 +40,6 @@ const VALUE_MODES: { [mode: string]: ValueMode } = {
|
||||
export class ExperimentCompareMetricValuesComponent implements OnInit, OnDestroy {
|
||||
public sortOrder$: Observable<any>;
|
||||
public comparedTasks$: Observable<any>;
|
||||
private selectRefreshing$: Observable<{ refreshing: boolean; autoRefresh: boolean }>;
|
||||
|
||||
private comparedTasksSubscription: Subscription;
|
||||
private refreshingSubscription: Subscription;
|
||||
@@ -54,10 +54,14 @@ export class ExperimentCompareMetricValuesComponent implements OnInit, OnDestroy
|
||||
private taskIds: string;
|
||||
public valuesMode: ValueMode;
|
||||
|
||||
constructor(private router: Router, public store: Store<any>, private changeDetection: ChangeDetectorRef) {
|
||||
constructor(
|
||||
private router: Router,
|
||||
public store: Store<any>,
|
||||
private changeDetection: ChangeDetectorRef,
|
||||
private refresh: RefreshService
|
||||
) {
|
||||
this.comparedTasks$ = this.store.pipe(select(selectCompareMetricsValuesExperiments));
|
||||
this.sortOrder$ = this.store.pipe(select(selectCompareMetricsValuesSortConfig));
|
||||
this.selectRefreshing$ = this.store.select(selectRefreshing);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -92,8 +96,9 @@ export class ExperimentCompareMetricValuesComponent implements OnInit, OnDestroy
|
||||
this.changeDetection.detectChanges();
|
||||
});
|
||||
|
||||
this.refreshingSubscription = this.selectRefreshing$.pipe(filter(({refreshing}) => refreshing))
|
||||
.subscribe(({autoRefresh}) => this.store.dispatch(
|
||||
this.refreshingSubscription = this.refresh.tick
|
||||
.pipe(filter(auto => auto !== null))
|
||||
.subscribe((autoRefresh) => this.store.dispatch(
|
||||
new metricsValuesActions.GetComparedExperimentsMetricsValues({taskIds: this.taskIds.split(','), autoRefresh})
|
||||
));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {ExperimentCompareBase} from '../experiment-compare-base';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {IExperimentInfoState} from '../../../../features/experiments/reducers/experiment-info.reducer';
|
||||
import {experimentListUpdated, setExperiments} from '../../actions/experiments-compare-params.actions';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-experiment-compare-params',
|
||||
@@ -23,11 +24,14 @@ import {experimentListUpdated, setExperiments} from '../../actions/experiments-c
|
||||
export class ExperimentCompareParamsComponent extends ExperimentCompareBase implements OnInit {
|
||||
public showEllipsis: boolean = true;
|
||||
|
||||
constructor(public router: Router,
|
||||
public store: Store<IExperimentInfoState>,
|
||||
public changeDetection: ChangeDetectorRef,
|
||||
public activeRoute: ActivatedRoute) {
|
||||
super(router, store, changeDetection, activeRoute);
|
||||
constructor(
|
||||
public router: Router,
|
||||
public store: Store<IExperimentInfoState>,
|
||||
public changeDetection: ChangeDetectorRef,
|
||||
public activeRoute: ActivatedRoute,
|
||||
public refresh: RefreshService
|
||||
) {
|
||||
super(router, store, changeDetection, activeRoute, refresh);
|
||||
}
|
||||
|
||||
experiments$ = this.store.pipe(select(selectExperimentsParams));
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
.graphs-container {
|
||||
padding: 0 !important;
|
||||
width: calc(100% - 420px);
|
||||
width: calc(100% - 344px);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {SelectableListItem} from '../../../shared/ui-components/data/selectable-list/selectable-list.model';
|
||||
import {SelectableListItem} from '@common/shared/ui-components/data/selectable-list/selectable-list.model';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {IExperimentInfoState} from '../../../../features/experiments/reducers/experiment-info.reducer';
|
||||
import {IExperimentInfoState} from '~/features/experiments/reducers/experiment-info.reducer';
|
||||
import {distinctUntilChanged, filter, map, tap} from 'rxjs/operators';
|
||||
import {selectRouterParams} from '../../../core/reducers/router-reducer';
|
||||
import {convertMultiPlots, prepareMultiPlots, sortMetricsList} from '../../../tasks/tasks.utils';
|
||||
import {selectRouterParams} from '@common/core/reducers/router-reducer';
|
||||
import {convertMultiPlots, prepareMultiPlots, sortMetricsList} from '@common/tasks/tasks.utils';
|
||||
import {isEqual} from 'lodash/fp';
|
||||
import {scrollToElement} from '../../../shared/utils/shared-utils';
|
||||
import {scrollToElement} from '@common/shared/utils/shared-utils';
|
||||
import {GetMultiPlotCharts, ResetExperimentMetrics, SetExperimentMetricsSearchTerm, SetExperimentSettings, SetSelectedExperiments} from '../../actions/experiments-compare-charts.actions';
|
||||
import {selectCompareTasksPlotCharts, selectExperimentMetricsSearchTerm, selectRefreshing, selectSelectedExperimentSettings, selectSelectedSettingsHiddenPlot} from '../../reducers';
|
||||
import {ExtFrame} from '../../../shared/experiment-graphs/single-graph/plotly-graph-base';
|
||||
import {selectCompareTasksPlotCharts, selectExperimentMetricsSearchTerm, selectSelectedExperimentSettings, selectSelectedSettingsHiddenPlot} from '../../reducers';
|
||||
import {ExtFrame} from '@common/shared/experiment-graphs/single-graph/plotly-graph-base';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-experiment-compare-plots',
|
||||
@@ -24,7 +25,6 @@ export class ExperimentComparePlotsComponent implements OnInit, OnDestroy {
|
||||
public plots$: Observable<any>;
|
||||
public experimentSettings$: Observable<any>;
|
||||
public searchTerm$: Observable<string>;
|
||||
private selectRefreshing$: Observable<{refreshing: boolean, autoRefresh: boolean}>;
|
||||
|
||||
private plotsSubscription: Subscription;
|
||||
private settingsSubscription: Subscription;
|
||||
@@ -33,16 +33,14 @@ export class ExperimentComparePlotsComponent implements OnInit, OnDestroy {
|
||||
|
||||
public graphList: SelectableListItem[] = [];
|
||||
public selectedGraph: string = null;
|
||||
private experimentId: string;
|
||||
private taskIds: Array<string>;
|
||||
public graphs: { [key: string]: ExtFrame[] };
|
||||
public refreshDisabled: boolean;
|
||||
|
||||
|
||||
constructor(private store: Store<IExperimentInfoState>, private changeDetection: ChangeDetectorRef) {
|
||||
constructor(private store: Store<IExperimentInfoState>, private changeDetection: ChangeDetectorRef, private refresh: RefreshService) {
|
||||
this.listOfHidden = this.store.pipe(select(selectSelectedSettingsHiddenPlot));
|
||||
this.searchTerm$ = this.store.pipe(select(selectExperimentMetricsSearchTerm));
|
||||
this.selectRefreshing$ = this.store.select(selectRefreshing);
|
||||
this.plots$ = this.store.pipe(
|
||||
select(selectCompareTasksPlotCharts),
|
||||
filter(metrics => !!metrics),
|
||||
@@ -92,8 +90,11 @@ export class ExperimentComparePlotsComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
});
|
||||
|
||||
this.refreshingSubscription = this.selectRefreshing$.pipe(filter(({refreshing}) => refreshing))
|
||||
.subscribe(({autoRefresh}) => this.store.dispatch(new GetMultiPlotCharts({taskIds: this.taskIds, autoRefresh})));
|
||||
this.refreshingSubscription = this.refresh.tick
|
||||
.pipe(filter(auto => auto !== null))
|
||||
.subscribe(autoRefresh =>
|
||||
this.store.dispatch(new GetMultiPlotCharts({taskIds: this.taskIds, autoRefresh}))
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
class="align-self-stretch"
|
||||
[selectionReachedLimit]="reachedCompareLimit"
|
||||
[selectionMode]="null"
|
||||
[disableContextMenu]="true"
|
||||
[reorderableColumns]="false"
|
||||
[minimizedView]="false"
|
||||
[colsOrder]="(tableColsOrder$ | async)"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {ChangeDetectorRef, Component, ElementRef, EventEmitter, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {
|
||||
compareAddDialogTableSortChanged,
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
setShowSearchExperimentsForCompare
|
||||
} from '../../actions/compare-header.actions';
|
||||
import {
|
||||
selectCompareAddTableSortFields,
|
||||
selectExperimentsForCompareSearchTerm,
|
||||
selectSelectedExperimentsForCompareAdd
|
||||
} from '../../reducers';
|
||||
@@ -52,6 +51,7 @@ import {ProjectsGetTaskParentsResponseParents} from '~/business-logic/model/proj
|
||||
import {FilterMetadata} from 'primeng/api/filtermetadata';
|
||||
import {INITIAL_EXPERIMENT_TABLE_COLS} from '@common/experiments/experiment.consts';
|
||||
import {EntityTypeEnum} from '~/shared/constants/non-common-consts';
|
||||
import {ExperimentsTableComponent} from '@common/experiments/dumb/experiments-table/experiments-table.component';
|
||||
|
||||
export const allowAddExperiment$ = (selectRouterParams$: Observable<Params>) => selectRouterParams$.pipe(
|
||||
distinctUntilKeyChanged('ids'),
|
||||
@@ -73,7 +73,6 @@ export class SelectExperimentsForCompareComponent implements OnInit, OnDestroy {
|
||||
public selectedExperimentsIds: string[] = [];
|
||||
private paramsSubscription: Subscription;
|
||||
public searchTerm$: Observable<string>;
|
||||
@ViewChild('searchExperiments', {static: true}) searchExperiments;
|
||||
public allowAddExperiment$: Observable<boolean>;
|
||||
public tableColsOrder$: Observable<string[]>;
|
||||
public tableSortOrder$: Observable<TableSortOrderEnum>;
|
||||
@@ -98,6 +97,8 @@ export class SelectExperimentsForCompareComponent implements OnInit, OnDestroy {
|
||||
public reachedCompareLimit: boolean;
|
||||
private _resizedCols = {} as { [colId: string]: string };
|
||||
private resizedCols$ = new BehaviorSubject<{[colId: string]: string }>(this._resizedCols);
|
||||
@ViewChild('searchExperiments', {static: true}) searchExperiments;
|
||||
@ViewChild(ExperimentsTableComponent) table: ExperimentsTableComponent;
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
@@ -183,6 +184,7 @@ export class SelectExperimentsForCompareComponent implements OnInit, OnDestroy {
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
window.setTimeout(() => this.table.table.rowRightClick = new EventEmitter());
|
||||
this.paramsSubscription = this.store.pipe(
|
||||
select(selectRouterParams),
|
||||
map(params => [params && params['ids'], get('projectId', params)]),
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
sm-tag-list {
|
||||
margin-top: 4px;
|
||||
height: 18px;
|
||||
max-width: 530px;
|
||||
}
|
||||
|
||||
.general-info {
|
||||
@@ -60,10 +61,10 @@
|
||||
}
|
||||
|
||||
.bw.i-completed.icon {
|
||||
background-image: url('/app/webapp-common/assets/icons/completed-dark.svg') !important;
|
||||
background-image: url('../../../assets/icons/completed-dark.svg') !important;
|
||||
}
|
||||
.bw.i-stopped.icon {
|
||||
background-image: url('/app/webapp-common/assets/icons/completed-dark.svg') !important;
|
||||
background-image: url('../../../assets/icons/completed-dark.svg') !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,6 @@
|
||||
<div class="settings">
|
||||
|
||||
<sm-refresh-button
|
||||
[autoRefreshState]="autoRefreshState$ | async"
|
||||
[allowAutoRefresh]="true"
|
||||
(refreshList)="refreshList($event)"
|
||||
(setAutoRefresh)="setAutoRefresh($event)"
|
||||
class="light-theme"
|
||||
>
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
} from '@angular/core';
|
||||
import {MatSelectChange} from '@angular/material/select';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectHideIdenticalFields, selectRefreshing} from '../../reducers';
|
||||
import {interval, Observable, Subscription} from 'rxjs';
|
||||
import {selectHideIdenticalFields} from '../../reducers';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {
|
||||
refreshIfNeeded,
|
||||
setHideIdenticalFields,
|
||||
@@ -19,19 +19,18 @@ import {
|
||||
toggleShowScalarOptions
|
||||
} from '../../actions/compare-header.actions';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {selectRouterParams, selectRouterQueryParams, selectRouterUrl} from '../../../core/reducers/router-reducer';
|
||||
import {selectRouterParams, selectRouterQueryParams, selectRouterUrl} from '@common/core/reducers/router-reducer';
|
||||
import {get} from 'lodash/fp';
|
||||
import {selectAppVisible, selectAutoRefresh} from '../../../core/reducers/view.reducer';
|
||||
import {setAutoRefresh} from '../../../core/actions/layout.actions';
|
||||
import {AUTO_REFRESH_INTERVAL} from '../../../../app.constants';
|
||||
import {filter, withLatestFrom} from 'rxjs/operators';
|
||||
import {setAutoRefresh} from '@common/core/actions/layout.actions';
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {MatSlideToggleChange} from '@angular/material/slide-toggle';
|
||||
import {compareLimitations} from '../../../shared/entity-page/footer-items/compare-footer-item';
|
||||
import {compareLimitations} from '@common/shared/entity-page/footer-items/compare-footer-item';
|
||||
import {
|
||||
allowAddExperiment$,
|
||||
SelectExperimentsForCompareComponent
|
||||
} from '../../containers/select-experiments-for-compare/select-experiments-for-compare.component';
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-experiment-compare-header',
|
||||
@@ -44,34 +43,32 @@ export class ExperimentCompareHeaderComponent implements OnInit, OnDestroy {
|
||||
private queryParamsSubscription: Subscription;
|
||||
public selectHideIdenticalFields$: Observable<boolean>;
|
||||
|
||||
public selectRefreshing$: Observable<{ refreshing: boolean; autoRefresh: boolean }>;
|
||||
public viewMode: string;
|
||||
public currentPage: string;
|
||||
public queryParamsViewMode: string;
|
||||
public compareLimitations = compareLimitations;
|
||||
public autoRefreshState$: Observable<boolean>;
|
||||
public allowAddExperiment$: Observable<boolean>;
|
||||
private autorRefreshSub: Subscription;
|
||||
private showMenuSub: Subscription;
|
||||
|
||||
private isAppVisible$: Observable<boolean>;
|
||||
|
||||
@Output() selectionChanged = new EventEmitter<string[]>();
|
||||
|
||||
constructor(private store: Store<any>, private route: ActivatedRoute, private router: Router, private cdr: ChangeDetectorRef, private dialog: MatDialog) {
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private dialog: MatDialog,
|
||||
private refresh: RefreshService
|
||||
) {
|
||||
this.selectHideIdenticalFields$ = this.store.select(selectHideIdenticalFields);
|
||||
this.selectRefreshing$ = this.store.select(selectRefreshing);
|
||||
this.autoRefreshState$ = this.store.select(selectAutoRefresh);
|
||||
this.isAppVisible$ = this.store.select(selectAppVisible);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.autorRefreshSub = interval(AUTO_REFRESH_INTERVAL).pipe(
|
||||
withLatestFrom(this.autoRefreshState$, this.isAppVisible$),
|
||||
filter(([, autoRefreshState, isAppVisible]) => autoRefreshState && isAppVisible)
|
||||
).subscribe(() => {
|
||||
this.refreshList(true);
|
||||
});
|
||||
this.autorRefreshSub = this.refresh.tick
|
||||
.pipe(filter(auto => auto === null))
|
||||
.subscribe(() => this.store.dispatch(refreshIfNeeded({payload: true, autoRefresh: true})));
|
||||
|
||||
this.routerSubscription = this.store.select(selectRouterUrl).subscribe(() => {
|
||||
this.currentPage = get('snapshot.firstChild.url[0].path', this.route);
|
||||
this.viewMode = get('snapshot.firstChild.url[1].path', this.route);
|
||||
@@ -90,10 +87,6 @@ export class ExperimentCompareHeaderComponent implements OnInit, OnDestroy {
|
||||
this.showMenuSub?.unsubscribe();
|
||||
}
|
||||
|
||||
refresh({isAutoRefresh}: { isAutoRefresh: boolean }) {
|
||||
this.store.dispatch(refreshIfNeeded({payload: true, autoRefresh: isAutoRefresh}));
|
||||
}
|
||||
|
||||
changeView($event: MatSelectChange) {
|
||||
const queryParam = {[this.currentPage]: $event.value};
|
||||
const page = $event.value.replace(/.*_/, '');
|
||||
@@ -121,10 +114,6 @@ export class ExperimentCompareHeaderComponent implements OnInit, OnDestroy {
|
||||
this.store.dispatch(toggleShowScalarOptions());
|
||||
}
|
||||
|
||||
refreshList(isAutorefresh: boolean) {
|
||||
this.store.dispatch(refreshIfNeeded({payload: true, autoRefresh: isAutorefresh}));
|
||||
}
|
||||
|
||||
setAutoRefresh($event: boolean) {
|
||||
this.store.dispatch(setAutoRefresh({autoRefresh: $event}));
|
||||
}
|
||||
|
||||
@@ -292,8 +292,8 @@ export class ParallelCoordinatesGraphComponent extends PlotlyGraphBase implement
|
||||
graph.selectAll('.axis-title').text((d: any) => this.wrap(d.key)).append('title').text(d => (d as any).key);
|
||||
graph.selectAll('.axis .tick text').text((d: string) => this.wrap(d)).append('title').text((d: string) => d);
|
||||
graph.selectAll('.axis .tick text').style('pointer-events', 'auto');
|
||||
graph.selectAll('.tick').on('mouseover', (d, i, e) => {
|
||||
const tick = e[i] as unknown as SVGGElement;
|
||||
graph.selectAll('.tick').on('mouseover', (event: MouseEvent) => {
|
||||
const tick = event.currentTarget as SVGGElement;
|
||||
const axis = tick.parentNode as SVGGElement;
|
||||
if (axis && axis.lastChild !== tick) {
|
||||
axis.removeChild(tick);
|
||||
|
||||
@@ -5,14 +5,13 @@ import {IExperimentCompareChartsState} from '../reducers/experiments-compare-cha
|
||||
import * as chartActions from '../actions/experiments-compare-charts.actions';
|
||||
import {GetMultiPlotCharts, GetMultiScalarCharts} from '../actions/experiments-compare-charts.actions';
|
||||
import {activeLoader, deactivateLoader, setServerError} from '../../core/actions/layout.actions';
|
||||
import {catchError, debounceTime, mergeMap, map, withLatestFrom} from 'rxjs/operators';
|
||||
import {catchError, debounceTime, mergeMap, map, withLatestFrom, filter} from 'rxjs/operators';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
import {ApiAuthService} from '~/business-logic/api-services/auth.service';
|
||||
import {BlTasksService} from '~/business-logic/services/tasks.service';
|
||||
import {ApiEventsService} from '~/business-logic/api-services/events.service';
|
||||
import {requestFailed} from '../../core/actions/http.actions';
|
||||
import {selectCompareHistogramCacheAxisType, selectCompareSelectedSettingsxAxisType} from '../reducers';
|
||||
import {setRefreshing} from '../actions/compare-header.actions';
|
||||
import {ScalarKeyEnum} from '~/business-logic/model/events/scalarKeyEnum';
|
||||
|
||||
|
||||
@@ -25,6 +24,7 @@ export class ExperimentsCompareChartsEffects {
|
||||
|
||||
activeLoader = createEffect(() => this.actions$.pipe(
|
||||
ofType(chartActions.GET_MULTI_SCALAR_CHARTS, chartActions.GET_MULTI_PLOT_CHARTS),
|
||||
filter(action => !(action as any).payload?.autoRefresh),
|
||||
map(action => activeLoader(action.type))
|
||||
));
|
||||
|
||||
@@ -37,7 +37,7 @@ export class ExperimentsCompareChartsEffects {
|
||||
[ScalarKeyEnum.IsoTime, ScalarKeyEnum.Timestamp].includes(axisType) &&
|
||||
prevAxisType !== axisType
|
||||
) {
|
||||
return [setRefreshing({payload: false}), deactivateLoader(action.type)];
|
||||
return [deactivateLoader(action.type)];
|
||||
}
|
||||
return this.eventsApi.eventsMultiTaskScalarMetricsIterHistogram({
|
||||
tasks: action.payload.taskIds,
|
||||
@@ -46,11 +46,10 @@ export class ExperimentsCompareChartsEffects {
|
||||
mergeMap(res => [
|
||||
// also here
|
||||
new chartActions.SetExperimentHistogram(res, axisType),
|
||||
setRefreshing({payload: false}),
|
||||
deactivateLoader(action.type)]
|
||||
),
|
||||
catchError(error => [
|
||||
requestFailed(error), deactivateLoader(action.type), setRefreshing({payload: false}),
|
||||
requestFailed(error), deactivateLoader(action.type),
|
||||
setServerError(error, null, 'Failed to get Scalar Charts', action.payload.autoRefresh)
|
||||
])
|
||||
);
|
||||
@@ -66,10 +65,9 @@ export class ExperimentsCompareChartsEffects {
|
||||
map(res => res.plots),
|
||||
mergeMap(res => [
|
||||
new chartActions.SetExperimentPlots(res),
|
||||
setRefreshing({payload: false}),
|
||||
deactivateLoader(action.type)]),
|
||||
catchError(error => [
|
||||
requestFailed(error), deactivateLoader(action.type), setRefreshing({payload: false}),
|
||||
requestFailed(error), deactivateLoader(action.type),
|
||||
setServerError(error, null, 'Failed to get Plot Charts', action.payload.autoRefresh)
|
||||
])
|
||||
)
|
||||
|
||||
@@ -3,17 +3,17 @@ import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {activeLoader, deactivateLoader, setServerError} from '../../core/actions/layout.actions';
|
||||
import {catchError, mergeMap, map, switchMap, withLatestFrom} from 'rxjs/operators';
|
||||
import {ApiTasksService} from '../../../business-logic/api-services/tasks.service';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
import {ExperimentDetailsReverterService} from '../services/experiment-details-reverter.service';
|
||||
import {requestFailed} from '../../core/actions/http.actions';
|
||||
import {selectExperimentIdsDetails, selectExperimentsDetails} from '../reducers';
|
||||
import {Observable, of} from 'rxjs';
|
||||
import {IExperimentDetail} from '../../../features/experiments-compare/experiments-compare-models';
|
||||
import {REFETCH_EXPERIMENT_REQUESTED, refetchExperimentRequested, setRefreshing} from '../actions/compare-header.actions';
|
||||
import {IExperimentDetail} from '~/features/experiments-compare/experiments-compare-models';
|
||||
import {REFETCH_EXPERIMENT_REQUESTED, refetchExperimentRequested} from '../actions/compare-header.actions';
|
||||
import {ExperimentCompareDetailsState} from '../reducers/experiments-compare-details.reducer';
|
||||
import {experimentListUpdated, setExperiments} from '../actions/experiments-compare-details.actions';
|
||||
import {getCompareDetailsOnlyFields} from '../../../features/experiments-compare/experiments-compare-consts';
|
||||
import {selectHasDataFeature} from '../../../core/reducers/users.reducer';
|
||||
import {getCompareDetailsOnlyFields} from '~/features/experiments-compare/experiments-compare-consts';
|
||||
import {selectHasDataFeature} from '~/core/reducers/users.reducer';
|
||||
|
||||
@Injectable()
|
||||
export class ExperimentsCompareDetailsEffects {
|
||||
@@ -61,13 +61,11 @@ export class ExperimentsCompareDetailsEffects {
|
||||
switchMap(([action, newExperimentIds, hasDataFeature]) => this.fetchExperimentDetails$(newExperimentIds, hasDataFeature).pipe(
|
||||
mergeMap(experiments => [
|
||||
deactivateLoader(action.type),
|
||||
setRefreshing({payload: false}),
|
||||
setExperiments({experiments})
|
||||
]),
|
||||
catchError(error => [
|
||||
requestFailed(error),
|
||||
deactivateLoader(action.type),
|
||||
setRefreshing({payload: false}),
|
||||
setServerError(
|
||||
error, null,
|
||||
'The attempt to retrieve your experiment data failed. Refresh your browser and try again.',
|
||||
|
||||
@@ -3,11 +3,10 @@ import {Actions, Effect, ofType} from '@ngrx/effects';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {IExperimentCompareMetricsValuesState} from '../reducers/experiments-compare-metrics-values.reducer';
|
||||
import * as metricsValuesActions from '../actions/experiments-compare-metrics-values.actions';
|
||||
import {activeLoader, deactivateLoader, setServerError} from '../../../webapp-common/core/actions/layout.actions';
|
||||
import {activeLoader, deactivateLoader, setServerError} from '@common/core/actions/layout.actions';
|
||||
import {catchError, mergeMap, map} from 'rxjs/operators';
|
||||
import {requestFailed} from '../../core/actions/http.actions';
|
||||
import {ApiTasksService} from '../../../business-logic/api-services/tasks.service';
|
||||
import {setRefreshing} from '../actions/compare-header.actions';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@@ -30,10 +29,9 @@ export class ExperimentsCompareMetricsValuesEffects {
|
||||
map(res => action.payload.taskIds.map(id => res.tasks.find(ex => ex.id === id))),
|
||||
mergeMap(experiments => [
|
||||
new metricsValuesActions.SetComparedExperiments(experiments),
|
||||
setRefreshing({payload: false}),
|
||||
deactivateLoader(action.type)]),
|
||||
catchError(error => [
|
||||
requestFailed(error), deactivateLoader(action.type), setRefreshing({payload: false}),
|
||||
requestFailed(error), deactivateLoader(action.type),
|
||||
setServerError(error, null, 'Failed to get Compared Experiments', action.payload.autoRefresh)
|
||||
])
|
||||
)
|
||||
|
||||
@@ -4,14 +4,14 @@ import {select, Store} from '@ngrx/store';
|
||||
import * as paramsActions from '../actions/experiments-compare-params.actions';
|
||||
import {activeLoader, deactivateLoader, setServerError} from '../../core/actions/layout.actions';
|
||||
import {catchError, map, mergeMap, switchMap, withLatestFrom} from 'rxjs/operators';
|
||||
import {ApiTasksService} from '../../../business-logic/api-services/tasks.service';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
import {ExperimentParamsReverterService} from '../services/experiment-params-reverter.service';
|
||||
import {requestFailed} from '../../core/actions/http.actions';
|
||||
import {selectExperimentIdsParams, selectExperimentsParams} from '../reducers';
|
||||
import {Observable, of} from 'rxjs';
|
||||
import {COMPARE_PARAMS_ONLY_FIELDS} from '../../../features/experiments-compare/experiments-compare-consts';
|
||||
import {IExperimentDetail} from '../../../features/experiments-compare/experiments-compare-models';
|
||||
import {REFETCH_EXPERIMENT_REQUESTED, refetchExperimentRequested, setRefreshing} from '../actions/compare-header.actions';
|
||||
import {COMPARE_PARAMS_ONLY_FIELDS} from '~/features/experiments-compare/experiments-compare-consts';
|
||||
import {IExperimentDetail} from '~/features/experiments-compare/experiments-compare-models';
|
||||
import {REFETCH_EXPERIMENT_REQUESTED, refetchExperimentRequested} from '../actions/compare-header.actions';
|
||||
import {ExperimentCompareParamsState} from '../reducers/experiments-compare-params.reducer';
|
||||
import {setExperiments} from '../actions/experiments-compare-params.actions';
|
||||
import {ExperimentDetailBase, ExperimentParams} from '../shared/experiments-compare-details.model';
|
||||
@@ -65,13 +65,11 @@ export class ExperimentsCompareParamsEffects {
|
||||
this.fetchExperimentParams$(newExperimentIds).pipe(
|
||||
mergeMap(experiments => [
|
||||
deactivateLoader(action.type),
|
||||
setRefreshing({payload: false}),
|
||||
setExperiments({experiments : experiments as ExperimentParams[]})
|
||||
]),
|
||||
catchError(error => [
|
||||
requestFailed(error),
|
||||
deactivateLoader(action.type),
|
||||
setRefreshing({payload: false}),
|
||||
setServerError(
|
||||
error, null,
|
||||
'The attempt to retrieve your experiment data failed. Refresh your browser and try again.',
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, Effect, ofType} from '@ngrx/effects';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {IExperimentCompareMetricsValuesState} from '../reducers/experiments-compare-metrics-values.reducer';
|
||||
import {activeLoader, deactivateLoader, setServerError} from '../../core/actions/layout.actions';
|
||||
import {catchError, mergeMap, map} from 'rxjs/operators';
|
||||
import {requestFailed} from '../../core/actions/http.actions';
|
||||
import {ApiTasksService} from '../../../business-logic/api-services/tasks.service';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
import {getExperimentsHyperParams, setHyperParamsList, setMetricsList, setTasks} from '../actions/experiments-compare-scalars-graph.actions';
|
||||
import {setRefreshing} from '../actions/compare-header.actions';
|
||||
import {GroupedHyperParams, HyperParams} from '../reducers/experiments-compare-charts.reducer';
|
||||
|
||||
@Injectable()
|
||||
export class ExperimentsCompareScalarsGraphEffects {
|
||||
|
||||
constructor(private actions$: Actions, private store: Store<IExperimentCompareMetricsValuesState>, public tasksApiService: ApiTasksService) {
|
||||
constructor(private actions$: Actions, public tasksApiService: ApiTasksService) {
|
||||
}
|
||||
|
||||
@Effect()
|
||||
@@ -38,11 +35,10 @@ export class ExperimentsCompareScalarsGraphEffects {
|
||||
setTasks({tasks: res.tasks}),
|
||||
setMetricsList({metricsList: metricsList}),
|
||||
setHyperParamsList({hyperParams: paramsHasDiffs}),
|
||||
setRefreshing({payload: false}),
|
||||
deactivateLoader(action.type)];
|
||||
}),
|
||||
catchError(error => [
|
||||
requestFailed(error), deactivateLoader(action.type), setRefreshing({payload: false}),
|
||||
requestFailed(error), deactivateLoader(action.type),
|
||||
setServerError(error, null, 'Failed to get Compared Experiments')
|
||||
])
|
||||
)
|
||||
@@ -65,14 +61,13 @@ export class ExperimentsCompareScalarsGraphEffects {
|
||||
}
|
||||
}
|
||||
}
|
||||
const metricsList = Object.keys(metrics).sort((a, b) => a.toLowerCase() > b.toLowerCase() ? 1 : -1).map(metricName => ({
|
||||
return Object.keys(metrics).sort((a, b) => a.toLowerCase() > b.toLowerCase() ? 1 : -1).map(metricName => ({
|
||||
metricName,
|
||||
variants: Object.keys(metrics[metricName]).sort().map(variant => ({
|
||||
name: variant,
|
||||
value: metrics[metricName][variant]
|
||||
}))
|
||||
}));
|
||||
return metricsList;
|
||||
}
|
||||
|
||||
private getParametersHasDiffs(tasks): GroupedHyperParams {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, Effect, ofType} from '@ngrx/effects';
|
||||
import {debounceTime, filter, map, mergeMap, switchMap, withLatestFrom} from 'rxjs/operators';
|
||||
import {activeLoader, deactivateLoader} from '../../core/actions/layout.actions';
|
||||
import {ApiTasksService} from '../../../business-logic/api-services/tasks.service';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
import {
|
||||
compareAddDialogSetTableSort,
|
||||
compareAddDialogTableSortChanged,
|
||||
@@ -10,13 +10,11 @@ import {
|
||||
getSelectedExperimentsForCompareAddDialog,
|
||||
refreshIfNeeded,
|
||||
setExperimentsUpdateTime,
|
||||
setRefreshing,
|
||||
setSearchExperimentsForCompareResults
|
||||
setSearchExperimentsForCompareResults,
|
||||
} from '../actions/compare-header.actions';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {flatten, get, isEmpty} from 'lodash/fp';
|
||||
import {selectCompareAddTableSortFields, selectExperimentsUpdateTime} from '../reducers';
|
||||
import {EmptyAction} from '../../../app.constants';
|
||||
import {selectExperimentsUpdateTime} from '../reducers';
|
||||
import {selectRouterParams} from '../../core/reducers/router-reducer';
|
||||
import {selectAppVisible} from '../../core/reducers/view.reducer';
|
||||
import {MINIMUM_ONLY_FIELDS} from '../../experiments/experiment.consts';
|
||||
@@ -24,12 +22,17 @@ import * as exSelectors from '../../experiments/reducers';
|
||||
import {selectExperimentsMetricsCols, selectExperimentsTableCols, selectTableSortFields} from '../../experiments/reducers';
|
||||
import {selectSelectedProjectId} from '../../core/reducers/projects.reducer';
|
||||
import {addMultipleSortColumns} from '../../shared/utils/shared-utils';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
@Injectable()
|
||||
export class SelectCompareHeaderEffects {
|
||||
|
||||
constructor(private actions: Actions, public experimentsApi: ApiTasksService, private store: Store<any>) {
|
||||
}
|
||||
constructor(
|
||||
private actions: Actions,
|
||||
public experimentsApi: ApiTasksService,
|
||||
private store: Store,
|
||||
private refresh: RefreshService
|
||||
) {}
|
||||
|
||||
@Effect()
|
||||
activeLoader = this.actions.pipe(
|
||||
@@ -48,25 +51,24 @@ export class SelectCompareHeaderEffects {
|
||||
filter(([, isAppVisible, ,]) => isAppVisible),
|
||||
switchMap(([action, , experimentsIds, experimentsUpdateTime]) =>
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
this.experimentsApi.tasksGetAllEx({id: experimentsIds, only_fields: ['last_update']}).pipe(
|
||||
this.experimentsApi.tasksGetAllEx({id: experimentsIds, only_fields: ['last_change']}).pipe(
|
||||
mergeMap((res) => {
|
||||
const updatedExperimentsUpdateTime: { [key: string]: Date } = {};
|
||||
res.tasks.forEach(task => {
|
||||
updatedExperimentsUpdateTime[task.id] = task.last_update;
|
||||
updatedExperimentsUpdateTime[task.id] = task.last_change;
|
||||
});
|
||||
const experimentsWhereUpdated = !!(experimentsIds.find((id) =>
|
||||
(new Date(experimentsUpdateTime[id]).getTime()) < new Date(updatedExperimentsUpdateTime[id]).getTime()
|
||||
));
|
||||
const shouldUpdate = ((!action.payload) || (!action.autoRefresh) || experimentsWhereUpdated) && !(isEmpty(experimentsUpdateTime));
|
||||
const experimentsWhereUpdated = experimentsIds.some(id =>
|
||||
new Date(experimentsUpdateTime[id]) < new Date(updatedExperimentsUpdateTime[id])
|
||||
);
|
||||
if (((!action.payload) || (!action.autoRefresh) || experimentsWhereUpdated) && !(isEmpty(experimentsUpdateTime))) {
|
||||
this.refresh.trigger(true);
|
||||
}
|
||||
return [
|
||||
setExperimentsUpdateTime({payload: updatedExperimentsUpdateTime}),
|
||||
(shouldUpdate) ? setRefreshing({
|
||||
payload: action.payload,
|
||||
autoRefresh: action.autoRefresh
|
||||
}) : new EmptyAction()];
|
||||
setExperimentsUpdateTime({payload: updatedExperimentsUpdateTime})];
|
||||
}))
|
||||
)
|
||||
);
|
||||
|
||||
tableSortChange = createEffect(() => this.actions.pipe(
|
||||
ofType(compareAddDialogTableSortChanged),
|
||||
withLatestFrom(
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { EXPERIMENTS_TABLE_COL_FIELDS } from "../../features/experiments/shared/experiments.const";
|
||||
import {ColHeaderFilterTypeEnum, ColHeaderTypeEnum, ISmCol} from "../shared/ui-components/data/table/table.consts";
|
||||
|
||||
export const RENAME_MAP = {
|
||||
'network_design': 'Network Design',
|
||||
'uncommitted_changes': 'Uncommitted Changes',
|
||||
@@ -49,138 +46,15 @@ export const COMPARE_DETAILS_ONLY_FIELDS_BASE = [
|
||||
'last_iteration',
|
||||
'configuration'
|
||||
];
|
||||
export const ADD_EXPERIMENT_FOR_COMPARE_TABLE_COLS: ISmCol[] = [
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.SELECTED,
|
||||
sortable : false,
|
||||
filterable : false,
|
||||
headerType : ColHeaderTypeEnum.checkBox,
|
||||
header : '',
|
||||
hidden : false,
|
||||
static : true,
|
||||
bodyStyleClass : 'selected-col-body type-col',
|
||||
headerStyleClass: 'selected-col-header',
|
||||
style : {width: '50px'},
|
||||
disableDrag : true,
|
||||
disablePointerEvents: true
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.TYPE,
|
||||
headerType : ColHeaderTypeEnum.sortFilter,
|
||||
sortable : false,
|
||||
filterable : false,
|
||||
static : true,
|
||||
header : 'TYPE',
|
||||
bodyStyleClass: 'type-col',
|
||||
style : {width: '115px'},
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.NAME,
|
||||
headerType : ColHeaderTypeEnum.sortFilter,
|
||||
sortable : false,
|
||||
static : true,
|
||||
header : 'NAME',
|
||||
style : {width: '400px'},
|
||||
},
|
||||
{
|
||||
id: EXPERIMENTS_TABLE_COL_FIELDS.TAGS,
|
||||
headerType : ColHeaderTypeEnum.sortFilter,
|
||||
filterable: false,
|
||||
searchableFilter: false,
|
||||
sortable: false,
|
||||
static: true,
|
||||
header: 'TAGS',
|
||||
style: {width: '300px'},
|
||||
andFilter: true
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.STATUS,
|
||||
headerType : ColHeaderTypeEnum.sortFilter,
|
||||
filterable : false,
|
||||
static : false,
|
||||
header : 'STATUS',
|
||||
style : {width: '115px'},
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.PROJECT,
|
||||
headerType : ColHeaderTypeEnum.title,
|
||||
static : true,
|
||||
header : 'PROJECT',
|
||||
style : {width: '150px'},
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.USER,
|
||||
getter : 'user.name',
|
||||
headerType : ColHeaderTypeEnum.sortFilter,
|
||||
searchableFilter: false,
|
||||
filterable : false,
|
||||
sortable : false,
|
||||
static : true,
|
||||
header : 'USER',
|
||||
style : {width: '115px'},
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.STARTED,
|
||||
headerType : ColHeaderTypeEnum.sortFilter,
|
||||
sortable : false,
|
||||
filterType : ColHeaderFilterTypeEnum.durationDate,
|
||||
filterable: false,
|
||||
searchableFilter: false,
|
||||
static : true,
|
||||
header : 'STARTED',
|
||||
style : {width: '150px'},
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.LAST_UPDATE,
|
||||
headerType : ColHeaderTypeEnum.sortFilter,
|
||||
sortable : false,
|
||||
filterType : ColHeaderFilterTypeEnum.durationDate,
|
||||
filterable: false,
|
||||
searchableFilter: false,
|
||||
static : true,
|
||||
header : 'UPDATED',
|
||||
label : 'Updated',
|
||||
style : {width: '150px'},
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.LAST_ITERATION,
|
||||
headerType : ColHeaderTypeEnum.sortFilter,
|
||||
sortable : false,
|
||||
filterType : ColHeaderFilterTypeEnum.durationNumeric,
|
||||
filterable : false,
|
||||
searchableFilter: false,
|
||||
static : true,
|
||||
header : 'ITERATION',
|
||||
label : 'Iterations:',
|
||||
style : {width: '115px'},
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.COMMENT,
|
||||
headerType: ColHeaderTypeEnum.sortFilter,
|
||||
sortable : false,
|
||||
header : 'DESCRIPTION',
|
||||
style : {width: '300px'}
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.ACTIVE_DURATION,
|
||||
headerType: ColHeaderTypeEnum.sortFilter,
|
||||
sortable : false,
|
||||
filterable: false,
|
||||
filterType : ColHeaderFilterTypeEnum.duration,
|
||||
searchableFilter: false,
|
||||
static : true,
|
||||
bodyStyleClass: 'type-col',
|
||||
header : 'RUN TIME',
|
||||
style : {width: '150px'}
|
||||
},
|
||||
{
|
||||
id : EXPERIMENTS_TABLE_COL_FIELDS.PARENT,
|
||||
getter : [EXPERIMENTS_TABLE_COL_FIELDS.PARENT, 'parent.project.id', 'parent.project.name'],
|
||||
headerType: ColHeaderTypeEnum.sortFilter,
|
||||
searchableFilter: false,
|
||||
filterable : false,
|
||||
sortable : false,
|
||||
header : 'PARENT TASK',
|
||||
style : {width: '200px'}
|
||||
}
|
||||
|
||||
export const COMPARE_DEBUG_IMAGES_ONLY_FIELDS = [
|
||||
'id',
|
||||
'name',
|
||||
'type',
|
||||
'status',
|
||||
'last_update',
|
||||
'project.name',
|
||||
'tags',
|
||||
'published',
|
||||
'last_iteration',
|
||||
];
|
||||
|
||||
@@ -20,7 +20,6 @@ import {ExperimentGraphsModule} from '../shared/experiment-graphs/experiment-gra
|
||||
|
||||
import {DebugImagesModule} from '../debug-images/debug-images.module';
|
||||
import {ExperimentCompareSharedModule} from './shared/experiment-compare-shared.module';
|
||||
import {ExperimentCompareGeneralDataComponent} from './dumbs/experiment-compare-general-data/experiment-compare-general-data.component';
|
||||
import {GetKeyValueArrayPipePipe} from './get-key-value-array-pipe.pipe';
|
||||
import {SelectCompareHeaderEffects} from './effects/select-experiment-for-compare-effects.service';
|
||||
import {SelectExperimentsForCompareComponent} from './containers/select-experiments-for-compare/select-experiments-for-compare.component';
|
||||
@@ -59,7 +58,6 @@ export const compareSyncedKeys = [
|
||||
ExperimentCompareScalarChartsComponent,
|
||||
ExperimentComparePlotsComponent,
|
||||
ExperimentCompareHeaderComponent,
|
||||
ExperimentCompareGeneralDataComponent,
|
||||
GetKeyValueArrayPipePipe,
|
||||
SelectExperimentsForCompareComponent,
|
||||
CompareCardListComponent,
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
setExperimentsUpdateTime,
|
||||
setHideIdenticalFields,
|
||||
setNavigationPreferences,
|
||||
setRefreshing,
|
||||
setSearchExperimentsForCompareResults,
|
||||
setShowSearchExperimentsForCompare,
|
||||
toggleShowScalarOptions
|
||||
@@ -22,7 +21,6 @@ export interface CompareHeaderState {
|
||||
hideIdenticalRows: boolean;
|
||||
viewMode: string;
|
||||
showScalarOptions: boolean;
|
||||
refreshing: boolean;
|
||||
autoRefresh: boolean;
|
||||
navigationPreferences: Params;
|
||||
experimentsUpdateTime: { [key: string]: Date };
|
||||
@@ -38,7 +36,6 @@ export const initialState: CompareHeaderState = {
|
||||
hideIdenticalRows: false,
|
||||
viewMode: 'values',
|
||||
showScalarOptions: false,
|
||||
refreshing: false,
|
||||
autoRefresh: false,
|
||||
navigationPreferences: {},
|
||||
experimentsUpdateTime: {},
|
||||
@@ -55,11 +52,6 @@ const _compareHeader = createReducer(initialState,
|
||||
on(setExperimentsUpdateTime, (state: CompareHeaderState, {payload}) => ({...state, experimentsUpdateTime: payload})),
|
||||
on(setShowSearchExperimentsForCompare, (state: CompareHeaderState, {payload}) => ({...state, showSearch: payload})),
|
||||
on(toggleShowScalarOptions, (state: CompareHeaderState) => ({...state, showScalarOptions: !state.showScalarOptions})),
|
||||
on(setRefreshing, (state: CompareHeaderState, {payload, autoRefresh}) => ({
|
||||
...state,
|
||||
refreshing: payload,
|
||||
autoRefresh
|
||||
})),
|
||||
on(setNavigationPreferences, (state: CompareHeaderState, {navigationPreferences}) => ({
|
||||
...state,
|
||||
navigationPreferences: {...state.navigationPreferences, ...navigationPreferences}
|
||||
|
||||
@@ -53,7 +53,6 @@ export const selectExperimentsForCompareSearchTerm = createSelector(selectCompar
|
||||
export const selectShowAddExperimentsForCompare = createSelector(selectCompareHeader, state => state?.showSearch);
|
||||
export const selectHideIdenticalFields = createSelector(selectCompareHeader, state => state?.hideIdenticalRows);
|
||||
export const selectShowScalarsOptions = createSelector(selectCompareHeader, state => state?.showScalarOptions);
|
||||
export const selectRefreshing = createSelector(selectCompareHeader, state => state ? {refreshing: state.refreshing, autoRefresh: state.autoRefresh} : {refreshing: false, autoRefresh: false});
|
||||
export const selectExperimentsUpdateTime = createSelector(selectCompareHeader, state => state ? state.experimentsUpdateTime : {});
|
||||
export const selectNavigationPreferences = createSelector(selectCompareHeader, state => state ? state.navigationPreferences : {});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Action, createAction, props} from '@ngrx/store';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {IExperimentInfo, ISelectedExperiment} from '~/features/experiments/shared/experiment-info.model';
|
||||
import {ITableExperiment} from '../shared/common-experiment-model.model';
|
||||
import {IExperimentModelInfo, ITableExperiment} from '../shared/common-experiment-model.model';
|
||||
import {ParamsItem} from '~/business-logic/model/tasks/paramsItem';
|
||||
import {ConfigurationItem} from '~/business-logic/model/tasks/configurationItem';
|
||||
import {IExperimentInfoState} from '~/features/experiments/reducers/experiment-info.reducer';
|
||||
@@ -82,10 +82,20 @@ export const setExperimentUncommittedChanges = createAction(
|
||||
props<{ diff: string }>()
|
||||
);
|
||||
|
||||
export const getExperimentArtifacts= createAction(
|
||||
EXPERIMENTS_INFO_PREFIX + '[get artifacts]',
|
||||
props<{ experimentId: string; autoRefresh?: boolean }>()
|
||||
);
|
||||
|
||||
export const setExperimentArtifacts = createAction(
|
||||
EXPERIMENTS_INFO_PREFIX + '[set artifacts]',
|
||||
props<{model: IExperimentModelInfo; experimentId: string}>()
|
||||
);
|
||||
|
||||
export class UpdateExperimentInfoData implements Action {
|
||||
readonly type = UPDATE_EXPERIMENT_INFO_DATA;
|
||||
|
||||
constructor(public payload: { id: ITableExperiment['id']; changes: Partial<IExperimentInfo> }) {
|
||||
constructor(public payload: { id?: ITableExperiment['id']; changes: Partial<IExperimentInfo> }) {
|
||||
}
|
||||
}
|
||||
export const saveExperimentInputModel = createAction(
|
||||
|
||||
@@ -80,6 +80,7 @@ export const experimentSelectionChanged = createAction(
|
||||
props<{experiment: {id?: string}; project?: string}>()
|
||||
);
|
||||
|
||||
|
||||
export const selectAllExperiments = createAction(
|
||||
EXPERIMENTS_PREFIX + ' [select all experiments]',
|
||||
props<{filtered: boolean}>()
|
||||
@@ -104,6 +105,7 @@ export const setUsers = createAction(
|
||||
props<{ users: User[] }>()
|
||||
);
|
||||
|
||||
|
||||
export const setParents = createAction(
|
||||
EXPERIMENTS_PREFIX + '[set project experiment parents]',
|
||||
props<{ parents: ProjectsGetTaskParentsResponseParents[]}>()
|
||||
@@ -232,3 +234,7 @@ export const setSelectedExperimentsDisableAvailable = createAction(
|
||||
EXPERIMENTS_PREFIX + 'setSelectedExperimentsDisableAvailable',
|
||||
props<{ selectedExperimentsDisableAvailable: Record<string, CountAvailableAndIsDisableSelectedFiltered> }>()
|
||||
);
|
||||
export const setTableMode = createAction(
|
||||
EXPERIMENTS_PREFIX + '[set table view mode]',
|
||||
props<{mode: 'info' | 'table'}>()
|
||||
)
|
||||
|
||||
@@ -117,7 +117,7 @@ import { GetVariantWithoutRoundPipe } from './dumb/experiments-table/hyper-param
|
||||
MatProgressSpinnerModule,
|
||||
SharedModule,
|
||||
ExperimentOutputLogModule,
|
||||
MatRadioModule,
|
||||
MatRadioModule
|
||||
],
|
||||
providers: [ExperimentTableCardComponent, NoUnderscorePipe, TitleCasePipe]
|
||||
})
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import {Component, OnDestroy} from '@angular/core';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectBackdropActive} from '../../../core/reducers/view.reducer';
|
||||
import {selectBackdropActive} from '@common/core/reducers/view.reducer';
|
||||
import {combineLatest, Observable, Subscription} from 'rxjs';
|
||||
import {IExperimentInfoState} from '../../../../features/experiments/reducers/experiment-info.reducer';
|
||||
import {selectExperimentModelInfoData} from '../../reducers';
|
||||
import {selectExperimentInfoData, selectIsExperimentEditable} from '../../../../features/experiments/reducers';
|
||||
import {IExperimentInfo} from '../../../../features/experiments/shared/experiment-info.model';
|
||||
import {selectRouterConfig, selectRouterParams} from '../../../core/reducers/router-reducer';
|
||||
import {distinctUntilChanged, filter, map} from 'rxjs/operators';
|
||||
import {get, getOr} from 'lodash/fp';
|
||||
import {IExperimentInfoState} from '~/features/experiments/reducers/experiment-info.reducer';
|
||||
import {selectCurrentArtifactExperimentId, selectExperimentModelInfoData} from '../../reducers';
|
||||
import {
|
||||
selectExperimentInfoData,
|
||||
selectIsExperimentEditable
|
||||
} from '~/features/experiments/reducers';
|
||||
import {IExperimentInfo} from '~/features/experiments/shared/experiment-info.model';
|
||||
import {selectRouterConfig, selectRouterParams} from '@common/core/reducers/router-reducer';
|
||||
import {debounceTime, distinctUntilChanged, filter, map} from 'rxjs/operators';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {IExperimentModelInfo} from '../../shared/common-experiment-model.model';
|
||||
import {
|
||||
getExperimentArtifacts,
|
||||
setExperimentArtifacts
|
||||
} from '@common/experiments/actions/common-experiments-info.actions';
|
||||
import {selectSelectedProject} from '@common/core/reducers/projects.reducer';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-experiment-info-artifacts-model',
|
||||
@@ -22,52 +29,66 @@ export class ExperimentInfoArtifactsComponent implements OnDestroy {
|
||||
public modelInfo$: Observable<IExperimentModelInfo>;
|
||||
public ExperimentInfo$: Observable<IExperimentInfo>;
|
||||
public activeSection: any;
|
||||
private selectedRouterConfigSubs: Subscription;
|
||||
public selectedId$: Observable<string>;
|
||||
private artifactSubscription: Subscription;
|
||||
private onOutputModel$: Observable<boolean>;
|
||||
private onInputModel$: Observable<boolean>;
|
||||
private experimentKey$: Observable<string>;
|
||||
public routerConfig$: Observable<string[]>;
|
||||
public editable$: Observable<boolean>;
|
||||
public minimized: boolean;
|
||||
private previousTarget: string;
|
||||
private sub = new Subscription();
|
||||
|
||||
constructor(private store: Store<IExperimentInfoState>, public router: Router, private route: ActivatedRoute
|
||||
) {
|
||||
this.minimized = getOr(false, 'data.minimized', this.route.snapshot.routeConfig);
|
||||
this.minimized = !!this.route.snapshot?.routeConfig?.data?.minimized;
|
||||
this.backdropActive$ = this.store.select(selectBackdropActive);
|
||||
this.editable$ = this.store.select(selectIsExperimentEditable);
|
||||
this.modelInfo$ = this.store.select(selectExperimentModelInfoData);
|
||||
this.ExperimentInfo$ = this.store.select(selectExperimentInfoData);
|
||||
this.routerConfig$ = this.store.select(selectRouterConfig);
|
||||
this.selectedRouterConfigSubs = this.store.select(selectRouterConfig)
|
||||
this.selectedId$ = this.store.select(selectRouterParams).pipe(map(params => params?.artifactId || params?.modelId));
|
||||
this.experimentKey$ = this.store.select(selectRouterParams).pipe(map(params => params?.experimentId));
|
||||
|
||||
this.sub.add(this.store.select(selectRouterConfig)
|
||||
.pipe(filter(rc => !!rc))
|
||||
.subscribe((routerConfig: string[]) => {
|
||||
this.activeSection = this.minimized ? routerConfig[5] : routerConfig[6];
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
this.selectedId$ = this.store.select(selectRouterParams)
|
||||
this.sub.add(combineLatest([
|
||||
this.experimentKey$,
|
||||
this.store.select(selectSelectedProject)
|
||||
])
|
||||
.pipe(
|
||||
map(params => get('artifactId', params) || get('modelId', params)),
|
||||
);
|
||||
filter(([id, project]) => !!id && !!project?.id),
|
||||
map(([id]) => id),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
.subscribe(experimentId => {
|
||||
this.store.dispatch(setExperimentArtifacts({model: null, experimentId: null}));
|
||||
this.store.dispatch(getExperimentArtifacts({experimentId}));
|
||||
})
|
||||
);
|
||||
|
||||
this.experimentKey$ = this.store.select(selectRouterParams)
|
||||
.pipe(
|
||||
map(params => get('experimentId', params)),
|
||||
);
|
||||
this.artifactSubscription = combineLatest([this.selectedId$, this.modelInfo$, this.experimentKey$, this.ExperimentInfo$])
|
||||
this.sub.add(combineLatest([
|
||||
this.selectedId$,
|
||||
this.modelInfo$,
|
||||
this.experimentKey$,
|
||||
this.ExperimentInfo$,
|
||||
this.store.select(selectCurrentArtifactExperimentId)
|
||||
])
|
||||
.pipe(
|
||||
debounceTime(0),
|
||||
distinctUntilChanged(),
|
||||
filter(([selectedId, modelInfo, experimentKey, experimentInfo]) =>
|
||||
!!modelInfo && experimentInfo && experimentKey && experimentInfo.id === experimentKey))
|
||||
filter(([, modelInfo, experimentKey, experimentInfo, artifactsExperiment]) =>
|
||||
!!modelInfo && experimentInfo && experimentKey && artifactsExperiment === experimentKey))
|
||||
.subscribe(([selectedId, modelInfo]) => {
|
||||
const onOutputModel = this.route.snapshot.firstChild?.data?.outputModel;
|
||||
const onInputModel = this.route.snapshot.firstChild?.data?.outputModel === false;
|
||||
if (selectedId) {
|
||||
const selectedArtifact = modelInfo.artifacts.find(artifact => artifact.key === selectedId);
|
||||
const selectedInputModel = modelInfo.input.find(model => model.id === selectedId);
|
||||
const selectedOutputModel = modelInfo.output.find(model => model.id === selectedId);
|
||||
const selectedArtifact = modelInfo.artifacts?.find(artifact => artifact.key === selectedId);
|
||||
const selectedInputModel = modelInfo.input?.find(model => model.id === selectedId);
|
||||
const selectedOutputModel = modelInfo.output?.find(model => model.id === selectedId);
|
||||
const onArtifact = !onInputModel && !onOutputModel;
|
||||
if ((onOutputModel && !selectedOutputModel) || (onInputModel && !selectedInputModel) || (onArtifact && !selectedArtifact)) {
|
||||
this.resetSelection(modelInfo);
|
||||
@@ -75,7 +96,8 @@ export class ExperimentInfoArtifactsComponent implements OnDestroy {
|
||||
} else {
|
||||
this.resetSelection(modelInfo);
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private navigateToTarget(target: string) {
|
||||
@@ -101,8 +123,7 @@ export class ExperimentInfoArtifactsComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.selectedRouterConfigSubs.unsubscribe();
|
||||
this.artifactSubscription.unsubscribe();
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ export class ExperimentInfoExecutionComponent implements OnInit, OnDestroy {
|
||||
.pipe(filter( bool => !isUndefined(bool)))
|
||||
.subscribe( setup_shell_script => {
|
||||
if (this.formData.container.setup_shell_script !== setup_shell_script) {
|
||||
smEditableSection.saveSection();
|
||||
this.store.dispatch(commonInfoActions.saveExperimentSection({container: {...this.formData.container, setup_shell_script}}));
|
||||
} else {
|
||||
smEditableSection.cancelClickedEvent();
|
||||
@@ -164,6 +165,7 @@ export class ExperimentInfoExecutionComponent implements OnInit, OnDestroy {
|
||||
if (data === undefined) {
|
||||
this.requirementsSection.cancelClickedEvent();
|
||||
} else {
|
||||
this.requirementsSection.saveSection();
|
||||
this.store.dispatch(commonInfoActions.saveExperimentSection({script: {requirements: {...this.formData.requirements, pip: data}}}));
|
||||
}
|
||||
});
|
||||
@@ -171,11 +173,14 @@ export class ExperimentInfoExecutionComponent implements OnInit, OnDestroy {
|
||||
|
||||
editDiff() {
|
||||
this.openEditJsonDialog({textData: this.formData?.diff, readOnly: false, title: 'EDIT UNCOMMITTED CHANGES', typeJson: false}, this.diffSection)
|
||||
.afterClosed().pipe(take(1)).subscribe((data) => {
|
||||
if (!isUndefined(data)) {
|
||||
this.store.dispatch(commonInfoActions.saveExperimentSection({script: {diff: data}}));
|
||||
}
|
||||
});
|
||||
.afterClosed()
|
||||
.pipe(take(1))
|
||||
.subscribe((data) => {
|
||||
this.diffSection.unsubscribeToEventListener();
|
||||
if (!isUndefined(data)) {
|
||||
this.store.dispatch(commonInfoActions.saveExperimentSection({script: {diff: data}}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clearInstalledPackages() {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
[modelLabels]="modelLabels"
|
||||
[source]="source"
|
||||
[showCreatedExperiment]="!outputMode"
|
||||
(modelSelected)="onModelSelected($event)">
|
||||
(modelSelected)="onModelSelected($event); modelSection.unsubscribeToEventListener()">
|
||||
</sm-experiment-models-form-view>
|
||||
</sm-editable-section>
|
||||
<sm-editable-section class="editable-design"
|
||||
|
||||
@@ -2,19 +2,19 @@ import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectExperimentModelInfoData, selectExperimentUserKnowledge, selectIsExperimentSaving} from '../../reducers';
|
||||
import {IExperimentModelInfo, IModelInfo, IModelInfoSource} from '../../shared/common-experiment-model.model';
|
||||
import {Model} from '../../../../business-logic/model/models/model';
|
||||
import {Model} from '~/business-logic/model/models/model';
|
||||
import {combineLatest, Observable, Subject} from 'rxjs';
|
||||
import {IExperimentInfoState} from '../../../../features/experiments/reducers/experiment-info.reducer';
|
||||
import {experimentSectionsEnum} from '../../../../features/experiments/shared/experiments.const';
|
||||
import {selectIsExperimentEditable, selectSelectedExperiment} from '../../../../features/experiments/reducers';
|
||||
import {IExperimentInfoState} from '~/features/experiments/reducers/experiment-info.reducer';
|
||||
import {experimentSectionsEnum} from '~/features/experiments/shared/experiments.const';
|
||||
import {selectIsExperimentEditable, selectSelectedExperiment} from '~/features/experiments/reducers';
|
||||
import * as commonInfoActions from '../../actions/common-experiments-info.actions';
|
||||
import {ActivateEdit, CancelExperimentEdit, DeactivateEdit} from '../../actions/common-experiments-info.actions';
|
||||
import {ExperimentModelsFormViewComponent} from '../../dumb/experiment-models-form-view/experiment-models-form-view.component';
|
||||
import {getModelDesign} from '../../../tasks/tasks.utils';
|
||||
import {distinctUntilKeyChanged, filter, map, takeUntil} from 'rxjs/operators';
|
||||
import {getModelDesign} from '@common/tasks/tasks.utils';
|
||||
import {distinctUntilChanged, filter, map, takeUntil} from 'rxjs/operators';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {IExperimentInfo} from '../../../../features/experiments/shared/experiment-info.model';
|
||||
import {addMessage} from '../../../core/actions/layout.actions';
|
||||
import {IExperimentInfo} from '~/features/experiments/shared/experiment-info.model';
|
||||
import {addMessage} from '@common/core/actions/layout.actions';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -48,16 +48,15 @@ export class ExperimentInfoModelComponent implements OnInit, OnDestroy {
|
||||
this.saving$ = this.store.select(selectIsExperimentSaving);
|
||||
this.selectedExperiment$ = this.store.select(selectSelectedExperiment);
|
||||
this.routerModelId$ = this.route.params.pipe(
|
||||
takeUntil(this.unsubscribe$),
|
||||
filter(params => params.modelId),
|
||||
distinctUntilKeyChanged('modelId'),
|
||||
map(params => params.modelId));
|
||||
map(params => params?.modelId),
|
||||
filter(params => !!params),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
|
||||
combineLatest([this.routerModelId$, this.modelInfo$]).pipe(takeUntil(this.unsubscribe$))
|
||||
combineLatest([this.routerModelId$, this.modelInfo$])
|
||||
.pipe(takeUntil(this.unsubscribe$))
|
||||
.subscribe(([modelId, formData]) => {
|
||||
this.modelId = modelId;
|
||||
this.outputMode = this.route.snapshot.data?.outputModel;
|
||||
@@ -72,7 +71,6 @@ export class ExperimentInfoModelComponent implements OnInit, OnDestroy {
|
||||
this.inputDesign = (design.value === undefined || design.key === undefined && Object.keys(design.value).length === 0) ? null : design.value;
|
||||
this.modelProjectId = this.model?.project?.id ? this.model.project.id : '*';
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@@ -91,9 +89,9 @@ export class ExperimentInfoModelComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
newModels = [...this.models.map(model => ({model: model.id, name: model.name})), {model: selectedModel.id, name: selectedModel.name}];
|
||||
}
|
||||
this.store.dispatch(new commonInfoActions.SetExperimentInfoData({models: {input: newModels as any}}));
|
||||
this.store.dispatch(new commonInfoActions.UpdateExperimentInfoData({changes: {model: {input: newModels as any}}}));
|
||||
this.store.dispatch(commonInfoActions.saveExperimentSection({models: {input: newModels as any}}));
|
||||
this.router.navigate(['..', selectedModel.id || ''], {relativeTo: this.route, queryParamsHandling: 'preserve'});
|
||||
return this.router.navigate([{modelId: selectedModel.id || ''}], {relativeTo: this.route, replaceUrl: true})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {selectRouterConfig, selectRouterParams} from '../../../core/reducers/router-reducer';
|
||||
import {selectRouterConfig, selectRouterParams} from '@common/core/reducers/router-reducer';
|
||||
import {get, getOr} from 'lodash/fp';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {interval, Observable, Subscription} from 'rxjs';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {distinctUntilChanged, filter, map, tap, withLatestFrom} from 'rxjs/operators';
|
||||
import {Project} from '../../../../business-logic/model/projects/project';
|
||||
import {IExperimentInfo} from '../../../../features/experiments/shared/experiment-info.model';
|
||||
import {ExperimentOutputState} from '../../../../features/experiments/reducers/experiment-output.reducer';
|
||||
import {Project} from '~/business-logic/model/projects/project';
|
||||
import {IExperimentInfo} from '~/features/experiments/shared/experiment-info.model';
|
||||
import {ExperimentOutputState} from '~/features/experiments/reducers/experiment-output.reducer';
|
||||
import {
|
||||
selectExperimentInfoData,
|
||||
selectIsSharedAndNotOwner,
|
||||
selectSelectedExperiment
|
||||
} from '../../../../features/experiments/reducers';
|
||||
} from '~/features/experiments/reducers';
|
||||
import {ResetExperimentMetrics, toggleSettings} from '../../actions/common-experiment-output.actions';
|
||||
import * as infoActions from '../../actions/common-experiments-info.actions';
|
||||
import {selectAppVisible, selectAutoRefresh, selectBackdropActive} from '../../../core/reducers/view.reducer';
|
||||
import {addMessage, setAutoRefresh} from '../../../core/actions/layout.actions';
|
||||
import {AUTO_REFRESH_INTERVAL, MESSAGES_SEVERITY} from '../../../../app.constants';
|
||||
import {selectAppVisible, selectBackdropActive} from '@common/core/reducers/view.reducer';
|
||||
import {addMessage, setAutoRefresh} from '@common/core/actions/layout.actions';
|
||||
import {MESSAGES_SEVERITY} from '~/app.constants';
|
||||
import {selectIsExperimentInEditMode, selectSelectedExperiments} from '../../reducers';
|
||||
import {isReadOnly} from '../../../shared/utils/shared-utils';
|
||||
import {isReadOnly} from '@common/shared/utils/shared-utils';
|
||||
import {ExperimentDetailsUpdated} from '../../actions/common-experiments-info.actions';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-base-experiment-output',
|
||||
@@ -35,7 +36,6 @@ export abstract class BaseExperimentOutputComponent implements OnInit, OnDestroy
|
||||
public currentComponent: string;
|
||||
public currentMetric: string;
|
||||
public minimized: boolean;
|
||||
public autoRefreshState$;
|
||||
private isExperimentInEditMode$: Observable<boolean>;
|
||||
private projectId: Project['id'];
|
||||
public experimentId: string;
|
||||
@@ -44,10 +44,14 @@ export abstract class BaseExperimentOutputComponent implements OnInit, OnDestroy
|
||||
isSharedAndNotOwner$: Observable<boolean>;
|
||||
public isExample: boolean;
|
||||
|
||||
constructor(private store: Store<ExperimentOutputState>, private router: Router, private route: ActivatedRoute) {
|
||||
constructor(
|
||||
private store: Store<ExperimentOutputState>,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private refresh: RefreshService
|
||||
) {
|
||||
this.infoData$ = this.store.select(selectExperimentInfoData);
|
||||
this.isSharedAndNotOwner$ = this.store.select((selectIsSharedAndNotOwner));
|
||||
this.autoRefreshState$ = this.store.select(selectAutoRefresh);
|
||||
this.isExperimentInEditMode$ = this.store.select(selectIsExperimentInEditMode);
|
||||
this.isAppVisible$ = this.store.select(selectAppVisible);
|
||||
this.backdropActive$ = this.store.select(selectBackdropActive);
|
||||
@@ -76,13 +80,20 @@ export abstract class BaseExperimentOutputComponent implements OnInit, OnDestroy
|
||||
this.store.dispatch(new infoActions.GetExperimentInfo(experimentId));
|
||||
})
|
||||
);
|
||||
this.subs.add(interval(AUTO_REFRESH_INTERVAL).pipe(
|
||||
withLatestFrom(this.autoRefreshState$, this.isAppVisible$, this.isExperimentInEditMode$),
|
||||
filter(([, autoRefreshState, isVisible, isExperimentInEditMode]) => isVisible && autoRefreshState && !isExperimentInEditMode &&!this.minimized)
|
||||
).subscribe(() => {
|
||||
this.refresh(true);
|
||||
|
||||
this.subs.add(this.refresh.tick
|
||||
.pipe(
|
||||
withLatestFrom(this.isExperimentInEditMode$),
|
||||
filter(([, isExperimentInEditMode]) => !isExperimentInEditMode && !this.minimized)
|
||||
).subscribe(([auto]) => {
|
||||
if (auto === null) {
|
||||
this.store.dispatch(new infoActions.AutoRefreshExperimentInfo(this.experimentId));
|
||||
} else {
|
||||
this.store.dispatch(new infoActions.GetExperimentInfo(this.experimentId));
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.subs.add(this.store.pipe(select(selectSelectedExperiment),
|
||||
filter(experiment => experiment?.id === this.experimentId))
|
||||
.subscribe(experiment => {
|
||||
@@ -92,33 +103,20 @@ export abstract class BaseExperimentOutputComponent implements OnInit, OnDestroy
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subs.unsubscribe();
|
||||
}
|
||||
|
||||
returnToInfo(experiment) {
|
||||
this.router.navigateByUrl(`projects/${this.projectId}/experiments/${experiment.id}`);
|
||||
}
|
||||
|
||||
setAutoRefresh($event: boolean) {
|
||||
this.store.dispatch(setAutoRefresh({autoRefresh: $event}));
|
||||
}
|
||||
|
||||
refresh(isAutorefresh) {
|
||||
if (isAutorefresh) {
|
||||
this.store.dispatch(new infoActions.AutoRefreshExperimentInfo(this.experimentId));
|
||||
} else {
|
||||
this.store.dispatch(new infoActions.GetExperimentInfo(this.experimentId));
|
||||
}
|
||||
}
|
||||
|
||||
minimizeView() {
|
||||
const part = this.route.firstChild.routeConfig.path;
|
||||
if (['log', 'metrics/scalar', 'metrics/plots', 'debugImages'].includes(part)) {
|
||||
this.router.navigateByUrl(`projects/${this.projectId}/experiments/${this.experimentId}/info-output/${part}`);
|
||||
} else {
|
||||
const parts = this.router ? this.router.url.split('/') : window.location.pathname.split('/');;
|
||||
const parts = this.router ? this.router.url.split('/') : window.location.pathname.split('/');
|
||||
parts.splice(5, 1);
|
||||
this.router.navigateByUrl(parts.join('/'));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<b>Hostname:</b> {{creator}}
|
||||
</span>
|
||||
</div>
|
||||
<button class="btn btn-primary mr-5" (click)="downloadLog()">
|
||||
<button class="btn btn-cml-primary mr-5" (click)="downloadLog()">
|
||||
<i class="fa fa-download"></i>
|
||||
Download full log
|
||||
</button>
|
||||
|
||||
@@ -24,8 +24,8 @@ import {
|
||||
SetLogFilter
|
||||
} from '../../actions/common-experiment-output.actions';
|
||||
import {ExperimentLogInfoComponent} from '../../dumb/experiment-log-info/experiment-log-info.component';
|
||||
import {selectRefreshing} from '@common/experiments-compare/reducers';
|
||||
import {ITableExperiment} from '@common/experiments/shared/common-experiment-model.model';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-experiment-output-log',
|
||||
@@ -52,7 +52,7 @@ export class ExperimentOutputLogComponent implements OnInit, AfterViewInit, OnDe
|
||||
private experiment$ = new BehaviorSubject<ITableExperiment>(null);
|
||||
@ViewChildren(ExperimentLogInfoComponent) private logRefs: QueryList<ExperimentLogInfoComponent>;
|
||||
|
||||
constructor(private store: Store<IExperimentInfoState>, private cdr: ChangeDetectorRef) {
|
||||
constructor(private store: Store<IExperimentInfoState>, private cdr: ChangeDetectorRef, private refresh: RefreshService) {
|
||||
this.log$ = this.store.select(selectExperimentLog);
|
||||
this.logBeginning$ = this.store.select(selectExperimentBeginningOfLog);
|
||||
this.filter$ = this.store.select(selectLogFilter);
|
||||
@@ -77,13 +77,14 @@ export class ExperimentOutputLogComponent implements OnInit, AfterViewInit, OnDe
|
||||
id: this.currExperiment.id,
|
||||
direction: null
|
||||
}));
|
||||
} else if (!this.logRef?.lines?.length || this.logRef?.canRefresh) {
|
||||
this.store.dispatch(getExperimentLog({
|
||||
id: this.currExperiment.id,
|
||||
direction: !this.logRef?.orgLogs ? 'prev' : 'next',
|
||||
from: last(this.logRef?.orgLogs)?.timestamp
|
||||
}));
|
||||
}
|
||||
// else if (!this.logRef?.lines?.length || this.logRef?.canRefresh) {
|
||||
// this.store.dispatch(getExperimentLog({
|
||||
// id: this.currExperiment.id,
|
||||
// direction: !this.logRef?.orgLogs ? 'prev' : 'next',
|
||||
// from: last(this.logRef?.orgLogs)?.timestamp
|
||||
// }));
|
||||
// }
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -102,9 +103,8 @@ export class ExperimentOutputLogComponent implements OnInit, AfterViewInit, OnDe
|
||||
}
|
||||
}));
|
||||
|
||||
this.subs.add(this.store.select(selectRefreshing)
|
||||
.pipe(filter(({refreshing}) => refreshing))
|
||||
.subscribe(({autoRefresh}) => this.store.dispatch(getExperimentLog({
|
||||
this.subs.add(this.refresh.tick
|
||||
.subscribe((autoRefresh) => this.store.dispatch(getExperimentLog({
|
||||
id: this.currExperiment.id,
|
||||
direction: autoRefresh ? 'prev' : 'next',
|
||||
from: last(this.logRef?.orgLogs)?.timestamp
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</sm-selectable-grouped-filter-list>
|
||||
</div>
|
||||
</mat-drawer>
|
||||
<mat-drawer-content>
|
||||
<mat-drawer-content class="overflow-hidden">
|
||||
<sm-graph-settings-bar
|
||||
class="ribbon-setting-bar"
|
||||
[class.showSettings]="showSettingsBar && minimized"
|
||||
@@ -42,8 +42,13 @@
|
||||
></sm-graph-settings-bar>
|
||||
<div class="graphs-container" [class.maximized]="!minimized">
|
||||
<div class="hover-button" *ngIf="minimized">
|
||||
<button class="btn btn-secondary" (click)="drawer.open()">
|
||||
Toggle Graphs
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
(click)="drawer.open()"
|
||||
smTooltip="Toggle Graphs"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<i class="al-icon al-ico-toogle-graph"></i>
|
||||
</button>
|
||||
</div>
|
||||
<sm-experiment-graphs
|
||||
@@ -52,6 +57,7 @@
|
||||
[isGroupGraphs]="false"
|
||||
[metrics]="graphs"
|
||||
[hiddenList]="listOfHidden | async"
|
||||
[smoothWeight]="smoothWeight$ | async"
|
||||
[legendStringLength]="minimized? 14 : undefined"
|
||||
[minimized]="minimized"
|
||||
[xAxisType]="xAxisType$ | async"
|
||||
|
||||
@@ -51,6 +51,11 @@ $list-width: 300px;
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
z-index: 12;
|
||||
button {
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.graphs-container {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
buttonTooltip="Customize table"
|
||||
[showButton]="false"
|
||||
(click)="!disabled && getMetricsToDisplay.emit()"
|
||||
(onMenuClosed)="selectMetricActiveChanged.emit(null)"
|
||||
(menuClosed)="setMode(CustomColumnMode.Standard)"
|
||||
[style.pointer-events]="disabled ? 'none' : 'initial'"
|
||||
>
|
||||
<div *ngIf="!customColumnMode" (click)="$event.stopPropagation()">
|
||||
@@ -22,13 +22,13 @@
|
||||
<div class="add-button metrics-button"
|
||||
smClickStopPropagation
|
||||
[ngClass]="{disabled: !metricVariants.length}"
|
||||
(click)="metricVariants.length && selectMetricActiveChanged.emit(CustomColumnMode.Metrics); $event.stopPropagation()"
|
||||
(click)="$event.stopPropagation(); metricVariants.length && setMode(CustomColumnMode.Metrics)"
|
||||
><i class="al-icon al-ico-add sm mr-1"></i><span class="caption">METRIC</span>
|
||||
</div>
|
||||
<div class="add-button metrics-button"
|
||||
smClickStopPropagation
|
||||
[ngClass]="{disabled: !hasHyperParams}"
|
||||
(click)="hasHyperParams && selectMetricActiveChanged.emit(CustomColumnMode.HyperParams); $event.stopPropagation()"
|
||||
(click)="$event.stopPropagation(); hasHyperParams && setMode(CustomColumnMode.HyperParams)"
|
||||
><i class="al-icon al-ico-add sm mr-1"></i><span class="caption">HYPER PARAMETERS</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -37,13 +37,13 @@
|
||||
<sm-select-metric-for-custom-col *ngIf="customColumnMode === CustomColumnMode.Metrics"
|
||||
[tableCols]="tableCols"
|
||||
[metricVariants]="metricVariants"
|
||||
(goBack)="selectMetricActiveChanged.emit(null)"
|
||||
(goBack)="setMode(CustomColumnMode.Standard)"
|
||||
(selectedMetricToShow)="selectedMetricToShow.emit($event)">
|
||||
</sm-select-metric-for-custom-col>
|
||||
<sm-select-hyper-params-for-custom-col *ngIf="customColumnMode === CustomColumnMode.HyperParams"
|
||||
[tableCols]="tableCols"
|
||||
[hyperParams]="hyperParams"
|
||||
(goBack)="selectMetricActiveChanged.emit(null)"
|
||||
(goBack)="setMode(CustomColumnMode.Standard)"
|
||||
(selectedHyperParamToShow)="selectedHyperParamToShow.emit($event)"
|
||||
(clearSelection)="clearSelection.emit()">
|
||||
</sm-select-hyper-params-for-custom-col>
|
||||
|
||||
@@ -11,13 +11,10 @@ import {MetricValueType} from '@common/experiments-compare/reducers/experiments-
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ExperimentCustomColsMenuComponent {
|
||||
|
||||
|
||||
public hasHyperParams: boolean;
|
||||
private _hyperParams: { [section: string]: any[] };
|
||||
|
||||
@Input() metricVariants;
|
||||
@Input() customColumnMode: CustomColumnMode;
|
||||
@Input() tableCols;
|
||||
@Input() disabled: boolean;
|
||||
|
||||
@@ -31,7 +28,6 @@ export class ExperimentCustomColsMenuComponent {
|
||||
|
||||
@Input() isLoading: boolean;
|
||||
|
||||
@Output() selectMetricActiveChanged = new EventEmitter<CustomColumnMode>();
|
||||
@Output() getMetricsToDisplay = new EventEmitter();
|
||||
@Output() removeColFromList = new EventEmitter<ISmCol['id']>();
|
||||
@Output() selectedTableColsChanged = new EventEmitter<ISmCol>();
|
||||
@@ -43,5 +39,10 @@ export class ExperimentCustomColsMenuComponent {
|
||||
@Output() selectedHyperParamToShow = new EventEmitter<{param: string; addCol: boolean}>();
|
||||
@Output() clearSelection = new EventEmitter();
|
||||
|
||||
customColumnMode = CustomColumnMode.Standard as CustomColumnMode;
|
||||
public CustomColumnMode = CustomColumnMode;
|
||||
|
||||
setMode(mode: CustomColumnMode) {
|
||||
this.customColumnMode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<mat-error *ngIf="parameterKey.invalid && parameterKey?.errors?.smNotAllowedStringsValidator">
|
||||
.(dot) $(dollar) and space are not allowed in parameter key.
|
||||
</mat-error>
|
||||
<mat-error *ngIf="parameterKey.invalid && parameterKey?.errors?.uniqueName">
|
||||
<mat-error *ngIf="!parameterKey?.errors?.required && parameterKey.invalid && parameterKey?.errors?.uniqueName">
|
||||
key already exists
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
<div class="d-flex justify-content-between header-container align-items-center"
|
||||
[ngClass]="{'archive-mode': isArchived}">
|
||||
<div class="d-flex-center">
|
||||
<ng-content></ng-content>
|
||||
<ng-container *ngTemplateOutlet="addButtonTemplate; context: {smallScreen: (isSmallScreen$ | async).matches}">
|
||||
</ng-container>
|
||||
<sm-toggle-archive
|
||||
[class.hide-item]="sharedView"
|
||||
[showArchived]="isArchived"
|
||||
[minimize]="(isSmallScreen$ | async).matches"
|
||||
(toggleArchived)="onIsArchivedChanged($event)"
|
||||
></sm-toggle-archive>
|
||||
<sm-button-toggle
|
||||
[disabled]="!tableMode"
|
||||
class="ml-3"
|
||||
[options]="[{label: 'Table view', value: 'table', icon: 'al-ico-table-view'}, {label: 'Details view', value: 'info', icon: 'al-ico-experiment-view', ripple: true}]"
|
||||
[value]="tableMode"
|
||||
[rippleEffect]="rippleEffect"
|
||||
(valueChanged)="tableModeChanged.emit($event)"></sm-button-toggle>
|
||||
</div>
|
||||
<sm-project-context-navbar
|
||||
*ngIf="showNavbarLinks"
|
||||
@@ -26,20 +35,16 @@
|
||||
[metricVariants]="metricVariants"
|
||||
[hyperParams]="hyperParams"
|
||||
[tableCols]="tableCols"
|
||||
[customColumnMode]="selectMetricActive"
|
||||
[isLoading]="isMetricsLoading"
|
||||
(selectedMetricToShow)="selectedMetricToShow.emit($event)"
|
||||
(selectedHyperParamToShow)="selectedHyperParamToShow.emit($event)"
|
||||
(selectedTableColsChanged)="selectedTableColsChanged.emit($event)"
|
||||
(getMetricsToDisplay)="getMetricsToDisplay.emit($event)"
|
||||
(removeColFromList)="removeColFromList.emit($event)"
|
||||
(selectMetricActiveChanged)="setCustomColumnMode($event)"
|
||||
(clearSelection)="clearSelection.emit()"
|
||||
></sm-experiment-custom-cols-menu>
|
||||
<sm-refresh-button
|
||||
[autoRefreshState]="autoRefreshState"
|
||||
[allowAutoRefresh]="true"
|
||||
(refreshList)="refreshListClicked.emit($event)"
|
||||
(setAutoRefresh)="setAutoRefresh.emit($event)"
|
||||
>
|
||||
</sm-refresh-button>
|
||||
|
||||
@@ -33,10 +33,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
border-color: $blue-600;
|
||||
}
|
||||
|
||||
sm-experiment-custom-cols-menu {
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {Component, EventEmitter, Input, Output, TemplateRef} from '@angular/core';
|
||||
import {MetricVariantResult} from '~/business-logic/model/projects/metricVariantResult';
|
||||
import {CustomColumnMode} from '../../shared/common-experiments.const';
|
||||
import {ISmCol} from '@common/shared/ui-components/data/table/table.consts';
|
||||
import {MetricValueType} from '@common/experiments-compare/reducers/experiments-compare-charts.reducer';
|
||||
import {FilterMetadata} from 'primeng/api/filtermetadata';
|
||||
import {BaseEntityHeaderComponent} from '@common/shared/entity-page/base-entity-header/base-entity-header.component';
|
||||
|
||||
@Component({
|
||||
selector : 'sm-experiment-header',
|
||||
templateUrl: './experiment-header.component.html',
|
||||
styleUrls : ['./experiment-header.component.scss']
|
||||
})
|
||||
export class ExperimentHeaderComponent {
|
||||
public selectMetricActive: CustomColumnMode;
|
||||
export class ExperimentHeaderComponent extends BaseEntityHeaderComponent {
|
||||
private _tableCols: any;
|
||||
|
||||
|
||||
@Input() isArchived: boolean;
|
||||
@Input() metricVariants: Array<MetricVariantResult>;
|
||||
@Input() hyperParams: { [section: string]: any[] };
|
||||
@Input() minimizedView: boolean;
|
||||
@Input() isMetricsLoading: boolean;
|
||||
@Input() autoRefreshState: boolean;
|
||||
@Input() tableFilters: { [s: string]: FilterMetadata };
|
||||
@Input() sharedView: boolean;
|
||||
@Input() showNavbarLinks: boolean;
|
||||
@Input() tableMode: string;
|
||||
@Input() rippleEffect: boolean;
|
||||
@Input() addButtonTemplate: TemplateRef<any>;
|
||||
|
||||
@Input() set tableCols(tableCols) {
|
||||
this._tableCols = tableCols.filter(col => col.header !== '');
|
||||
@@ -43,25 +43,13 @@ export class ExperimentHeaderComponent {
|
||||
valueType: MetricValueType;
|
||||
}>();
|
||||
@Output() selectedHyperParamToShow = new EventEmitter<{param: string; addCol: boolean}>();
|
||||
@Output() refreshListClicked = new EventEmitter<boolean>();
|
||||
@Output() setAutoRefresh = new EventEmitter<boolean>();
|
||||
@Output() clearSelection = new EventEmitter();
|
||||
@Output() clearTableFilters = new EventEmitter<{ [s: string]: FilterMetadata }>();
|
||||
@Output() setAutoRefresh = new EventEmitter<boolean>();
|
||||
@Output() clearSelection = new EventEmitter();
|
||||
@Output() clearTableFilters = new EventEmitter<{ [s: string]: FilterMetadata }>();
|
||||
@Output() tableModeChanged = new EventEmitter<'table' | 'info'>();
|
||||
|
||||
|
||||
onIsArchivedChanged(value: boolean) {
|
||||
this.isArchivedChanged.emit(value);
|
||||
}
|
||||
|
||||
onRefreshListClicked() {
|
||||
this.refreshListClicked.emit(false);
|
||||
}
|
||||
|
||||
setCustomColumnMode(mode: CustomColumnMode) {
|
||||
this.selectMetricActive = mode;
|
||||
}
|
||||
|
||||
newRun() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
class="edit-name"
|
||||
[originalText]="infoData?.name || experiment?.name"
|
||||
[editable]="editable"
|
||||
[minWidth]="250"
|
||||
(textChanged)="onNameChanged($event)"
|
||||
(inlineActiveStateChanged)="editExperimentName($event)"
|
||||
[warning]="isDev && 'Renaming a DEV experiment without changing the code to reflect the rename, will create a new experiment the next time the code is executed.'"
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<mat-spinner *ngIf="fetching" [diameter]="80" [strokeWidth]="8"></mat-spinner>
|
||||
<button *ngIf="!fetching" class="btn btn-primary get-last" [class.at-end]="canRefresh" (click)="getLast()">Jump to end</button>
|
||||
<button *ngIf="!fetching" class="btn btn-cml-primary get-last" [class.at-end]="canRefresh" (click)="getLast()">Jump to end</button>
|
||||
|
||||
@@ -175,20 +175,28 @@ export class ExperimentLogInfoComponent implements OnDestroy, AfterViewInit {
|
||||
|
||||
calcLines() {
|
||||
this.lines = [];
|
||||
this.orgLogs.filter((row) => !this.hasFilter || this.regex.test(row.msg))
|
||||
this.orgLogs
|
||||
.filter((row) => !this.hasFilter || this.regex.test(row?.msg ?? ''))
|
||||
.forEach(logItem => {
|
||||
let first = true;
|
||||
logItem.msg.split('\n').filter(msg => !!msg).forEach((msg: string) => {
|
||||
const hasAnsi = this.hasAnsi(msg);
|
||||
const converted = msg ? (hasAnsi ? this.convert.toHtml(msg) :
|
||||
msg) : '';
|
||||
if (first) {
|
||||
this.lines.push({timestamp: logItem['timestamp'] || logItem['@timestamp'], entry: converted, hasAnsi: hasAnsi});
|
||||
first = false;
|
||||
} else {
|
||||
this.lines.push({entry: converted, hasAnsi: hasAnsi});
|
||||
}
|
||||
});
|
||||
if (!logItem.msg) {
|
||||
this.lines.push({timestamp: logItem['timestamp'] || logItem['@timestamp'], entry: '', hasAnsi: false, separator: true});
|
||||
return;
|
||||
}
|
||||
logItem.msg
|
||||
.split('\n')
|
||||
.filter(msg => !!msg)
|
||||
.forEach((msg: string) => {
|
||||
const hasAnsi = this.hasAnsi(msg);
|
||||
const converted = msg ? (hasAnsi ? this.convert.toHtml(msg) :
|
||||
msg) : '';
|
||||
if (first) {
|
||||
this.lines.push({timestamp: logItem['timestamp'] || logItem['@timestamp'], entry: converted, hasAnsi: hasAnsi});
|
||||
first = false;
|
||||
} else {
|
||||
this.lines.push({entry: converted, hasAnsi: hasAnsi});
|
||||
}
|
||||
});
|
||||
this.lines[this.lines.length - 1].separator = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<script src="../../../models/shared/models-table/models-table.component.ts"></script>
|
||||
<ng-container *ngTemplateOutlet="contextMenuTemplate; context: { $implicit: contextExperiment}"></ng-container>
|
||||
<div class="table-container" [class.card-view]="minimizedView" #tableContainer>
|
||||
<sm-table
|
||||
@@ -11,7 +10,7 @@
|
||||
[columns]="tableCols"
|
||||
[lazyLoading]="true"
|
||||
[minimizedView]="minimizedView"
|
||||
minimizedTableHeader="{{entityType.replace('pipeline ', '')}}S LIST"
|
||||
minimizedTableHeader="{{entityType.replace('ripeline ', '')}}S LIST"
|
||||
[noMoreData]="noMoreExperiments"
|
||||
[selection]="selectedExperiment"
|
||||
[activeContextRow]="contextExperiment"
|
||||
@@ -19,9 +18,10 @@
|
||||
[checkedItems]="selectedExperiments"
|
||||
[keyboardControl]="true"
|
||||
[sortFields]="tableSortFields"
|
||||
(rowSelectionChanged)="onRowSelectionChanged($event)"
|
||||
(rowSelectionChanged)="experimentSelectionChanged.emit($any($event).data)"
|
||||
(rowClicked)="tableRowClicked($event)"
|
||||
(loadMoreClicked)="onLoadMoreClicked()"
|
||||
(onRowRightClick)="onContextMenu($event)"
|
||||
(rowRightClick)="onContextMenu($event)"
|
||||
(colReordered)="columnsReordered.emit($event)"
|
||||
(sortChanged)="onSortChanged($event.isShift, $event.field)"
|
||||
(columnResized)="columnResized.emit($event)"
|
||||
@@ -42,7 +42,7 @@
|
||||
</ng-template>
|
||||
|
||||
<ng-template let-col pTemplate="checkbox">
|
||||
<div class="d-flex">
|
||||
<div class="d-flex align-items-center">
|
||||
<sm-checkbox-control
|
||||
*ngIf="col.headerType === colHeaderTypeEnum.checkBox"
|
||||
class="checkbox-col header"
|
||||
@@ -55,7 +55,7 @@
|
||||
<div class="al-icon al-ico-dropdown-arrow sm drop-down" [matMenuTriggerFor]="selectionMenu"></div>
|
||||
</div>
|
||||
<mat-menu class="light-theme" #selectionMenu="matMenu">
|
||||
<div class="menu-title">Select from project</div>
|
||||
<div *ngIf="entityType !== entityTypes.controller" class="menu-title">Select from project</div>
|
||||
<button mat-menu-item (click)="selectAll()">All</button>
|
||||
<button mat-menu-item (click)="emitSelection([])">None</button>
|
||||
<button mat-menu-item (click)="selectAll(true)">All matching filter</button>
|
||||
@@ -97,6 +97,7 @@
|
||||
class="experiment-name"
|
||||
[class.italic]="isDevelopment(experiment)"
|
||||
matTooltipPosition="above"
|
||||
smShowTooltipIfEllipsis
|
||||
[smTooltip]="experiment.name">
|
||||
{{experiment.name}}
|
||||
</div>
|
||||
@@ -107,11 +108,14 @@
|
||||
<sm-tag-list [tags]="experiment.tags" [sysTags]="getSystemTags(experiment)"></sm-tag-list>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="EXPERIMENTS_TABLE_COL_FIELDS.USER">
|
||||
<span class="ellipsis">{{experiment.user?.name ? experiment.user?.name : 'Unknown User'}}</span>
|
||||
<span class="ellipsis" matTooltipPosition="above"
|
||||
smShowTooltipIfEllipsis
|
||||
[smTooltip]="experiment.user?.name ? experiment.user?.name : 'Unknown User'">{{experiment.user?.name ? experiment.user?.name : 'Unknown User'}}</span>
|
||||
</ng-container>
|
||||
<span *ngSwitchCase="EXPERIMENTS_TABLE_COL_FIELDS.PROJECT"
|
||||
class="ellipsis"
|
||||
matTooltipPosition="above"
|
||||
smShowTooltipIfEllipsis
|
||||
[smTooltip]="experiment.project?.name">
|
||||
{{experiment.project?.name}}
|
||||
</span>
|
||||
@@ -129,7 +133,7 @@
|
||||
<span class="ellipsis">{{experiment.active_duration | duration}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="EXPERIMENTS_TABLE_COL_FIELDS.COMMENT">
|
||||
<span class="ellipsis" matTooltipPosition="above" [smTooltip]="experiment.comment">{{experiment.comment}}</span>
|
||||
<span class="ellipsis" matTooltipPosition="above" smShowTooltipIfEllipsis [smTooltip]="experiment.comment">{{experiment.comment}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="EXPERIMENTS_TABLE_COL_FIELDS.SELECTED">
|
||||
<sm-checkbox-control
|
||||
@@ -150,6 +154,7 @@
|
||||
class="parent-name"
|
||||
*ngIf="experiment?.parent?.id"
|
||||
matTooltipPosition="above"
|
||||
smShowTooltipIfEllipsis
|
||||
[smTooltip]="experiment.parent.project?.name? experiment.parent.project.name + ' / ' + experiment.parent.name : experiment.parent.name"
|
||||
>{{experiment.parent.name}}</span>
|
||||
</ng-container>
|
||||
@@ -165,14 +170,17 @@
|
||||
</ng-template>
|
||||
|
||||
<ng-template let-experiment="rowData" let-selected="selected" let-rowNumber="rowNumber" pTemplate="card">
|
||||
<sm-table-card class="flex-grow-1" [selected]="selected"
|
||||
[cardName]="experiment.name"
|
||||
[columns]="tableCols"
|
||||
[rowData]="experiment"
|
||||
[checked]="isRowSelected(experiment)"
|
||||
[activeContextRow]="contextExperiment"
|
||||
[contextMenuOpen]="contextMenuActive"
|
||||
[entityType]="entityType"
|
||||
<sm-table-card
|
||||
class="flex-grow-1"
|
||||
[selected]="selected"
|
||||
[cardName]="experiment.name"
|
||||
[columns]="tableCols"
|
||||
[rowData]="experiment"
|
||||
[checked]="isRowSelected(experiment)"
|
||||
[activeContextRow]="contextExperiment"
|
||||
[contextMenuOpen]="contextMenuActive"
|
||||
[entityType]="entityType"
|
||||
(click)="selected && onContextMenu({e: $event, rowData: experiment, backdrop: true})"
|
||||
>
|
||||
<div sm-name-icon *ngIf="experiment?.system_tags.includes('shared')"><i class="al-icon al-ico-link sm-md ml-2"></i></div>
|
||||
<div sm-name-version *ngIf="experiment?.hyperparams?.properties?.version?.value" class="mr-5">
|
||||
|
||||
@@ -41,10 +41,15 @@
|
||||
.checkbox-col {
|
||||
display: block;
|
||||
float: left;
|
||||
padding-left: 18px;
|
||||
padding: 13px 0 13px 26px;
|
||||
margin-left: -8px;
|
||||
|
||||
&:not(.header) {
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
&.header.minimised {
|
||||
padding-left: 16px;
|
||||
padding-left: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnDestroy,
|
||||
Output, TemplateRef,
|
||||
Output,
|
||||
TemplateRef,
|
||||
} from '@angular/core';
|
||||
import {ICONS, TIME_FORMAT_STRING} from '@common/constants';
|
||||
import {ColHeaderTypeEnum, ISmCol} from '@common/shared/ui-components/data/table/table.consts';
|
||||
@@ -22,15 +23,12 @@ import {Store} from '@ngrx/store';
|
||||
import {NoUnderscorePipe} from '@common/shared/pipes/no-underscore.pipe';
|
||||
import {TitleCasePipe} from '@angular/common';
|
||||
import {INITIAL_EXPERIMENT_TABLE_COLS} from '../../experiment.consts';
|
||||
import {
|
||||
ProjectsGetTaskParentsResponseParents
|
||||
} from '~/business-logic/model/projects/projectsGetTaskParentsResponseParents';
|
||||
import {ProjectsGetTaskParentsResponseParents} from '~/business-logic/model/projects/projectsGetTaskParentsResponseParents';
|
||||
import {Router} from '@angular/router';
|
||||
import {IOption} from '@common/shared/ui-components/inputs/select-autocomplete-for-template-forms/select-autocomplete-for-template-forms.component';
|
||||
import {createFiltersFromStore} from '../../effects/common-experiments-view.effects';
|
||||
import {CountAvailableAndIsDisableSelectedFiltered} from '@common/shared/entity-page/items.utils';
|
||||
import {hyperParamSelectedExperiments, selectAllExperiments} from '../../actions/common-experiments-view.actions';
|
||||
import {excludedKey, uniqueFilterValueAndExcluded} from '@common/shared/utils/tableParamEncode';
|
||||
import {createFiltersFromStore, excludedKey, uniqueFilterValueAndExcluded} from '@common/shared/utils/tableParamEncode';
|
||||
import {getRoundedNumber} from '../../shared/common-experiments.utils';
|
||||
|
||||
@Component({
|
||||
@@ -56,7 +54,6 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
private _selectedExperiments: ITableExperiment[] = [];
|
||||
readonly colHeaderTypeEnum = ColHeaderTypeEnum;
|
||||
@Input() initialColumns = INITIAL_EXPERIMENT_TABLE_COLS;
|
||||
@Input() disableContextMenu = false;
|
||||
@Input() contextMenuTemplate: TemplateRef<any> = null;
|
||||
|
||||
@Input() tableCols: ISmCol[];
|
||||
@@ -94,7 +91,7 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
label: user.name ? user.name : 'Unknown User',
|
||||
value: user.id
|
||||
}));
|
||||
this.sortOptionalUsersList();
|
||||
this.sortOptionsList(EXPERIMENTS_TABLE_COL_FIELDS.USER);
|
||||
}
|
||||
|
||||
@Input() set hyperParamsOptions(hyperParamsOptions: Record<ISmCol['id'], string[]>) {
|
||||
@@ -109,7 +106,6 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
@Input() activeParentsFilter: ProjectsGetTaskParentsResponseParents[];
|
||||
|
||||
|
||||
|
||||
@Input() set parents(parents: ProjectsGetTaskParentsResponseParents[]) {
|
||||
const parentsAndActiveFilter = Array.from(new Set(parents.concat(this.activeParentsFilter || [])));
|
||||
this.filtersOptions[EXPERIMENTS_TABLE_COL_FIELDS.PARENT] = parentsAndActiveFilter.map(parent => ({
|
||||
@@ -117,7 +113,7 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
value: parent.id,
|
||||
tooltip: `${parent.project?.name} / ${parent.name}`
|
||||
}));
|
||||
this.sortOptionalParentsList();
|
||||
this.sortOptionsList(EXPERIMENTS_TABLE_COL_FIELDS.PARENT);
|
||||
}
|
||||
|
||||
@Input() set selectedExperiments(experiments: ITableExperiment[]) {
|
||||
@@ -148,7 +144,7 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
label: tag === null ? '(No tags)' : tag,
|
||||
value: tag
|
||||
}) as IOption);
|
||||
this.sortOptionalTagsList();
|
||||
this.sortOptionalTagsList()
|
||||
}
|
||||
|
||||
@Input() set experimentTypes(types: string[]) {
|
||||
@@ -162,6 +158,15 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
);
|
||||
}
|
||||
|
||||
@Input() set projects(projects) {
|
||||
this.filtersOptions[EXPERIMENTS_TABLE_COL_FIELDS.PROJECT] = projects.map(project => ({
|
||||
label: project.name,
|
||||
value: project.id,
|
||||
tooltip: `${project.name}`
|
||||
}));
|
||||
this.sortOptionsList(EXPERIMENTS_TABLE_COL_FIELDS.PROJECT);
|
||||
}
|
||||
|
||||
@Input() systemTags = [] as string[];
|
||||
@Input() reorderableColumns: boolean = true;
|
||||
@Input() selectionReachedLimit: boolean;
|
||||
@@ -183,20 +188,12 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
this.filtersValues[EXPERIMENTS_TABLE_COL_FIELDS.VERSION] = get([EXPERIMENTS_TABLE_COL_FIELDS.VERSION, 'value'], filters) || [];
|
||||
this.filtersValues[EXPERIMENTS_TABLE_COL_FIELDS.LAST_UPDATE] = get([EXPERIMENTS_TABLE_COL_FIELDS.LAST_UPDATE, 'value'], filters) || [];
|
||||
this.filtersValues[EXPERIMENTS_TABLE_COL_FIELDS.STARTED] = get([EXPERIMENTS_TABLE_COL_FIELDS.STARTED, 'value'], filters) || [];
|
||||
this.sortOptionalProjectsList();
|
||||
|
||||
// handle dynamic filters;
|
||||
const filtersValues = createFiltersFromStore(filters || {}, false);
|
||||
this.filtersValues = Object.assign({}, {...this.filtersValues}, {...filtersValues});
|
||||
}
|
||||
@Input() set projects(projects) {
|
||||
this.filtersOptions[EXPERIMENTS_TABLE_COL_FIELDS.PROJECT] = projects.map(project => ({
|
||||
label: project.name,
|
||||
value: project.id,
|
||||
tooltip: `${project.name}`
|
||||
}));
|
||||
this.sortOptionalProjectsList();
|
||||
}
|
||||
|
||||
|
||||
@Output() experimentSelectionChanged = new EventEmitter<ITableExperiment>();
|
||||
@Output() experimentsSelectionChanged = new EventEmitter<Array<ITableExperiment>>();
|
||||
@@ -205,8 +202,8 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
@Output() tagsMenuOpened = new EventEmitter();
|
||||
@Output() typesMenuOpened = new EventEmitter();
|
||||
@Output() columnResized = new EventEmitter<{ columnId: string; widthPx: number }>();
|
||||
@Output() openContextMenu = new EventEmitter<{ x: number; y: number }>();
|
||||
@Output() removeTag = new EventEmitter<{experiment: ITableExperiment; tag: string}>();
|
||||
@Output() openContextMenu = new EventEmitter<{ x: number; y: number; single?: boolean; backdrop?: boolean }>();
|
||||
@Output() removeTag = new EventEmitter<{ experiment: ITableExperiment; tag: string }>();
|
||||
TIME_FORMAT_STRING = TIME_FORMAT_STRING;
|
||||
|
||||
constructor(
|
||||
@@ -225,25 +222,6 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
super.ngOnDestroy();
|
||||
}
|
||||
|
||||
onRowSelectionChanged(event) {
|
||||
this.experimentSelectionChanged.emit(event.data);
|
||||
}
|
||||
|
||||
sortOptionalUsersList() {
|
||||
this.filtersOptions[EXPERIMENTS_TABLE_COL_FIELDS.USER]
|
||||
.sort((a, b) => sortByArr(a.value, b.value, this.filtersValues[EXPERIMENTS_TABLE_COL_FIELDS.USER]));
|
||||
}
|
||||
|
||||
sortOptionalParentsList() {
|
||||
this.filtersOptions[EXPERIMENTS_TABLE_COL_FIELDS.PARENT]
|
||||
.sort((a, b) => sortByArr(a.value, b.value, this.filtersValues[EXPERIMENTS_TABLE_COL_FIELDS.PARENT]));
|
||||
}
|
||||
|
||||
sortOptionalProjectsList() {
|
||||
this.filtersOptions[EXPERIMENTS_TABLE_COL_FIELDS.PROJECT]
|
||||
.sort((a, b) => sortByArr(a.value, b.value, this.filtersValues[EXPERIMENTS_TABLE_COL_FIELDS.PROJECT]));
|
||||
}
|
||||
|
||||
sortOptionalTagsList() {
|
||||
const selectedTags = (this.filtersValues[EXPERIMENTS_TABLE_COL_FIELDS.TAGS] || [])
|
||||
.map(tag => typeof tag === 'string' ? tag.replace(excludedKey, '') : tag);
|
||||
@@ -277,26 +255,32 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
}
|
||||
}
|
||||
|
||||
tableRowClicked(event: { e: MouseEvent; data: ITableExperiment }) {
|
||||
if (this._selectedExperiments.some(exp => exp.id === event.data.id)) {
|
||||
this.onContextMenu({e: event.e, rowData: event.data, backdrop: true});
|
||||
} else {
|
||||
this.experimentsSelectionChanged.emit([event.data]);
|
||||
}
|
||||
}
|
||||
|
||||
emitSelection(selection: any[]) {
|
||||
this.experimentsSelectionChanged.emit(selection);
|
||||
}
|
||||
|
||||
searchValueChanged($event: string, colId) {
|
||||
this.searchValues[colId] = $event;
|
||||
if (colId === EXPERIMENTS_TABLE_COL_FIELDS.TAGS) {
|
||||
this.sortOptionalTagsList();
|
||||
}
|
||||
}
|
||||
|
||||
onContextMenu(data) {
|
||||
this.contextExperiment = this._experiments.find(experiment => experiment.id === data.rowData.id);
|
||||
if (!this.selectedExperiments.map(exp => exp.id).includes(this.contextExperiment.id)) {
|
||||
this.prevSelected = this.contextExperiment;
|
||||
this.emitSelection([this.contextExperiment]);
|
||||
onContextMenu(data: { e: MouseEvent, rowData; single?: boolean; backdrop?: boolean }) {
|
||||
if (!data?.single) {
|
||||
this.contextExperiment = this._experiments.find(experiment => experiment.id === data.rowData.id);
|
||||
if (!this.selectedExperiments.map(exp => exp.id).includes(this.contextExperiment.id)) {
|
||||
this.prevSelected = this.contextExperiment;
|
||||
this.emitSelection([this.contextExperiment]);
|
||||
}
|
||||
} else {
|
||||
this.contextExperiment = data.rowData;
|
||||
}
|
||||
const event = data.e as MouseEvent;
|
||||
event.preventDefault();
|
||||
this.openContextMenu.emit({x: event.clientX, y: event.clientY});
|
||||
this.openContextMenu.emit({x: event.clientX, y: event.clientY, single: data?.single, backdrop: data?.backdrop});
|
||||
}
|
||||
|
||||
|
||||
@@ -322,19 +306,6 @@ export class ExperimentsTableComponent extends BaseTableView implements OnDestro
|
||||
}
|
||||
}
|
||||
|
||||
columnFilterClosed(col: ISmCol) {
|
||||
switch (col.id) {
|
||||
case EXPERIMENTS_TABLE_COL_FIELDS.TAGS:
|
||||
this.sortOptionalTagsList();
|
||||
break;
|
||||
case EXPERIMENTS_TABLE_COL_FIELDS.USER:
|
||||
this.sortOptionalUsersList();
|
||||
break;
|
||||
case EXPERIMENTS_TABLE_COL_FIELDS.PROJECT:
|
||||
this.sortOptionalProjectsList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
selectAll(filtered?: boolean) {
|
||||
this.store.dispatch(selectAllExperiments({filtered}));
|
||||
|
||||
@@ -18,22 +18,22 @@ import {
|
||||
resetDisplayer,
|
||||
setDebugImageViewerScrollId,
|
||||
setDisplayerEndOfTime
|
||||
} from '../../../debug-images/debug-images-actions';
|
||||
} from '@common/debug-images/debug-images-actions';
|
||||
import {
|
||||
selectCurrentImageViewerDebugImage,
|
||||
selectDisplayerBeginningOfTime,
|
||||
selectDisplayerEndOfTime,
|
||||
selectMinMaxIterations
|
||||
} from '../../../debug-images/debug-images-reducer';
|
||||
} from '@common/debug-images/debug-images-reducer';
|
||||
import {interval, Observable, Subscription} from 'rxjs';
|
||||
import {EventsGetDebugImageIterationsResponse} from '../../../../business-logic/model/events/eventsGetDebugImageIterationsResponse';
|
||||
import {EventsGetDebugImageIterationsResponse} from '~/business-logic/model/events/eventsGetDebugImageIterationsResponse';
|
||||
import {filter, map, switchMap, tap, withLatestFrom} from 'rxjs/operators';
|
||||
import {selectAppVisible, selectAutoRefresh} from '../../../core/reducers/view.reducer';
|
||||
import {isFileserverUrl} from '../../../../shared/utils/url';
|
||||
import {getSignedUrl} from '../../../core/actions/common-auth.actions';
|
||||
import {getSignedUrlOrOrigin$} from '../../../core/reducers/common-auth-reducer';
|
||||
import {IsVideoPipe} from '../../../shared/pipes/is-video.pipe';
|
||||
import {IsAudioPipe} from '../../../shared/pipes/is-audio.pipe';
|
||||
import {selectAppVisible, selectAutoRefresh} from '@common/core/reducers/view.reducer';
|
||||
import {isFileserverUrl} from '~/shared/utils/url';
|
||||
import {getSignedUrl} from '@common/core/actions/common-auth.actions';
|
||||
import {getSignedUrlOrOrigin$} from '@common/core/reducers/common-auth-reducer';
|
||||
import {IsVideoPipe} from '@common/shared/pipes/is-video.pipe';
|
||||
import {IsAudioPipe} from '@common/shared/pipes/is-audio.pipe';
|
||||
|
||||
const DISPLAYER_AUTO_REFRESH_INTERVAL = 60 * 1000;
|
||||
|
||||
@@ -292,7 +292,7 @@ export class ImageDisplayerComponent implements OnInit, OnDestroy {
|
||||
this.currentDebugImageSubscription.unsubscribe();
|
||||
this.begOfTimeSub.unsubscribe();
|
||||
this.endOfTimeSub.unsubscribe();
|
||||
this.autoRefreshSub.unsubscribe();
|
||||
this.autoRefreshSub?.unsubscribe();
|
||||
}
|
||||
|
||||
showImage() {
|
||||
|
||||
@@ -10,5 +10,4 @@
|
||||
[selectedItemsList]="metricsCols"
|
||||
(selectedItems)="toggleParamToDisplay($event)"
|
||||
(clearSelection)="clearSelection.emit()"
|
||||
>
|
||||
</sm-grouped-checked-filter-list>
|
||||
></sm-grouped-checked-filter-list>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
@import "../../../shared/ui-components/styles/variables";
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
height: 640px;
|
||||
max-height: calc(100vh - 120px);
|
||||
width: 370px;
|
||||
min-height: 470px;
|
||||
}
|
||||
|
||||
sm-grouped-checked-filter-list {
|
||||
padding: 15px 15px 0 15px;
|
||||
max-height: 419px;
|
||||
height: calc(100% - 52px);
|
||||
padding: 15px 0 0 15px;
|
||||
::ng-deep .actions {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user