mirror of
https://github.com/clearml/clearml-web
synced 2025-06-26 18:27:02 +00:00
Release v1.6 (#31)
This commit is contained in:
8510
package-lock.json
generated
8510
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
103
package.json
103
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ClearML-webapp",
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"license": "",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
@@ -20,30 +20,30 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@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.14",
|
||||
"@angular/animations": "^13.3.7",
|
||||
"@angular/cdk": "^13.3.7",
|
||||
"@angular/common": "^13.3.7",
|
||||
"@angular/compiler": "^13.3.7",
|
||||
"@angular/core": "^13.3.7",
|
||||
"@angular/forms": "^13.3.7",
|
||||
"@angular/material": "^13.3.7",
|
||||
"@angular/platform-browser": "^13.3.7",
|
||||
"@angular/platform-browser-dynamic": "^13.3.7",
|
||||
"@angular/platform-server": "^13.3.7",
|
||||
"@angular/router": "^13.3.7",
|
||||
"@angular/service-worker": "^13.3.7",
|
||||
"@angular/youtube-player": "^13.3.7",
|
||||
"@aws-sdk/client-s3": "^3.88.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.88.0",
|
||||
"@ngneat/dag": "^2.0.0",
|
||||
"@ngrx/effects": "^13.2.0",
|
||||
"@ngrx/entity": "^13.2.0",
|
||||
"@ngrx/router-store": "^13.2.0",
|
||||
"@ngrx/store": "^13.2.0",
|
||||
"ace-builds": "^1.5.0",
|
||||
"angular-google-tag-manager": "^1.5.0",
|
||||
"angular-resizable-element": "^5.0.0",
|
||||
"angular-split": "^13.1.0",
|
||||
"angular-split": "^13.2.0",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"bootstrap": "^4.6.1",
|
||||
"britecharts": "^2.18.0",
|
||||
@@ -56,49 +56,48 @@
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash": "^4.17.21",
|
||||
"lucene": "^2.1.1",
|
||||
"ngx-clipboard": "^15.0.1",
|
||||
"ngx-color-picker": "^12.0.0",
|
||||
"ngx-clipboard": "^15.1.0",
|
||||
"ngx-color-picker": "^12.0.1",
|
||||
"ngx-markdown-editor": "^4.0.0",
|
||||
"ngx-window-token": "^6.0.0",
|
||||
"object-hash": "^2.2.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"primeicons": "^5.0.0",
|
||||
"primeng": "^13.0.4",
|
||||
"primeng": "^13.4.0",
|
||||
"process": "^0.11.10",
|
||||
"rxjs": "^7.5.5",
|
||||
"string-to-color": "^2.2.2",
|
||||
"tslib": "^2.3.1",
|
||||
"tslib": "^2.4.0",
|
||||
"url": "^0.11.0",
|
||||
"uuid": "^8.3.2",
|
||||
"zone.js": "~0.11.4"
|
||||
"zone.js": "~0.11.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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",
|
||||
"@angular-devkit/build-angular": "^13.3.5",
|
||||
"@angular-devkit/core": "^13.3.5",
|
||||
"@angular-devkit/schematics": "^13.3.5",
|
||||
"@angular-devkit/schematics-cli": "^13.3.5",
|
||||
"@angular-eslint/builder": "^13.2.1",
|
||||
"@angular-eslint/eslint-plugin": "^13.2.1",
|
||||
"@angular-eslint/eslint-plugin-template": "^13.2.1",
|
||||
"@angular-eslint/schematics": "13.2.1",
|
||||
"@angular-eslint/template-parser": "^13.2.1",
|
||||
"@angular/cli": "^13.3.5",
|
||||
"@angular/compiler-cli": "^13.3.7",
|
||||
"@angular/language-service": "^13.3.7",
|
||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
||||
"@ngrx/schematics": "^13.2.0",
|
||||
"@ngrx/store-devtools": "^13.2.0",
|
||||
"@types/d3-selection": "^3.0.2",
|
||||
"@types/lodash": "^4.14.178",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/node": "^16.11.19",
|
||||
"@types/plotly.js": "^1.54.20",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "5.9.0",
|
||||
"@typescript-eslint/parser": "5.9.0",
|
||||
"codelyzer": "^6.0.2",
|
||||
"eslint": "^8.9.0",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eslint-plugin-jsdoc": "37.9.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.23.0",
|
||||
"@typescript-eslint/parser": "5.23.0",
|
||||
"eslint": "^8.15.0",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-jsdoc": "39.2.9",
|
||||
"eslint-plugin-prefer-arrow": "1.2.3",
|
||||
"typescript": "^4.5.5"
|
||||
"typescript": "~4.6.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {Routes} from '@angular/router';
|
||||
import {AdminComponent} from '@common/settings/admin/admin.component';
|
||||
*/
|
||||
import {ProjectRedirectGuardGuard} from '@common/shared/guards/project-redirect.guard';
|
||||
import {EntityTypeEnum} from '~/shared/constants/non-common-consts';
|
||||
|
||||
|
||||
export const routes: Routes = [
|
||||
@@ -42,7 +43,7 @@ export const routes: Routes = [
|
||||
{
|
||||
path: 'compare-experiments',
|
||||
loadChildren: () => import('./webapp-common/experiments-compare/experiments-compare.module').then(m => m.ExperimentsCompareModule),
|
||||
data: {search: false}
|
||||
data: {entityType: EntityTypeEnum.experiment},
|
||||
},
|
||||
]
|
||||
},
|
||||
@@ -68,12 +69,18 @@ export const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'compare-experiments',
|
||||
data: {entityType: EntityTypeEnum.controller},
|
||||
loadChildren: () => import('./webapp-common/experiments-compare/experiments-compare.module').then(m => m.ExperimentsCompareModule)
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'datasets',
|
||||
data: {search: true},
|
||||
loadChildren: () => import('./features/datasets/datasets.module').then(m => m.DatasetsModule)
|
||||
},
|
||||
{path: 'workers-and-queues', loadChildren: () => import('./features/workers-and-queues/workers-and-queues.module').then(m => m.WorkersAndQueuesModule)},
|
||||
{path: '404', loadChildren: () => import('./features/not-found/not-found.module').then(m => m.NotFoundModule)},
|
||||
{path: '**', loadChildren: () => import('./features/not-found/not-found.module').then(m => m.NotFoundModule)},
|
||||
|
||||
@@ -59,6 +59,11 @@ import { BASE_PATH, COLLECTION_FORMATS } from '../variables'
|
||||
import { Configuration } from '../configuration';
|
||||
import {EventsScalarMetricsIterRawRequest} from "~/business-logic/model/events/eventsScalarMetricsIterRawRequest";
|
||||
import {EventsScalarMetricsIterRawResponse} from "~/business-logic/model/events/eventsScalarMetricsIterRawResponse";
|
||||
import {EventsNextPlotSampleRequest} from '~/business-logic/model/events/eventsNextPlotSampleRequest';
|
||||
import {PlotSampleResponse} from '~/business-logic/model/events/plotSampleResponse';
|
||||
import {EventsGetPlotSampleRequest} from '~/business-logic/model/events/eventsGetPlotSampleRequest';
|
||||
import {EventsGetTaskSingleValueMetricsRequest} from '~/business-logic/model/events/eventsGetTaskSingleValueMetricsRequest';
|
||||
import {EventsGetTaskSingleValueMetricsResponse} from '~/business-logic/model/events/eventsGetTaskSingleValueMetricsResponse';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@@ -453,7 +458,97 @@ export class ApiEventsService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
*
|
||||
* Get the plot for the next variant for the same iteration or for the next iteration
|
||||
* @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 eventsNextPlotSample(request: EventsNextPlotSampleRequest, 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 eventsNextPlotSample.');
|
||||
}
|
||||
|
||||
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<PlotSampleResponse>(`${this.basePath}/events.next_plot_sample`,
|
||||
request,
|
||||
{
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: headers,
|
||||
observe: observe,
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Return the plot per metric and variant for the provided iteration
|
||||
* @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 eventsGetPlotSample(request: EventsGetPlotSampleRequest, 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 eventsGetPlotSample.');
|
||||
}
|
||||
|
||||
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<PlotSampleResponse>(`${this.basePath}/events.get_plot_sample`,
|
||||
request,
|
||||
{
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: headers,
|
||||
observe: observe,
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* get task scalar metrics and variants
|
||||
* @param request request body
|
||||
@@ -723,6 +818,51 @@ export class ApiEventsService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Get single value metrics for the passed tasks
|
||||
* @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 eventsGetTaskSingleValueMetrics(request: EventsGetTaskSingleValueMetricsRequest, 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 eventsGetTaskSingleValueMetrics.');
|
||||
}
|
||||
|
||||
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<EventsGetTaskSingleValueMetricsResponse>(`${this.basePath}/events.get_task_single_value_metrics`,
|
||||
request,
|
||||
{
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: headers,
|
||||
observe: observe,
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
|
||||
@@ -25,6 +25,8 @@ import { Observable } from 'rxjs';
|
||||
import { OrganizationGetTagsRequest } from '../model/organization/organizationGetTagsRequest';
|
||||
import { OrganizationGetTagsResponse } from '../model/organization/organizationGetTagsResponse';
|
||||
import { OrganizationGetUserCompaniesResponse } from '../model/organization/organizationGetUserCompaniesResponse';
|
||||
import { OrganizationGetEntitiesCountRequest } from '../model/organization/organizationGetEntitiesCountRequest';
|
||||
import { OrganizationGetEntitiesCountResponse } from '../model/organization/organizationGetEntitiesCountResponse';
|
||||
|
||||
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
||||
import { Configuration } from '../configuration';
|
||||
@@ -152,4 +154,48 @@ export class ApiOrganizationService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Get counts for the company entities according to the passed search criteria
|
||||
* @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 organizationGetEntitiesCount(request: OrganizationGetEntitiesCountRequest, 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 organizationGetEntitiesCount.');
|
||||
}
|
||||
|
||||
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<OrganizationGetEntitiesCountResponse>(`${this.basePath}/organization.get_entities_count`,
|
||||
request,
|
||||
{
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: headers,
|
||||
observe: observe,
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,4 +37,8 @@ export interface EventsGetDebugImageSampleRequest {
|
||||
* Scroll ID from the previous call to get_debug_image_sample or empty
|
||||
*/
|
||||
scroll_id?: string;
|
||||
/**
|
||||
* If set then subsequent navigation with next_debug_image_sample is done on the debug images for the passed metric only. Otherwise for all the metrics
|
||||
*/
|
||||
navigate_current_metric?: boolean;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* events
|
||||
* 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 EventsGetPlotSampleRequest {
|
||||
/**
|
||||
* Task ID
|
||||
*/
|
||||
task: string;
|
||||
/**
|
||||
* Metric name
|
||||
*/
|
||||
metric: string;
|
||||
/**
|
||||
* Metric variant
|
||||
*/
|
||||
variant: string;
|
||||
/**
|
||||
* The iteration to bring plot from. If not specified then the latest reported plot is retrieved
|
||||
*/
|
||||
iteration?: number;
|
||||
/**
|
||||
* If set then scroll state will be refreshed to reflect the latest changes in the plots
|
||||
*/
|
||||
refresh?: boolean;
|
||||
/**
|
||||
* Scroll ID from the previous call to get_plot_sample or empty
|
||||
*/
|
||||
scroll_id?: string;
|
||||
/**
|
||||
* If set then subsequent navigation with next_plot_sample is done on the plots for the passed metric only. Otherwise for all the metrics
|
||||
*/
|
||||
navigate_current_metric?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* events
|
||||
* 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 EventsGetTaskSingleValueMetricsRequest {
|
||||
/**
|
||||
* List of task Task IDs
|
||||
*/
|
||||
tasks: Array<string>;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* events
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { EventsGetTaskSingleValueMetricsResponseTasks } from '././eventsGetTaskSingleValueMetricsResponseTasks';
|
||||
|
||||
|
||||
export interface EventsGetTaskSingleValueMetricsResponse {
|
||||
/**
|
||||
* Single value metrics grouped by task
|
||||
*/
|
||||
tasks?: Array<EventsGetTaskSingleValueMetricsResponseTasks>;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* events
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { EventsGetTaskSingleValueMetricsResponseValues } from '././eventsGetTaskSingleValueMetricsResponseValues';
|
||||
|
||||
|
||||
export interface EventsGetTaskSingleValueMetricsResponseTasks {
|
||||
/**
|
||||
* Task ID
|
||||
*/
|
||||
task?: string;
|
||||
values?: Array<EventsGetTaskSingleValueMetricsResponseValues>;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* events
|
||||
* 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 EventsGetTaskSingleValueMetricsResponseValues {
|
||||
metric?: string;
|
||||
variant?: string;
|
||||
value?: number;
|
||||
timestamp?: number;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* events
|
||||
* 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 EventsNextPlotSampleRequest {
|
||||
/**
|
||||
* Task ID
|
||||
*/
|
||||
task: string;
|
||||
/**
|
||||
* Scroll ID from the previous call to get_plot_sample
|
||||
*/
|
||||
scroll_id: string;
|
||||
/**
|
||||
* If set then get the either previous variant event from the current iteration or (if does not exist) the last variant event from the previous iteration. Otherwise next variant event from the current iteration or first variant event from the next iteration
|
||||
*/
|
||||
navigate_earlier?: boolean;
|
||||
}
|
||||
32
src/app/business-logic/model/events/plotSampleResponse.ts
Normal file
32
src/app/business-logic/model/events/plotSampleResponse.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* events
|
||||
* 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 PlotSampleResponse {
|
||||
/**
|
||||
* Scroll ID to pass to the next calls to get_plot_sample or next_plot_sample
|
||||
*/
|
||||
scroll_id?: string;
|
||||
/**
|
||||
* Plot event
|
||||
*/
|
||||
event?: object;
|
||||
/**
|
||||
* minimal valid iteration for the variant
|
||||
*/
|
||||
min_iteration?: number;
|
||||
/**
|
||||
* maximal valid iteration for the variant
|
||||
*/
|
||||
max_iteration?: number;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* models
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* OpenAPI spec version: 2.14
|
||||
* OpenAPI spec version: 999.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
@@ -92,4 +92,8 @@ export interface ModelsGetAllExRequest {
|
||||
* The number of models to retrieve
|
||||
*/
|
||||
size?: number;
|
||||
/**
|
||||
* If true, include models statistic in response
|
||||
*/
|
||||
include_stats?: boolean;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* organization
|
||||
* 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 OrganizationGetEntitiesCountRequest {
|
||||
/**
|
||||
* Search criteria for projects
|
||||
*/
|
||||
projects?: object;
|
||||
/**
|
||||
* Search criteria for experiments
|
||||
*/
|
||||
tasks?: object;
|
||||
/**
|
||||
* Search criteria for models
|
||||
*/
|
||||
models?: object;
|
||||
/**
|
||||
* Search criteria for dataviews
|
||||
*/
|
||||
dataviews?: object;
|
||||
/**
|
||||
* Search criteria for hyper datasets
|
||||
*/
|
||||
hyper_datasets?: object;
|
||||
/**
|
||||
* Search criteria for pipelines
|
||||
*/
|
||||
pipelines?: object;
|
||||
datasets: object;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* organization
|
||||
* 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 OrganizationGetEntitiesCountResponse {
|
||||
/**
|
||||
* The number of projects matching the criteria
|
||||
*/
|
||||
projects?: number;
|
||||
/**
|
||||
* The number of experiments matching the criteria
|
||||
*/
|
||||
tasks?: number;
|
||||
/**
|
||||
* The number of models matching the criteria
|
||||
*/
|
||||
models?: number;
|
||||
/**
|
||||
* The number of dataviews matching the criteria
|
||||
*/
|
||||
dataviews?: number;
|
||||
/**
|
||||
* The number of hyper datasets matching the criteria
|
||||
*/
|
||||
hyper_datasets?: number;
|
||||
/**
|
||||
* The number of pipelines matching the criteria
|
||||
*/
|
||||
pipelines?: number;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import {ProjectsGetAllResponseSingleSubProjects} from '~/business-logic/model/projects/projectsGetAllResponseSingleSubProjects';
|
||||
import {Stats} from '~/business-logic/model/projects/stats';
|
||||
import {ProjectsGetAllResponseSingleDatasetStats} from '~/business-logic/model/projects/projectsGetAllResponseSingleDatasetStats';
|
||||
|
||||
/**
|
||||
* projects
|
||||
@@ -24,6 +25,10 @@ export interface Project {
|
||||
* Project name
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* Project base name
|
||||
*/
|
||||
basename?: string;
|
||||
/**
|
||||
* Project description
|
||||
*/
|
||||
@@ -53,6 +58,7 @@ export interface Project {
|
||||
*/
|
||||
default_output_destination?: string;
|
||||
stats?: Stats;
|
||||
dataset_stats?: ProjectsGetAllResponseSingleDatasetStats;
|
||||
/**
|
||||
* Last project update time. Reflects the last time the project metadata was changed or a task in this project has changed status
|
||||
*/
|
||||
|
||||
@@ -94,6 +94,7 @@ export interface ProjectsGetAllExRequest {
|
||||
*/
|
||||
size?: number;
|
||||
stats_with_children?: boolean;
|
||||
include_stats_filter?: any;
|
||||
}
|
||||
export namespace ProjectsGetAllExRequest {
|
||||
export type StatsForStateEnum = 'active' | 'archived';
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Project dataset statistics
|
||||
*/
|
||||
export interface ProjectsGetAllResponseSingleDatasetStats {
|
||||
/**
|
||||
* The number of files stored in the dataset
|
||||
*/
|
||||
file_count?: number;
|
||||
/**
|
||||
* The total dataset size in bytes
|
||||
*/
|
||||
total_size?: number;
|
||||
}
|
||||
@@ -12,9 +12,10 @@
|
||||
|
||||
|
||||
|
||||
export type FeaturesEnum = 'experiments' | 'queues';
|
||||
export type FeaturesEnum = 'experiments' | 'queues' | 'pipelines';
|
||||
|
||||
export const FeaturesEnum = {
|
||||
Experiments: 'experiments' as FeaturesEnum,
|
||||
Queues: 'queues' as FeaturesEnum
|
||||
Queues: 'queues' as FeaturesEnum,
|
||||
Pipelines: 'pipelines' as FeaturesEnum
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface GetCurrentUserResponseUserObject {
|
||||
avatar?: string;
|
||||
company?: GetCurrentUserResponseUserObjectCompany;
|
||||
email?: string;
|
||||
getting_started?: any;
|
||||
/**
|
||||
* User preferences
|
||||
*/
|
||||
|
||||
@@ -15,4 +15,5 @@ import { GetCurrentUserResponseUserObject } from '././getCurrentUserResponseUser
|
||||
|
||||
export interface UsersGetCurrentUserResponse {
|
||||
user?: GetCurrentUserResponseUserObject;
|
||||
getting_started?: object;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {createAction, props} from '@ngrx/store';
|
||||
import {USERS_PREFIX} from '../../app.constants';
|
||||
import {GetCurrentUserResponseUserObject} from '../../business-logic/model/users/getCurrentUserResponseUserObject';
|
||||
import {USERS_PREFIX} from '~/app.constants';
|
||||
import {GetCurrentUserResponseUserObject} from '~/business-logic/model/users/getCurrentUserResponseUserObject';
|
||||
|
||||
export const setCurrentUser = createAction(USERS_PREFIX + 'SET_CURRENT_USER',
|
||||
props<{user: GetCurrentUserResponseUserObject; terms_of_use?: any}>()
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
props<{user: GetCurrentUserResponseUserObject; terms_of_use?: any; getting_started?: any}>()
|
||||
);
|
||||
|
||||
@@ -94,7 +94,7 @@ export const localStorageReducer = (reducer: ActionReducer<any>): ActionReducer<
|
||||
|
||||
const userPrefMetaFactory = (userPreferences: UserPreferences): MetaReducer<any>[] => [
|
||||
(reducer: ActionReducer<any>) =>
|
||||
createUserPrefReducer('users', ['activeWorkspace'], [USERS_PREFIX], userPreferences, reducer),
|
||||
createUserPrefReducer('users', ['activeWorkspace', 'showOnlyUserWork'], [USERS_PREFIX], userPreferences, reducer),
|
||||
(reducer: ActionReducer<any>) =>
|
||||
createUserPrefReducer('rootProjects', ['tagsColors', 'graphVariant'], [ROOT_PROJECTS_PREFIX], userPreferences, reducer),
|
||||
(reducer: ActionReducer<any>) =>
|
||||
|
||||
@@ -8,6 +8,9 @@ import {deactivateLoader} from '@common/core/actions/layout.actions';
|
||||
import {ALL_PROJECTS_OBJECT} from '@common/core/effects/projects.effects';
|
||||
import {requestFailed} from '@common/core/actions/http.actions';
|
||||
import {ApiProjectsService} from '~/business-logic/api-services/projects.service';
|
||||
import {selectCurrentUser, selectShowOnlyUserWork} from '@common/core/reducers/users-reducer';
|
||||
import {ProjectsGetAllExRequest} from '~/business-logic/model/projects/projectsGetAllExRequest';
|
||||
import {selectShowHidden} from '@common/projects/common-projects.reducer';
|
||||
|
||||
|
||||
|
||||
@@ -23,8 +26,13 @@ export class ProjectsEffects {
|
||||
|
||||
getSelectedProject = createEffect(() => this.actions$.pipe(
|
||||
ofType(actions.setSelectedProjectId),
|
||||
withLatestFrom(this.store.select(selectSelectedProjectId)),
|
||||
switchMap(([action, selectedProjectId]) => {
|
||||
withLatestFrom(
|
||||
this.store.select(selectSelectedProjectId),
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectShowOnlyUserWork),
|
||||
this.store.select(selectShowHidden),
|
||||
),
|
||||
switchMap(([action, selectedProjectId, user, showOnlyUserWork, showHidden]) => {
|
||||
if (!action.projectId) {
|
||||
return [
|
||||
deactivateLoader(action.type),
|
||||
@@ -45,9 +53,12 @@ export class ProjectsEffects {
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
id: [action.projectId],
|
||||
include_stats: true,
|
||||
...((action.example !== false || this.fetchingExampleExperiment === action.projectId) && {check_own_contents: true})
|
||||
...(!showHidden && {include_stats_filter: {system_tags: ['-pipeline']}}),
|
||||
...((action.example !== false || this.fetchingExampleExperiment === action.projectId) && {check_own_contents: true}),
|
||||
...(showOnlyUserWork && {active_users: [user.id]}),
|
||||
...(showHidden && {search_hidden: true}),
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
})
|
||||
} as ProjectsGetAllExRequest)
|
||||
.pipe(
|
||||
finalize(() => this.fetchingExampleExperiment = null),
|
||||
mergeMap(({projects}) => [
|
||||
|
||||
@@ -12,6 +12,7 @@ export const usersReducer = createReducer<UsersState>(initUsers,
|
||||
on(setCurrentUser, (state, action) => ({
|
||||
...state,
|
||||
currentUser: action.user,
|
||||
gettingStarted: action.getting_started,
|
||||
activeWorkspace: action.user?.company,
|
||||
userWorkspaces: [action.user?.company],
|
||||
}))
|
||||
|
||||
32
src/app/features/dashboard-search/dashboard-search.consts.ts
Normal file
32
src/app/features/dashboard-search/dashboard-search.consts.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
export type ActiveSearchLink = 'projects' | 'experiments' | 'models' | 'pipelines' | 'datasets';
|
||||
|
||||
export const activeSearchLink = {
|
||||
projects: 'projects' as ActiveSearchLink,
|
||||
experiments: 'experiments' as ActiveSearchLink,
|
||||
models: 'models' as ActiveSearchLink,
|
||||
pipelines: 'pipelines' as ActiveSearchLink,
|
||||
openDatasets: 'datasets' as ActiveSearchLink
|
||||
};
|
||||
|
||||
export const activeLinksList = [
|
||||
{
|
||||
label: 'PROJECTS',
|
||||
name: activeSearchLink.projects,
|
||||
},
|
||||
{
|
||||
label: 'DATASETS',
|
||||
name: activeSearchLink.openDatasets,
|
||||
},
|
||||
{
|
||||
label: 'EXPERIMENTS',
|
||||
name: activeSearchLink.experiments,
|
||||
},
|
||||
{
|
||||
label: 'MODELS',
|
||||
name: activeSearchLink.models,
|
||||
},
|
||||
{
|
||||
label: 'PIPELINES',
|
||||
name: activeSearchLink.pipelines,
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,26 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {ApiProjectsService} from '~/business-logic/api-services/projects.service';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
import {getResultsCount, setResultsCount} from '@common/dashboard-search/dashboard-search.actions';
|
||||
import {getEntityStatQuery} from '@common/dashboard-search/dashboard-search.effects';
|
||||
import {ApiOrganizationService} from '~/business-logic/api-services/organization.service';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class DashboardSearchEffects {
|
||||
constructor(
|
||||
private actions: Actions,
|
||||
public projectsApi: ApiProjectsService,
|
||||
public organizationApi: ApiOrganizationService,
|
||||
) {
|
||||
}
|
||||
|
||||
getResultsCount = createEffect(() => this.actions.pipe(
|
||||
ofType(getResultsCount),
|
||||
switchMap(action => this.organizationApi.organizationGetEntitiesCount(getEntityStatQuery(action))),
|
||||
map(({tasks: experiments, ...rest}) =>
|
||||
setResultsCount({counts: {...rest, experiments}}))
|
||||
));
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<sm-search-results-page
|
||||
*ngIf="activeSearch$ | async"
|
||||
(projectSelected)="projectCardClicked($event)"
|
||||
(experimentSelected)="taskSelected($event)"
|
||||
(modelSelected)="modelSelected($event)"
|
||||
(pipelineSelected)="pipelineSelected($event)"
|
||||
(activeLinkChanged)="activeLinkChanged($event)"
|
||||
[projectsList]="projectsResults$ | async"
|
||||
[pipelinesList]="pipelinesResults$ | async"
|
||||
[experimentsList]="experimentsResults$ | async"
|
||||
[modelsList]="modelsResults$ | async"
|
||||
[activeLink]="activeLink">
|
||||
</sm-search-results-page>
|
||||
@@ -1,45 +0,0 @@
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {combineLatest, Observable, ObservedValueOf, Subscription} from 'rxjs';
|
||||
import {filter, skip} from 'rxjs/operators';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {Project} from '~/business-logic/model/projects/project';
|
||||
import {Model} from '~/business-logic/model/models/model';
|
||||
import {DashboardSearchComponentBase} from '@common/dashboard/dashboard-search.component.base';
|
||||
import {SearchClear} from '@common/dashboard-search/dashboard-search.actions';
|
||||
|
||||
export type ActiveSearchLink = 'projects' | 'experiments' | 'models' | 'pipelines';
|
||||
|
||||
|
||||
@Component({
|
||||
selector : ' sm-dashboard-search',
|
||||
templateUrl: './dashboard-search.component.html',
|
||||
styleUrls : ['./dashboard-search.component.scss']
|
||||
})
|
||||
export class DashboardSearchComponent extends DashboardSearchComponentBase implements OnInit, OnDestroy {
|
||||
|
||||
readonly tabsIndexes = ['projects', 'experiments', 'models'];
|
||||
private allResultsJoin$: Observable<[ObservedValueOf<Observable<Array<Project>>>, ObservedValueOf<Observable<any>>, ObservedValueOf<Observable<Array<Model>>>, ObservedValueOf<Observable<number>>]>;
|
||||
private allResultsSubscription: Subscription;
|
||||
|
||||
constructor(public store: Store<any>, public router: Router) {
|
||||
super(store);
|
||||
this.allResultsJoin$ = combineLatest([this.projectsResults$, this.experimentsResults$, this.modelsResults$, this.resultsCounter$]);
|
||||
this.syncAppSearch();
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.allResultsSubscription = this.allResultsJoin$.pipe(
|
||||
skip(1),
|
||||
filter(allResults => allResults[this.tabsIndexes.length] === this.tabsIndexes.length)
|
||||
).subscribe(allResults => this.setFirstActiveLink(allResults, this.tabsIndexes));
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.store.dispatch(new SearchClear());
|
||||
this.searchTermChanged('');
|
||||
this.stopSyncSearch();
|
||||
this.allResultsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,8 @@ import {CommonModule} from '@angular/common';
|
||||
import {SMSharedModule} from '@common/shared/shared.module';
|
||||
import {StoreModule} from '@ngrx/store';
|
||||
import {EffectsModule} from '@ngrx/effects';
|
||||
import {DashboardSearchEffects} from '@common/dashboard-search/dashboard-search.effects';
|
||||
import {DashboardSearchEffects as commonDashboardSearchEffects} from '@common/dashboard-search/dashboard-search.effects';
|
||||
import {DashboardSearchEffects} from '~/features/dashboard-search/dashboard-search.effects';
|
||||
import {ProjectsSharedModule} from '../../projects/shared/projects-shared.module';
|
||||
import {SharedModule} from '~/shared/shared.module';
|
||||
import {dashboardSearchReducer} from '@common/dashboard-search/dashboard-search.reducer';
|
||||
@@ -14,7 +15,7 @@ import {dashboardSearchReducer} from '@common/dashboard-search/dashboard-search.
|
||||
SMSharedModule,
|
||||
ProjectsSharedModule,
|
||||
StoreModule.forFeature('search', dashboardSearchReducer),
|
||||
EffectsModule.forFeature([DashboardSearchEffects]),
|
||||
EffectsModule.forFeature([DashboardSearchEffects, commonDashboardSearchEffects]),
|
||||
SharedModule
|
||||
],
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<sm-dashboard-search [class.dashboard-search]="activeSearch$ | async"></sm-dashboard-search>
|
||||
<sm-dashboard-search-base [class.dashboard-search]="activeSearch$ | async"></sm-dashboard-search-base>
|
||||
<div *ngIf="(activeSearch$ | async) !== true" class="dashboard-body">
|
||||
<div class="recent">
|
||||
<sm-dashboard-projects (width)="setWidth($event)"></sm-dashboard-projects>
|
||||
|
||||
@@ -7,12 +7,12 @@ import {GetCurrentUserResponseUserObjectCompany} from '~/business-logic/model/us
|
||||
import {filter, skip, take} from 'rxjs/operators';
|
||||
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 '@common/layout/welcome-message/welcome-message.component';
|
||||
import {firstLogin} from '@common/core/actions/layout.actions';
|
||||
import {IRecentTask, selectRecentTasks} from '@common/dashboard/common-dashboard.reducer';
|
||||
import {selectActiveSearch} from '@common/dashboard-search/dashboard-search.reducer';
|
||||
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -8,13 +8,15 @@ import {GettingStartedCardComponent} from './dumb/getting-started-card/getting-s
|
||||
import {SMSharedModule} from '@common/shared/shared.module';
|
||||
import {CommonDashboardModule} from '@common/dashboard/common-dashboard.module';
|
||||
import {commonDashboardReducer} from '@common/dashboard/common-dashboard.reducer';
|
||||
import {DashboardSearchComponent} from './containers/dashboard-search/dashboard-search.component';
|
||||
import {SearchResultsPageComponent} from './dumb/search-results-page/search-results-page.component';
|
||||
import {SharedModule} from '~/shared/shared.module';
|
||||
import {DashboardSearchModule} from './dashboard-search/dashboard-search.module';
|
||||
import {ProjectDialogModule} from '@common/shared/project-dialog/project-dialog.module';
|
||||
import {ProjectsSharedModule} from '../projects/shared/projects-shared.module';
|
||||
import {SearchResultsComponent} from '@common/dashboard-search/dumb/search-results/search-results.component';
|
||||
import {DashboardSearchBaseComponent} from '@common/dashboard/dashboard-search.component.base';
|
||||
import {DatasetsModule} from '~/features/datasets/datasets.module';
|
||||
import {DatasetsSharedModule} from '~/features/datasets/shared/datasets-shared.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -27,9 +29,11 @@ import {SearchResultsComponent} from '@common/dashboard-search/dumb/search-resul
|
||||
StoreModule.forFeature('dashboard', commonDashboardReducer),
|
||||
CommonDashboardModule,
|
||||
SharedModule,
|
||||
DashboardSearchModule
|
||||
DashboardSearchModule,
|
||||
DatasetsModule,
|
||||
DatasetsSharedModule
|
||||
],
|
||||
declarations : [DashboardComponent, GettingStartedCardComponent, DashboardSearchComponent, SearchResultsPageComponent, SearchResultsComponent]
|
||||
declarations : [DashboardComponent, GettingStartedCardComponent, DashboardSearchBaseComponent, SearchResultsPageComponent, SearchResultsComponent]
|
||||
})
|
||||
export class DashboardModule {
|
||||
}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
<div class="search-container">
|
||||
<div class="container">
|
||||
<div class="d-flex-center">
|
||||
<div class="pl-3 py-3">
|
||||
<span [ngClass]="{'active': activeLink === 'projects'}" class="pointer category-link"
|
||||
(click)="activeLinkChanged.emit('projects')">PROJECTS {{'(' + projectsList.length + ')'}} </span>
|
||||
<span [ngClass]="{'active': activeLink === 'experiments'}" class="pointer category-link"
|
||||
(click)="activeLinkChanged.emit('experiments')">EXPERIMENTS {{'(' + experimentsList.length + ')'}} </span>
|
||||
<span [ngClass]="{'active': activeLink === 'models'}" class="pointer category-link"
|
||||
(click)="activeLinkChanged.emit('models')">MODELS {{'(' + modelsList.length + ')'}} </span>
|
||||
<span [ngClass]="{'active': activeLink === 'pipelines'}" class="pointer category-link"
|
||||
(click)="activeLinkChanged.emit('pipelines')">PIPELINES ({{pipelinesList?.length}}) </span>
|
||||
<ng-container *ngFor="let searchTab of activeLinksList">
|
||||
<span [class.active]="activeLink === searchTab.name" class="pointer category-link"
|
||||
(click)="activeLinkChanged.emit(searchTab.name)">{{searchTab.label}} ({{resultsCount?.[searchTab.name]}}) </span>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-container">
|
||||
<sm-search-results
|
||||
[cardTemplate]="
|
||||
activeLink === 'projects' ? ProjectTemplate :
|
||||
activeLink === 'experiments' ? ExperimentTemplate :
|
||||
activeLink === 'models' ? ModelsTemplate :
|
||||
activeLink === 'pipelines' ? PipelineTemplate :
|
||||
activeLink === searchPages.projects ? ProjectTemplate :
|
||||
activeLink === searchPages.experiments ? ExperimentTemplate :
|
||||
activeLink === searchPages.models ? ModelsTemplate :
|
||||
activeLink === searchPages.openDatasets ? openDatasetTemplate :
|
||||
activeLink === searchPages.pipelines ? PipelineTemplate :
|
||||
ProjectTemplate"
|
||||
[results]="getResults()"
|
||||
[cardHeight]="getCardHeight()"
|
||||
(resultClicked)="projectClicked($event)">
|
||||
[showLoadMoreButton]="getResults().length < resultsCount?.[activeLink]"
|
||||
(resultClicked)="projectClicked($event)"
|
||||
(loadMoreClicked)="loadMoreClicked.emit()">
|
||||
</sm-search-results>
|
||||
</div>
|
||||
|
||||
@@ -54,4 +53,13 @@
|
||||
(projectCardClicked)="pipelineClicked($event)"
|
||||
></sm-pipeline-card>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #openDatasetTemplate let-dataset="result">
|
||||
<sm-simple-dataset-card
|
||||
[hideMenu]="true"
|
||||
[project]="dataset"
|
||||
(projectCardClicked)="openDatasetClicked($event)"
|
||||
></sm-simple-dataset-card>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import "../../../../webapp-common/shared/ui-components/styles/mixins/common";
|
||||
|
||||
.category-link{
|
||||
padding-right: 10px;
|
||||
padding-right: 24px;
|
||||
font-size: $font-size-lg;
|
||||
color: $blue-100;
|
||||
opacity: 0.3;
|
||||
|
||||
@@ -3,25 +3,32 @@ import {Project} from '~/business-logic/model/projects/project';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {ITask} from '~/business-logic/model/al-task';
|
||||
import {Model} from '~/business-logic/model/models/model';
|
||||
import {ActiveSearchLink} from '~/features/dashboard/containers/dashboard-search/dashboard-search.component';
|
||||
import {activeLinksList, ActiveSearchLink, activeSearchLink} from '~/features/dashboard-search/dashboard-search.consts';
|
||||
|
||||
@Component({
|
||||
selector : 'sm-search-results-page',
|
||||
selector: 'sm-search-results-page',
|
||||
templateUrl: './search-results-page.component.html',
|
||||
styleUrls : ['./search-results-page.component.scss']
|
||||
styleUrls: ['./search-results-page.component.scss']
|
||||
})
|
||||
export class SearchResultsPageComponent {
|
||||
public searchPages = activeSearchLink;
|
||||
public activeLinksList = activeLinksList;
|
||||
|
||||
@Input() projectsList: Array<Project> = [];
|
||||
@Input() experimentsList: Array<Task> = [];
|
||||
@Input() modelsList: Array<Model> = [];
|
||||
@Input() modelsList: Array<Model> = [];
|
||||
@Input() pipelinesList: Array<Project> = [];
|
||||
@Input() datasetsList: Array<Project> = [];
|
||||
@Input() activeLink: ActiveSearchLink;
|
||||
@Input() resultsCount: Map<ActiveSearchLink, number>;
|
||||
|
||||
@Output() projectSelected = new EventEmitter<Project>();
|
||||
@Output() activeLinkChanged = new EventEmitter<string>();
|
||||
@Output() projectSelected = new EventEmitter<Project>();
|
||||
@Output() activeLinkChanged = new EventEmitter<string>();
|
||||
@Output() experimentSelected = new EventEmitter<ITask>();
|
||||
@Output() modelSelected = new EventEmitter<Model>();
|
||||
@Output() pipelineSelected = new EventEmitter<Project>();
|
||||
@Output() modelSelected = new EventEmitter<Model>();
|
||||
@Output() pipelineSelected = new EventEmitter<Project>();
|
||||
@Output() openDatasetSelected = new EventEmitter<Project>();
|
||||
@Output() loadMoreClicked = new EventEmitter();
|
||||
|
||||
public projectClicked(project: Project) {
|
||||
this.projectSelected.emit(project);
|
||||
@@ -39,18 +46,20 @@ export class SearchResultsPageComponent {
|
||||
this.pipelineSelected.emit(pipeline);
|
||||
}
|
||||
|
||||
getResults() {
|
||||
return this[`${this.activeLink}List`];
|
||||
public openDatasetClicked(project: Project) {
|
||||
this.openDatasetSelected.emit(project);
|
||||
}
|
||||
|
||||
getResults = () => this[`${this.activeLink}List`];
|
||||
|
||||
getCardHeight() {
|
||||
switch (this.activeLink) {
|
||||
case 'projects':
|
||||
case activeSearchLink.projects:
|
||||
return 246;
|
||||
case 'experiments':
|
||||
case 'models':
|
||||
case activeSearchLink.experiments:
|
||||
case activeSearchLink.models:
|
||||
return 264;
|
||||
case 'pipelines':
|
||||
case activeSearchLink.pipelines:
|
||||
return 226;
|
||||
default:
|
||||
return 250;
|
||||
|
||||
43
src/app/features/datasets/datasets-routing.module.ts
Normal file
43
src/app/features/datasets/datasets-routing.module.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {SimpleDatasetVersionsComponent} from '@common/datasets/simple-dataset-versions/simple-dataset-versions.component';
|
||||
import {
|
||||
SimpleDatasetVersionInfoComponent
|
||||
} from '@common/datasets/simple-dataset-version-info/simple-dataset-version-info.component';
|
||||
import {SimpleDatasetsComponent} from '@common/datasets/simple-datasets/simple-datasets.component';
|
||||
import {EntityTypeEnum} from '../../shared/constants/non-common-consts';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path : '',
|
||||
component: SimpleDatasetsComponent,
|
||||
data : {search: true}
|
||||
},
|
||||
{
|
||||
path: 'simple/:projectId',
|
||||
data: {search: true},
|
||||
children: [
|
||||
{path: '', redirectTo: 'experiments', pathMatch: 'full'},
|
||||
{
|
||||
path: 'experiments',
|
||||
component: SimpleDatasetVersionsComponent,
|
||||
children: [
|
||||
{
|
||||
path: ':versionId', component: SimpleDatasetVersionInfoComponent,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'compare-experiments',
|
||||
data: {entityType: EntityTypeEnum.dataset},
|
||||
loadChildren: () => import('@common/experiments-compare/experiments-compare.module').then(m => m.ExperimentsCompareModule)
|
||||
},
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class DatasetsRoutingModule {}
|
||||
77
src/app/features/datasets/datasets.module.ts
Normal file
77
src/app/features/datasets/datasets.module.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {SMSharedModule} from '@common/shared/shared.module';
|
||||
import {ChipsModule} from '@common/shared/ui-components/buttons/chips/chips.module';
|
||||
import {SimpleDatasetsComponent} from '@common/datasets/simple-datasets/simple-datasets.component';
|
||||
import {CommonProjectsModule} from '@common/projects/common-projects.module';
|
||||
import {ProjectsSharedModule} from '~/features/projects/shared/projects-shared.module';
|
||||
import {ExperimentSharedModule} from '~/features/experiments/shared/experiment-shared.module';
|
||||
import {ExperimentCompareSharedModule} from '@common/experiments-compare/shared/experiment-compare-shared.module';
|
||||
import {ExperimentsCommonModule} from '@common/experiments/common-experiments.module';
|
||||
import {CommonExperimentSharedModule} from '@common/experiments/shared/common-experiment-shared.module';
|
||||
import {
|
||||
SimpleDatasetVersionsComponent
|
||||
} from '@common/datasets/simple-dataset-versions/simple-dataset-versions.component';
|
||||
import {AngularSplitModule} from 'angular-split';
|
||||
import {
|
||||
SimpleDatasetVersionMenuComponent
|
||||
} from '@common/datasets/simple-dataset-version-menu/simple-dataset-version-menu.component';
|
||||
import {
|
||||
SimpleDatasetVersionInfoComponent
|
||||
} from '@common/datasets/simple-dataset-version-info/simple-dataset-version-info.component';
|
||||
import {PipelinesControllerModule} from '@common/pipelines-controller/pipelines-controller.module';
|
||||
import {DatasetsRoutingModule} from '~/features/datasets/datasets-routing.module';
|
||||
import {DatasetVersionStepComponent} from '@common/datasets/dataset-version-step/dataset-version-step.component';
|
||||
import {DatasetsSharedModule} from '~/features/datasets/shared/datasets-shared.module';
|
||||
import {
|
||||
SimpleDatasetVersionDetailsComponent
|
||||
} from '../../webapp-common/datasets/simple-dataset-version-details/simple-dataset-version-details.component';
|
||||
import {
|
||||
SimpleDatasetVersionContentComponent
|
||||
} from '../../webapp-common/datasets/simple-dataset-version-content/simple-dataset-version-content.component';
|
||||
import {
|
||||
SimpleDatasetVersionPreviewComponent
|
||||
} from '../../webapp-common/datasets/simple-dataset-version-preview/simple-dataset-version-preview.component';
|
||||
import {DebugImagesModule} from '../../webapp-common/debug-images/debug-images.module';
|
||||
import {
|
||||
ExperimentOutputLogModule
|
||||
} from '../../webapp-common/experiments/shared/experiment-output-log/experiment-output-log.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
SMSharedModule,
|
||||
ChipsModule,
|
||||
FormsModule,
|
||||
AngularSplitModule,
|
||||
CommonProjectsModule,
|
||||
ProjectsSharedModule,
|
||||
CommonExperimentSharedModule,
|
||||
ExperimentSharedModule,
|
||||
DatasetsRoutingModule,
|
||||
ExperimentsCommonModule,
|
||||
ExperimentCompareSharedModule,
|
||||
PipelinesControllerModule,
|
||||
DatasetsSharedModule,
|
||||
DebugImagesModule,
|
||||
ExperimentOutputLogModule
|
||||
],
|
||||
declarations: [
|
||||
SimpleDatasetsComponent,
|
||||
SimpleDatasetVersionsComponent,
|
||||
SimpleDatasetVersionMenuComponent,
|
||||
SimpleDatasetVersionInfoComponent,
|
||||
DatasetVersionStepComponent,
|
||||
SimpleDatasetVersionDetailsComponent,
|
||||
SimpleDatasetVersionContentComponent,
|
||||
SimpleDatasetVersionPreviewComponent,
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
exports: []
|
||||
})
|
||||
export class DatasetsModule {
|
||||
}
|
||||
|
||||
28
src/app/features/datasets/shared/datasets-shared.module.ts
Normal file
28
src/app/features/datasets/shared/datasets-shared.module.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {SMSharedModule} from '@common/shared/shared.module';
|
||||
import {ChipsModule} from '@common/shared/ui-components/buttons/chips/chips.module';
|
||||
import {SharedModule} from '~/shared/shared.module';
|
||||
import {SimpleDatasetCardComponent} from '@common/datasets/simple-dataset-card/simple-dataset-card.component';
|
||||
import {ProjectsSharedModule} from '~/features/projects/shared/projects-shared.module';
|
||||
|
||||
const _declerations = [
|
||||
SimpleDatasetCardComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
SMSharedModule,
|
||||
ChipsModule,
|
||||
SharedModule,
|
||||
ProjectsSharedModule,
|
||||
],
|
||||
declarations: [..._declerations],
|
||||
exports : [..._declerations]
|
||||
})
|
||||
|
||||
export class DatasetsSharedModule { }
|
||||
@@ -25,7 +25,8 @@ export abstract class ExperimentDetailsReverterServiceBase {
|
||||
tags: exp.tags,
|
||||
execution: this.revertExecution(exp),
|
||||
artifacts: this.revertArtifacts(exp),
|
||||
configuration: this.revertconfiguration(exp.configuration)
|
||||
configuration: this.revertconfiguration(exp.configuration),
|
||||
info: this.revertInfo(exp)
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -56,4 +57,6 @@ export abstract class ExperimentDetailsReverterServiceBase {
|
||||
abstract revertArtifacts(exp: ITask);
|
||||
|
||||
abstract revertExecution(exp: ITask);
|
||||
|
||||
abstract revertInfo(exp: ITask);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ export abstract class ExperimentCompareDetailsBase {
|
||||
artifacts: this.buildSectionTree(experiment, 'artifacts', mergedExperiment),
|
||||
execution: this.buildSectionTree(experiment, 'execution', mergedExperiment),
|
||||
configuration: this.buildSectionTree(experiment, 'configuration', mergedExperiment),
|
||||
info: this.buildSectionTree(experiment, 'info', mergedExperiment),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,58 +1,66 @@
|
||||
<nav [smOverflows]="splitSize" (onOverflows)="navbarOverflowed($event)" [class.minimized]="minimized">
|
||||
<span [routerLink]="['execution']" routerLinkActive #rlaExecution="routerLinkActive" queryParamsHandling="preserve">
|
||||
<sm-navbar-item header="execution" [active]="rlaExecution.isActive" class="small-nav"></sm-navbar-item>
|
||||
</span>
|
||||
<span [routerLink]="['hyper-params/hyper-param/_empty_']" queryParamsHandling="merge">
|
||||
<sm-navbar-item header="configuration"
|
||||
class="small-nav"
|
||||
[active]="(routerConfig$| async)?.includes('hyper-params')"></sm-navbar-item>
|
||||
</span>
|
||||
<span [routerLink]="['artifacts']" routerLinkActive #rlaModel="routerLinkActive" queryParamsHandling="preserve">
|
||||
<sm-navbar-item header="artifacts"
|
||||
class="small-nav"
|
||||
[active]="rlaModel.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span [routerLink]="['general']" routerLinkActive #rlaGeneral="routerLinkActive" queryParamsHandling="preserve">
|
||||
<sm-navbar-item header="info"
|
||||
class="small-nav"
|
||||
[active]="rlaGeneral.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
|
||||
<nav (smOverflows)="navbarOverflowed($event)" [overflowTrigger]="splitSize" [overflowDelay]="800"
|
||||
[class.minimized]="minimized">
|
||||
<span [routerLink]="['execution']" routerLinkActive="active" #rlaExecution="routerLinkActive"
|
||||
queryParamsHandling="preserve">
|
||||
<sm-navbar-item header="execution" [active]="rlaExecution.isActive" class="small-nav"></sm-navbar-item>
|
||||
</span>
|
||||
<span [routerLink]="['hyper-params/hyper-param/_empty_']" queryParamsHandling="merge">
|
||||
<sm-navbar-item header="configuration" class="small-nav" [active]="(routerConfig$| async)?.includes('hyper-params')"></sm-navbar-item>
|
||||
</span>
|
||||
<span [routerLink]="['artifacts']" routerLinkActive="active" #rlaModel="routerLinkActive" queryParamsHandling="preserve">
|
||||
<sm-navbar-item header="artifacts" class="small-nav" [active]="rlaModel.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span [routerLink]="['general']" routerLinkActive="active" #rlaGeneral="routerLinkActive" queryParamsHandling="preserve">
|
||||
<sm-navbar-item header="info" class="small-nav" [active]="rlaGeneral.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span [matMenuTriggerFor]="results" *ngIf="overflow">
|
||||
<sm-navbar-item header="results"
|
||||
class="small-nav"
|
||||
[multi]="true"
|
||||
[active]="rlaDebug.isActive || rlaPlots.isActive || rlaScalars.isActive || rlaLog.isActive"></sm-navbar-item>
|
||||
<sm-navbar-item
|
||||
header="results" class="small-nav" [multi]="true"
|
||||
[active]="rlaDebug.isActive || rlaPlots.isActive || rlaScalars.isActive || rlaLog.isActive"
|
||||
></sm-navbar-item>
|
||||
</span>
|
||||
<div class="d-inline-block" [style.visibility]="overflow ? 'hidden' : 'visible'">
|
||||
<span [routerLink]="baseInfoRoute.concat(['log'])" routerLinkActive queryParamsHandling="preserve"
|
||||
#rlaLog="routerLinkActive">
|
||||
<sm-navbar-item class="small-nav" header="console" [active]="rlaLog.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span [routerLink]="baseInfoRoute.concat(['metrics','scalar'])" routerLinkActive queryParamsHandling="preserve"
|
||||
#rlaScalars="routerLinkActive">
|
||||
<sm-navbar-item class="small-nav" header="Scalars" [active]="rlaScalars.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span [routerLink]="baseInfoRoute.concat(['metrics','plots'])" routerLinkActive queryParamsHandling="preserve"
|
||||
#rlaPlots="routerLinkActive">
|
||||
<sm-navbar-item class="small-nav" header="PLOTS" [active]="rlaPlots.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span [routerLink]="baseInfoRoute.concat(['debugImages'])" routerLinkActive queryParamsHandling="preserve"
|
||||
#rlaDebug="routerLinkActive">
|
||||
<sm-navbar-item class="small-nav" header="DEBUG SAMPLES" [active]="rlaDebug.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span
|
||||
[routerLink]="baseInfoRoute.concat(['log'])"
|
||||
routerLinkActive="active"
|
||||
queryParamsHandling="preserve"
|
||||
#rlaLog="routerLinkActive"
|
||||
>
|
||||
<sm-navbar-item class="small-nav" header="console" [active]="rlaLog.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span
|
||||
[routerLink]="baseInfoRoute.concat(['metrics','scalar'])"
|
||||
routerLinkActive="active"
|
||||
queryParamsHandling="preserve"
|
||||
#rlaScalars="routerLinkActive"
|
||||
>
|
||||
<sm-navbar-item class="small-nav" header="Scalars" [active]="rlaScalars.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span
|
||||
[routerLink]="baseInfoRoute.concat(['metrics','plots'])" routerLinkActive="active"
|
||||
queryParamsHandling="preserve"
|
||||
#rlaPlots="routerLinkActive"
|
||||
>
|
||||
<sm-navbar-item class="small-nav" header="PLOTS" [active]="rlaPlots.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
<span
|
||||
[routerLink]="baseInfoRoute.concat(['debugImages'])"
|
||||
routerLinkActive="active"
|
||||
queryParamsHandling="preserve"
|
||||
#rlaDebug="routerLinkActive"
|
||||
>
|
||||
<sm-navbar-item class="small-nav" header="DEBUG SAMPLES" [active]="rlaDebug.isActive"></sm-navbar-item>
|
||||
</span>
|
||||
</div>
|
||||
<mat-menu #results="matMenu">
|
||||
<button mat-menu-item [routerLink]="baseInfoRoute.concat(['log'])" [class.active]="rlaLog.isActive">CONSOLE</button>
|
||||
<button mat-menu-item [routerLink]="baseInfoRoute.concat(['metrics','scalar'])"
|
||||
[class.active]="rlaScalars.isActive">SCALARS
|
||||
</button>
|
||||
<button mat-menu-item [routerLink]="baseInfoRoute.concat(['metrics','plots'])" [class.active]="rlaPlots.isActive">
|
||||
PLOTS
|
||||
</button>
|
||||
<button mat-menu-item [routerLink]="baseInfoRoute.concat(['debugImages'])" [class.active]="rlaDebug.isActive">DEBUG
|
||||
SAMPLES
|
||||
</button>
|
||||
<button mat-menu-item [routerLink]="baseInfoRoute.concat(['log'])" [class.active]="rlaLog.isActive"
|
||||
>CONSOLE</button>
|
||||
<button mat-menu-item [routerLink]="baseInfoRoute.concat(['metrics','scalar'])" [class.active]="rlaScalars.isActive"
|
||||
>SCALARS</button>
|
||||
<button mat-menu-item [routerLink]="baseInfoRoute.concat(['metrics','plots'])" [class.active]="rlaPlots.isActive"
|
||||
>PLOTS</button>
|
||||
<button mat-menu-item [routerLink]="baseInfoRoute.concat(['debugImages'])" [class.active]="rlaDebug.isActive"
|
||||
>DEBUG SAMPLES</button>
|
||||
</mat-menu>
|
||||
<ng-content select="[refresh]"></ng-content>
|
||||
</nav>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<sm-overlay [backdropActive]="backdropActive$|async"></sm-overlay>
|
||||
<sm-experiment-info-header-status-icon-label
|
||||
[status]=" (selectedExperiment)?.status || selectedExperiment?.status"
|
||||
[development]="isDevelopment"
|
||||
></sm-experiment-info-header-status-icon-label>
|
||||
<div class="experiment-output-container light-theme" [class.minimized]="minimized">
|
||||
<sm-experiment-info-header
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Actions} from '@ngrx/effects';
|
||||
import {Store} from '@ngrx/store';
|
||||
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 {CommonExperimentOutputState} from '../../../webapp-common/experiments/reducers/common-experiment-output.reducer';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import { downloadFullLog } from '@common/experiments/actions/common-experiment-output.actions';
|
||||
import {filter, map} from 'rxjs/operators';
|
||||
import {HTTP} from '~/app.constants';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ExperimentOutputEffects {
|
||||
|
||||
constructor(private actions$: Actions, private store: Store<CommonExperimentOutputState>, private apiTasks: ApiTasksService,
|
||||
private authApi: ApiAuthService, private taskBl: BlTasksService, private eventsApi: ApiEventsService) {
|
||||
constructor(private actions$: Actions) {
|
||||
}
|
||||
|
||||
|
||||
downloadFullLog$ = createEffect(() => this.actions$.pipe(
|
||||
ofType(downloadFullLog),
|
||||
filter(action => !!action.experimentId),
|
||||
map(action => {
|
||||
const a = document.createElement('a');
|
||||
a.href = `${HTTP.API_BASE_URL}/events.download_task_log?line_type=text&task=${action.experimentId}`;
|
||||
a.target = '_blank';
|
||||
a.download = 'Log';
|
||||
a.click();
|
||||
})
|
||||
), {dispatch: false});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {TaskStatusEnum} from '../../../business-logic/model/tasks/taskStatusEnum';
|
||||
import {TaskStatusEnum} from '~/business-logic/model/tasks/taskStatusEnum';
|
||||
import {ExperimentTableColFieldsEnum} from './experiments.model';
|
||||
import {TaskTypeEnum} from '~/business-logic/model/tasks/taskTypeEnum';
|
||||
import {Model} from '~/business-logic/model/models/model';
|
||||
|
||||
export type experimentSectionsEnum =
|
||||
'MODEL_INPUT'
|
||||
@@ -62,3 +60,11 @@ export const EXPERIMENTS_STATUS_LABELS = {
|
||||
export const DevWarningEnabled = false;
|
||||
|
||||
export const excludeTypes = [];
|
||||
|
||||
export const DATASETS_STATUS_LABEL = {
|
||||
[TaskStatusEnum.InProgress]: 'Uploading',
|
||||
[TaskStatusEnum.Completed]: 'Final',
|
||||
Running: 'Uploading',
|
||||
Completed: 'Final'
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {SMSharedModule} from '../../webapp-common/shared/shared.module';
|
||||
import {SMSharedModule} from '@common/shared/shared.module';
|
||||
import {StoreModule} from '@ngrx/store';
|
||||
import {ProjectRouterModule} from './projects-routing.module';
|
||||
import {projectsReducer} from './projects.reducer';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {CommonProjectsModule} from '../../webapp-common/projects/common-projects.module';
|
||||
import {CommonProjectsModule} from '@common/projects/common-projects.module';
|
||||
|
||||
export const projectSyncedKeys = ['showHidden', 'tableModeAwareness'];
|
||||
export const projectSyncedKeys = ['showHidden', 'tableModeAwareness', 'orderBy', 'sortOrder'];
|
||||
|
||||
@NgModule({
|
||||
imports : [
|
||||
|
||||
@@ -6,7 +6,8 @@ import {selectSelectedTableModel} from '@common/models/reducers';
|
||||
import {createSelector} from '@ngrx/store';
|
||||
import {selectSelectedExperiment} from '~/features/experiments/reducers';
|
||||
import {selectRootProjects, selectSelectedProject} from '@common/core/reducers/projects.reducer';
|
||||
import {formatStaticCrumb, prepareLinkData} from '@common/layout/breadcrumbs/breadcrumbs-common.utils';
|
||||
import {formatStaticCrumb as commonFormatStaticCrumb, prepareLinkData} from '@common/layout/breadcrumbs/breadcrumbs-common.utils';
|
||||
import {IBreadcrumbsLink} from '@common/layout/breadcrumbs/breadcrumbs.component';
|
||||
|
||||
export interface IBreadcrumbs {
|
||||
project: Project;
|
||||
@@ -16,33 +17,42 @@ export interface IBreadcrumbs {
|
||||
task: Task;
|
||||
}
|
||||
|
||||
|
||||
export const formatStaticCrumb = (crumb: string): IBreadcrumbsLink => {
|
||||
if (!crumb) {
|
||||
return {url: null, name: null};
|
||||
}
|
||||
return commonFormatStaticCrumb(crumb);
|
||||
};
|
||||
|
||||
export const selectBreadcrumbsStringsBase = createSelector(
|
||||
selectSelectedProject, selectSelectedExperiment, selectSelectedTableModel, selectRootProjects,
|
||||
(project, experiment, model, projects) =>
|
||||
({project, experiment, model, projects}) as IBreadcrumbs);
|
||||
|
||||
export const prepareNames = (data: IBreadcrumbs, isPipeline?: boolean, fullScreen = false) => {
|
||||
export const prepareNames = (data: IBreadcrumbs, customProject?: boolean, fullScreen = false) => {
|
||||
const project = prepareLinkData(data.project, true);
|
||||
if (data.project) {
|
||||
const subProjects = [];
|
||||
let subProjectsNames = [data.project?.name];
|
||||
if (!isPipeline) {
|
||||
if (!customProject) {
|
||||
subProjectsNames = data.project?.name?.split('/');
|
||||
}
|
||||
const allProjects = [
|
||||
...data.projects,
|
||||
{id: '*', name: 'All Experiments'},
|
||||
data.project
|
||||
];
|
||||
let currentName = '';
|
||||
subProjectsNames.forEach(name => {
|
||||
const subProjects = subProjectsNames.map(name => {
|
||||
currentName += currentName ? ('/' + name) : name;
|
||||
const foundProject = [
|
||||
...data.projects,
|
||||
{id: '*', name: 'All Experiments'},
|
||||
{...data.project}
|
||||
].find(proj => currentName === proj.name);
|
||||
subProjects.push(foundProject);
|
||||
});
|
||||
const subProjectsLinks = subProjects.map(subProject => ({
|
||||
return allProjects.find(proj => currentName === proj.name);
|
||||
}) || [];
|
||||
|
||||
const subProjectsLinks = subProjects.map((subProject, index, arr) => ({
|
||||
name: subProject?.name.substring(subProject?.name.lastIndexOf('/') + 1),
|
||||
url: isPipeline ? `pipelines/${subProject?.id}/experiments` :
|
||||
fullScreen ? `projects/${subProject?.id}/experiments/${data.experiment.id}` :
|
||||
url: customProject ?
|
||||
data.project?.system_tags?.includes('pipeline') ? `pipelines/${subProject?.id}/experiments` : '' :
|
||||
fullScreen && index === (arr.length - 1) ? `projects/${subProject?.id}/experiments/${data?.experiment?.id}` :
|
||||
subProject?.name === data.project?.name && data.project?.sub_projects?.length === 0 ?
|
||||
`projects/${subProject?.id}` :
|
||||
`projects/${subProject?.id}/projects`
|
||||
@@ -50,16 +60,21 @@ export const prepareNames = (data: IBreadcrumbs, isPipeline?: boolean, fullScree
|
||||
project.name = project?.name.substring(project.name.lastIndexOf('/') + 1);
|
||||
project.subCrumbs = subProjectsLinks;
|
||||
}
|
||||
const task = prepareLinkData(data.task);
|
||||
const task = prepareLinkData(data.task);
|
||||
const experiment = (data.experiment) ? prepareLinkData(data.experiment, true) : {};
|
||||
const output = formatStaticCrumb('');
|
||||
const accountAdministration = formatStaticCrumb('account-administration');
|
||||
const output = formatStaticCrumb('');
|
||||
const accountAdministration = formatStaticCrumb('account-administration');
|
||||
const experiments = formatStaticCrumb('experiments');
|
||||
const models = formatStaticCrumb('models');
|
||||
const compare = formatStaticCrumb('compare-experiments');
|
||||
const models = formatStaticCrumb('models');
|
||||
const compare = customProject ?
|
||||
data.project?.system_tags?.includes('pipeline') ?
|
||||
{url: 'compare-experiments', name: 'Compare Runs'} :
|
||||
{url: 'compare-experiments', name: 'Compare Versions'}
|
||||
: formatStaticCrumb('compare-experiments');
|
||||
|
||||
return {
|
||||
...(project.url !== '*' && {':projectId': project}),
|
||||
':taskId' : task,
|
||||
':taskId': task,
|
||||
':controllerId': experiment,
|
||||
'compare-experiments': compare,
|
||||
output,
|
||||
|
||||
@@ -1,53 +1,67 @@
|
||||
<!-- DON'T FORGET TO ADD LINK TO 404 PAGE TOO -->
|
||||
|
||||
<div class="side-nav">
|
||||
<div class="item logo">
|
||||
<div class="item-icon">
|
||||
<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">-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="currentUser">
|
||||
<a class="item d-block" #rlaHome="routerLinkActive" routerLink="/dashboard" routerLinkActive (click)="resetSearch()"
|
||||
smTooltip="DASHBOARD" [matTooltipShowDelay]="0" matTooltipPosition="right">
|
||||
<a class="item d-block"
|
||||
routerLink="/dashboard"
|
||||
routerLinkActive="active"
|
||||
(click)="resetSearch()"
|
||||
smTooltip="DASHBOARD"
|
||||
[matTooltipShowDelay]="0"
|
||||
matTooltipPosition="right"
|
||||
>
|
||||
<div class="item-icon">
|
||||
<i [class]="'al-icon al-ico-home al-color ' + (rlaHome.isActive ? 'neon-yellow': 'blue-300')" ></i>
|
||||
<i class="al-icon al-ico-home al-color blue-300"></i>
|
||||
</div>
|
||||
<div class="caption">dashboard</div>
|
||||
</a>
|
||||
|
||||
<a class="item d-block" smTooltip="PROJECTS" [matTooltipShowDelay]="0" matTooltipPosition="right" routerLink="/projects"
|
||||
[routerLinkActive] #rlaProjects="routerLinkActive">
|
||||
<div class="item-icon">
|
||||
<i [class]="'al-icon al-ico-projects al-color ' + (rlaProjects.isActive ? 'neon-yellow': 'blue-300')" ></i>
|
||||
<a class="item d-block"
|
||||
smTooltip="PROJECTS"
|
||||
[matTooltipShowDelay]="0"
|
||||
matTooltipPosition="right"
|
||||
routerLink="/projects"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<div class="item-icon">
|
||||
<i class="al-icon al-ico-projects al-color blue-300"></i>
|
||||
</div>
|
||||
<div class="caption">projects</div>
|
||||
</a>
|
||||
<a class="item d-block"
|
||||
routerLink="/datasets"
|
||||
routerLinkActive="active"
|
||||
smTooltip="DATASETS"
|
||||
[matTooltipShowDelay]="0"
|
||||
matTooltipPosition="right">
|
||||
<div class="item-icon">
|
||||
<i class="al-icon al-ico-datasets al-color blue-300"></i>
|
||||
</div>
|
||||
<div class="caption">datasets</div>
|
||||
</a>
|
||||
<a class="item d-block"
|
||||
routerLink="/pipelines"
|
||||
routerLinkActive
|
||||
#rlaDatasets="routerLinkActive"
|
||||
routerLinkActive="active"
|
||||
smTooltip="PIPELINES"
|
||||
[matTooltipShowDelay]="0"
|
||||
matTooltipPosition="right">
|
||||
<div class="item-icon">
|
||||
<i [class]="'al-icon al-ico-pipelines al-color ' + (rlaDatasets.isActive ? 'neon-yellow': 'blue-300')"></i>
|
||||
<i class="al-icon al-ico-pipelines al-color blue-300"></i>
|
||||
</div>
|
||||
<div class="caption">pipelines</div>
|
||||
</a>
|
||||
<a class="item d-block"
|
||||
*smCheckPermission="true"
|
||||
routerLink="/workers-and-queues"
|
||||
routerLinkActive
|
||||
#rlaQueues="routerLinkActive"
|
||||
routerLinkActive="active"
|
||||
smTooltip="WORKERS & QUEUES"
|
||||
[matTooltipShowDelay]="0"
|
||||
matTooltipPosition="right">
|
||||
<div class="item-icon">
|
||||
<i [class]="'al-icon al-ico-queues al-color ' + (rlaQueues.isActive ? 'neon-yellow': 'blue-300')" ></i>
|
||||
<i class="al-icon al-ico-queues al-color blue-300"></i>
|
||||
</div>
|
||||
<div class="caption">workers & queues</div>
|
||||
</a>
|
||||
@@ -66,14 +80,13 @@
|
||||
</div>
|
||||
<div class="caption">Ignite</div>
|
||||
</a>
|
||||
<a class="item d-block" href="https://github.com/allegroai/clearml" target="_blank"
|
||||
smTooltip="GitHub Repository"
|
||||
[matTooltipShowDelay]="0"
|
||||
matTooltipPosition="right">
|
||||
<a *ngIf="environment.slackLink" class="item d-block" [href]="environment.slackLink"
|
||||
target="_blank"
|
||||
smTooltip="Community support on Slack" [matTooltipShowDelay]="0" matTooltipPosition="right">
|
||||
<div class="item-icon">
|
||||
<i class="fab fa-github fa-24 al-color blue-300"></i>
|
||||
<i class="al-icon al-ico-slack md al-color blue-300"></i>
|
||||
</div>
|
||||
<div class="caption">GitHub</div>
|
||||
<div class="caption">Slack</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,6 @@ $transition-speed: 0.15;
|
||||
transition: opacity $transition-speed + $transition-delay + s;
|
||||
opacity: 1;
|
||||
height: 100%;
|
||||
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -29,6 +28,7 @@ $transition-speed: 0.15;
|
||||
box-shadow: unset;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
a:hover {
|
||||
@@ -67,8 +67,11 @@ $transition-speed: 0.15;
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
}
|
||||
.neon {
|
||||
color: $neon-yellow-betterinchrome;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.item-icon i {
|
||||
color: $neon-yellow
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {selectSelectedProjectId} from '../../webapp-common/core/reducers/project
|
||||
import {Observable} from 'rxjs';
|
||||
import {Router} from '@angular/router';
|
||||
import {ConfigurationService} from '../../webapp-common/shared/services/configuration.service';
|
||||
import {SearchDeactivate} from '../../webapp-common/dashboard-search/dashboard-search.actions';
|
||||
import {searchDeactivate} from '../../webapp-common/dashboard-search/dashboard-search.actions';
|
||||
|
||||
@Component({
|
||||
selector : 'sm-side-nav',
|
||||
@@ -25,7 +25,7 @@ export class SideNavComponent {
|
||||
|
||||
|
||||
public resetSearch() {
|
||||
this.store.dispatch(new SearchDeactivate());
|
||||
this.store.dispatch(searchDeactivate());
|
||||
}
|
||||
|
||||
get guestUser(): boolean {
|
||||
|
||||
@@ -5,7 +5,8 @@ export enum EntityTypeEnum {
|
||||
experiment = 'experiment',
|
||||
model = 'model',
|
||||
project = 'project',
|
||||
controller = 'pipeline run'
|
||||
controller = 'pipeline run',
|
||||
dataset = 'version'
|
||||
}
|
||||
|
||||
export enum CircleTypeEnum {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@font-face {
|
||||
font-family: '#{$icomoon-font-family}';
|
||||
src: url('./#{$icomoon-font-family}.ttf?2i0eh5') format('truetype');
|
||||
src: url('./#{$icomoon-font-family}.ttf?8714dr') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
@@ -23,6 +23,26 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.al-ico-upload {
|
||||
&:before {
|
||||
content: $al-ico-upload;
|
||||
}
|
||||
}
|
||||
.al-ico-min-panel {
|
||||
&:before {
|
||||
content: $al-ico-min-panel;
|
||||
}
|
||||
}
|
||||
.al-ico-max-panel {
|
||||
&:before {
|
||||
content: $al-ico-max-panel;
|
||||
}
|
||||
}
|
||||
.al-ico-datasets {
|
||||
&:before {
|
||||
content: $al-ico-datasets;
|
||||
}
|
||||
}
|
||||
.al-ico-t-logo-b {
|
||||
&:before {
|
||||
content: $al-ico-t-logo-b;
|
||||
@@ -43,11 +63,6 @@
|
||||
content: $al-ico-projects;
|
||||
}
|
||||
}
|
||||
.al-ico-datasets {
|
||||
&:before {
|
||||
content: $al-ico-datasets;
|
||||
}
|
||||
}
|
||||
.al-ico-queues {
|
||||
&:before {
|
||||
content: $al-ico-queues;
|
||||
@@ -1171,6 +1186,11 @@
|
||||
content: $al-ico-eye-outline;
|
||||
}
|
||||
}
|
||||
.al-ico-csv {
|
||||
&:before {
|
||||
content: $al-ico-csv;
|
||||
}
|
||||
}
|
||||
.al-ico-status-draft {
|
||||
&:before {
|
||||
content: $al-ico-status-draft;
|
||||
@@ -1191,9 +1211,9 @@
|
||||
content: $al-ico-status-pending;
|
||||
}
|
||||
}
|
||||
.al-ico-status-skiped {
|
||||
.al-ico-status-skipped {
|
||||
&:before {
|
||||
content: $al-ico-status-skiped;
|
||||
content: $al-ico-status-skipped;
|
||||
}
|
||||
}
|
||||
.al-ico-status-cached {
|
||||
@@ -1226,9 +1246,4 @@
|
||||
content: $al-ico-status-completed;
|
||||
}
|
||||
}
|
||||
.al-ico-status-queued {
|
||||
&:before {
|
||||
content: $al-ico-enqueue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,11 +1,14 @@
|
||||
$icomoon-font-family: "trains" !default;
|
||||
$icomoon-font-path: "fonts" !default;
|
||||
|
||||
$al-ico-upload: "\e9cc";
|
||||
$al-ico-min-panel: "\e9ca";
|
||||
$al-ico-max-panel: "\e9cb";
|
||||
$al-ico-datasets: "\e90b";
|
||||
$al-ico-t-logo-b: "\e908";
|
||||
$al-ico-bars-menu: "\e933";
|
||||
$al-ico-home: "\e909";
|
||||
$al-ico-projects: "\e90a";
|
||||
$al-ico-datasets: "\e90b";
|
||||
$al-ico-queues: "\e90c";
|
||||
$al-ico-annotator: "\e90d";
|
||||
$al-ico-account: "\e998";
|
||||
@@ -226,11 +229,12 @@ $al-ico-video: "\e9c5";
|
||||
$al-ico-less-than: "\e9c6";
|
||||
$al-ico-greater-than: "\e9c7";
|
||||
$al-ico-eye-outline: "\e9c8";
|
||||
$al-ico-csv: "\e9c9";
|
||||
$al-ico-status-draft: "\e902";
|
||||
$al-ico-status-published: "\e906";
|
||||
$al-ico-status-aborted-sec: "\e918";
|
||||
$al-ico-status-pending: "\e903";
|
||||
$al-ico-status-skiped: "\e9bd";
|
||||
$al-ico-status-skipped: "\e9bd";
|
||||
$al-ico-status-cached: "\e9be";
|
||||
$al-ico-status-executed: "\e9ac";
|
||||
$al-ico-status-running: "\e904";
|
||||
|
||||
97
src/app/webapp-common/assets/icons/datasets-empty-state.svg
Normal file
97
src/app/webapp-common/assets/icons/datasets-empty-state.svg
Normal file
@@ -0,0 +1,97 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="338" height="284" viewBox="0 0 338 284">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
opacity: 0.996;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: none;
|
||||
stroke: #5a658e;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: rgba(0,154,255,0.2);
|
||||
}
|
||||
|
||||
.cls-4 {
|
||||
fill: #009aff;
|
||||
}
|
||||
|
||||
.cls-5 {
|
||||
fill: #153856;
|
||||
}
|
||||
|
||||
.cls-6 {
|
||||
fill: rgba(90,101,142,0.2);
|
||||
}
|
||||
|
||||
.cls-7 {
|
||||
fill: #5a658e;
|
||||
}
|
||||
|
||||
.cls-8 {
|
||||
fill: #1a1e2c;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="datasets-empty-state" class="cls-1" transform="translate(-526.845 -206)">
|
||||
<g id="g" transform="translate(0 -26)">
|
||||
<g id="connectors" transform="translate(574.84 261.145)">
|
||||
<g id="connector" transform="translate(124.333 123.032) rotate(180)">
|
||||
<line id="connector-2" data-name="connector" class="cls-2" y2="24.176" transform="translate(3.429 -3.103)"/>
|
||||
<path id="connector-head" class="cls-2" d="M936.044,346.708l3.429,3.429,3.429-3.429" transform="translate(-936.044 -328.283)"/>
|
||||
</g>
|
||||
<g id="connector-3" data-name="connector" transform="translate(124.333 45.033) rotate(180)">
|
||||
<line id="connector-4" data-name="connector" class="cls-2" y2="24.176" transform="translate(3.429 -3.103)"/>
|
||||
<path id="connector-head-2" data-name="connector-head" class="cls-2" d="M936.044,346.708l3.429,3.429,3.429-3.429" transform="translate(-936.044 -328.283)"/>
|
||||
</g>
|
||||
<g id="connector-5" data-name="connector" transform="translate(124.333 201.033) rotate(180)">
|
||||
<path id="connector-head-3" data-name="connector-head" class="cls-2" d="M936.044,346.708l3.429,3.429,3.429-3.429" transform="translate(-936.044 -328.283)"/>
|
||||
<path id="Path_767" data-name="Path 767" class="cls-2" d="M9.81,21.056c0-44.9,22.7-49.264,31.507-49.264" transform="translate(-6.403)"/>
|
||||
</g>
|
||||
<g id="connector-6" data-name="connector" transform="translate(117.474 179.179)">
|
||||
<path id="connector-head-4" data-name="connector-head" class="cls-2" d="M0,3.429,3.429,0,6.858,3.429" transform="translate(0 0)"/>
|
||||
<path id="Path_767-2" data-name="Path 767" class="cls-2" d="M0,0C0,44.9,22.7,49.264,31.507,49.264" transform="translate(3.407 0.798)"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="step" transform="translate(625.146 229)">
|
||||
<path id="step-2" data-name="step" class="cls-3" d="M4,0H133a4,4,0,0,1,4,4V33a0,0,0,0,1,0,0H0a0,0,0,0,1,0,0V4A4,4,0,0,1,4,0Z" transform="translate(2.699 3)"/>
|
||||
<path id="step-status" class="cls-4" d="M0,0H137a0,0,0,0,1,0,0V13a4,4,0,0,1-4,4H4a4,4,0,0,1-4-4V0A0,0,0,0,1,0,0Z" transform="translate(2.699 36)"/>
|
||||
<rect id="time" class="cls-5" width="52" height="4" rx="2" transform="translate(79.699 43)"/>
|
||||
<rect id="time-2" data-name="time" class="cls-5" width="20" height="4" rx="2" transform="translate(10.699 43)"/>
|
||||
<rect id="title" class="cls-4" width="55" height="4" rx="2" transform="translate(10.699 17)"/>
|
||||
</g>
|
||||
<g id="step-3" data-name="step" transform="translate(625.146 307)">
|
||||
<path id="step-4" data-name="step" class="cls-3" d="M4,0H133a4,4,0,0,1,4,4V33a0,0,0,0,1,0,0H0a0,0,0,0,1,0,0V4A4,4,0,0,1,4,0Z" transform="translate(2.699 3)"/>
|
||||
<path id="step-status-2" data-name="step-status" class="cls-4" d="M0,0H137a0,0,0,0,1,0,0V13a4,4,0,0,1-4,4H4a4,4,0,0,1-4-4V0A0,0,0,0,1,0,0Z" transform="translate(2.699 36)"/>
|
||||
<rect id="time-3" data-name="time" class="cls-5" width="34" height="4" rx="2" transform="translate(97.699 43)"/>
|
||||
<rect id="time-4" data-name="time" class="cls-5" width="30" height="4" rx="2" transform="translate(10.699 43)"/>
|
||||
<rect id="title-2" data-name="title" class="cls-4" width="36" height="4" rx="2" transform="translate(10.699 17)"/>
|
||||
</g>
|
||||
<g id="step-5" data-name="step" transform="translate(625.146 385)">
|
||||
<path id="step-6" data-name="step" class="cls-3" d="M4,0H133a4,4,0,0,1,4,4V33a0,0,0,0,1,0,0H0a0,0,0,0,1,0,0V4A4,4,0,0,1,4,0Z" transform="translate(2.699 3)"/>
|
||||
<path id="step-status-3" data-name="step-status" class="cls-4" d="M0,0H137a0,0,0,0,1,0,0V13a4,4,0,0,1-4,4H4a4,4,0,0,1-4-4V0A0,0,0,0,1,0,0Z" transform="translate(2.699 36)"/>
|
||||
<rect id="time-5" data-name="time" class="cls-5" width="50" height="4" rx="2" transform="translate(81.699 43)"/>
|
||||
<rect id="time-6" data-name="time" class="cls-5" width="26" height="4" rx="2" transform="translate(10.699 43)"/>
|
||||
<rect id="title-3" data-name="title" class="cls-4" width="37" height="4" rx="2" transform="translate(10.699 17)"/>
|
||||
</g>
|
||||
<g id="step-7" data-name="step" transform="translate(725.146 463)">
|
||||
<path id="step-8" data-name="step" class="cls-6" d="M4,0H133a4,4,0,0,1,4,4V33a0,0,0,0,1,0,0H0a0,0,0,0,1,0,0V4A4,4,0,0,1,4,0Z" transform="translate(2.699 3)"/>
|
||||
<path id="step-status-4" data-name="step-status" class="cls-7" d="M0,0H137a0,0,0,0,1,0,0V13a4,4,0,0,1-4,4H4a4,4,0,0,1-4-4V0A0,0,0,0,1,0,0Z" transform="translate(2.699 36)"/>
|
||||
<rect id="time-7" data-name="time" class="cls-8" width="50" height="4" rx="2" transform="translate(81.699 43)"/>
|
||||
<rect id="time-8" data-name="time" class="cls-8" width="26" height="4" rx="2" transform="translate(10.699 43)"/>
|
||||
<rect id="title-4" data-name="title" class="cls-7" width="37" height="4" rx="2" transform="translate(10.699 17)"/>
|
||||
</g>
|
||||
<g id="step-9" data-name="step" transform="translate(524.146 463)">
|
||||
<path id="step-10" data-name="step" class="cls-6" d="M4,0H133a4,4,0,0,1,4,4V33a0,0,0,0,1,0,0H0a0,0,0,0,1,0,0V4A4,4,0,0,1,4,0Z" transform="translate(2.699 3)"/>
|
||||
<path id="step-status-5" data-name="step-status" class="cls-7" d="M0,0H137a0,0,0,0,1,0,0V13a4,4,0,0,1-4,4H4a4,4,0,0,1-4-4V0A0,0,0,0,1,0,0Z" transform="translate(2.699 36)"/>
|
||||
<rect id="time-9" data-name="time" class="cls-8" width="50" height="4" rx="2" transform="translate(81.699 43)"/>
|
||||
<rect id="time-10" data-name="time" class="cls-8" width="26" height="4" rx="2" transform="translate(10.699 43)"/>
|
||||
<rect id="title-5" data-name="title" class="cls-7" width="37" height="4" rx="2" transform="translate(10.699 17)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 6.3 KiB |
@@ -1,53 +1,20 @@
|
||||
import {Action, createAction, props} from '@ngrx/store';
|
||||
import {createAction, props} from '@ngrx/store';
|
||||
import {SearchState} from '@common/common-search/common-search.reducer';
|
||||
|
||||
const COMMON_SEARCH_PREFIX = 'CommonSearch_';
|
||||
|
||||
export const COMMON_SEARCH_ACTIONS = {
|
||||
SET_IS_SEARCHING : COMMON_SEARCH_PREFIX + 'SET_IS_SEARCHING',
|
||||
RESET_SEARCH : COMMON_SEARCH_PREFIX + 'RESET_SEARCH',
|
||||
INIT_SEARCH : COMMON_SEARCH_PREFIX + 'INIT_SEARCH',
|
||||
SET_SEARCH_QUERY : COMMON_SEARCH_PREFIX + 'SET_SEARCH_QUERY',
|
||||
SET_SEARCH_PLACEHOLDER: COMMON_SEARCH_PREFIX + 'SET_SEARCH_PLACEHOLDER',
|
||||
SET_SEARCH_ACTIVE : COMMON_SEARCH_PREFIX + 'SET_SEARCH_ACTIVE',
|
||||
};
|
||||
|
||||
export class SetIsSearching implements Action {
|
||||
readonly type = COMMON_SEARCH_ACTIONS.SET_IS_SEARCHING;
|
||||
|
||||
constructor(public payload: boolean) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class SetSearchActive implements Action {
|
||||
readonly type = COMMON_SEARCH_ACTIONS.SET_SEARCH_ACTIVE;
|
||||
|
||||
constructor(public payload: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
export const setSearchQuery = createAction(
|
||||
COMMON_SEARCH_ACTIONS.SET_SEARCH_QUERY,
|
||||
props<{query: string; regExp?: boolean}>()
|
||||
export const setSearching = createAction(
|
||||
COMMON_SEARCH_PREFIX + 'SET_IS_SEARCHING',
|
||||
props<{payload: boolean}>()
|
||||
);
|
||||
|
||||
export class SetSearchPlaceholder implements Action {
|
||||
readonly type = COMMON_SEARCH_ACTIONS.SET_SEARCH_PLACEHOLDER;
|
||||
export const setSearchQuery = createAction(
|
||||
COMMON_SEARCH_PREFIX + 'SET_SEARCH_QUERY',
|
||||
props<SearchState['searchQuery']>()
|
||||
);
|
||||
|
||||
constructor(public payload: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export class ResetSearch implements Action {
|
||||
readonly type = COMMON_SEARCH_ACTIONS.RESET_SEARCH;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
export class InitSearch implements Action {
|
||||
readonly type = COMMON_SEARCH_ACTIONS.INIT_SEARCH;
|
||||
|
||||
constructor(public payload: string) {
|
||||
}
|
||||
}
|
||||
export const resetSearch = createAction(COMMON_SEARCH_PREFIX + 'RESET_SEARCH');
|
||||
export const initSearch = createAction(
|
||||
COMMON_SEARCH_PREFIX + 'INIT_SEARCH',
|
||||
props<{payload: string}>()
|
||||
);
|
||||
|
||||
@@ -3,13 +3,13 @@ import {CommonModule} from '@angular/common';
|
||||
import {SMSharedModule} from '../shared/shared.module';
|
||||
import {CommonSearchComponent} from './containers/common-search/common-search.component';
|
||||
import {StoreModule} from '@ngrx/store';
|
||||
import {commonSearchReducer} from './common-search.reducer';
|
||||
import {searchReducer} from './common-search.reducer';
|
||||
|
||||
@NgModule({
|
||||
imports : [
|
||||
CommonModule,
|
||||
SMSharedModule,
|
||||
StoreModule.forFeature('commonSearch', commonSearchReducer),
|
||||
StoreModule.forFeature('commonSearch', searchReducer),
|
||||
],
|
||||
declarations: [CommonSearchComponent],
|
||||
exports : [CommonSearchComponent]
|
||||
|
||||
@@ -1,58 +1,33 @@
|
||||
import {createFeatureSelector, createSelector} from '@ngrx/store';
|
||||
import {COMMON_SEARCH_ACTIONS, setSearchQuery} from './common-search.actions';
|
||||
import {createFeatureSelector, createReducer, createSelector, on} from '@ngrx/store';
|
||||
import {initSearch, resetSearch, setSearching, setSearchQuery} from './common-search.actions';
|
||||
|
||||
|
||||
export interface ICommonSearchState {
|
||||
export interface SearchState {
|
||||
isSearching: boolean;
|
||||
searchQuery: {query: string; regExp?: boolean};
|
||||
searchQuery: {query: string; regExp?: boolean; original?: string};
|
||||
placeholder: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
// Todo remove selectedProjectId
|
||||
const commonSearchInitState: ICommonSearchState = {
|
||||
const searchInitState: SearchState = {
|
||||
isSearching: false,
|
||||
searchQuery: null,
|
||||
placeholder: null,
|
||||
active : false
|
||||
};
|
||||
|
||||
export function commonSearchReducer<ActionReducer>(state: ICommonSearchState = commonSearchInitState, action): ICommonSearchState {
|
||||
export const searchReducer = createReducer(
|
||||
searchInitState,
|
||||
on(setSearching, (state, action) => ({...state, isSearching: action.payload})),
|
||||
on(setSearchQuery, (state, action) => ({...state, searchQuery: action})),
|
||||
on(initSearch, (state, action) => ({...searchInitState, placeholder: action.payload || 'Search'})),
|
||||
on(resetSearch, () => ({...searchInitState, placeholder: 'Search'})),
|
||||
);
|
||||
|
||||
switch (action.type) {
|
||||
case COMMON_SEARCH_ACTIONS.SET_IS_SEARCHING:
|
||||
return {...state, isSearching: action.payload};
|
||||
case setSearchQuery.type:
|
||||
return {...state, searchQuery: action as ReturnType<typeof setSearchQuery>};
|
||||
case COMMON_SEARCH_ACTIONS.SET_SEARCH_PLACEHOLDER:
|
||||
return {...state, placeholder: action.payload};
|
||||
case COMMON_SEARCH_ACTIONS.SET_SEARCH_ACTIVE:
|
||||
return {...state, active: action.payload};
|
||||
case COMMON_SEARCH_ACTIONS.INIT_SEARCH:
|
||||
return {
|
||||
...state,
|
||||
isSearching: false,
|
||||
searchQuery: commonSearchInitState.searchQuery,
|
||||
placeholder: action.payload || 'Search',
|
||||
active : false
|
||||
};
|
||||
case COMMON_SEARCH_ACTIONS.RESET_SEARCH:
|
||||
return {
|
||||
...state,
|
||||
isSearching: false,
|
||||
searchQuery: commonSearchInitState.searchQuery,
|
||||
placeholder: 'Search',
|
||||
active : false
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export const selectCommonSearch = createFeatureSelector<ICommonSearchState>('commonSearch');
|
||||
export const selectIsSearching = createSelector(selectCommonSearch, (state: ICommonSearchState): boolean => state ? state.isSearching : false);
|
||||
export const selectSearchQuery = createSelector(selectCommonSearch, (state: ICommonSearchState) => state?.searchQuery || commonSearchInitState.searchQuery);
|
||||
export const selectPlaceholder = createSelector(selectCommonSearch, (state: ICommonSearchState): string => state ? state.placeholder : '');
|
||||
export const selectActiveSearch = createSelector(selectCommonSearch, (state: ICommonSearchState): boolean => state ? state.active : false);
|
||||
export const selectCommonSearch = createFeatureSelector<SearchState>('commonSearch');
|
||||
export const selectIsSearching = createSelector(selectCommonSearch, (state: SearchState): boolean => state ? state.isSearching : false);
|
||||
export const selectSearchQuery = createSelector(selectCommonSearch, (state: SearchState) => state?.searchQuery || searchInitState.searchQuery);
|
||||
export const selectPlaceholder = createSelector(selectCommonSearch, (state: SearchState): string => state ? state.placeholder : '');
|
||||
|
||||
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
<span class="search-container" [ngClass]="{'open': isSearching$ | async}">
|
||||
<span class="search-container" [class.open]="isSearching$ | async">
|
||||
<sm-search
|
||||
#search
|
||||
class="search-header"
|
||||
[value]="(searchQuery$ | async)?.query"
|
||||
[class.regex-error]="regexError"
|
||||
[value]="(searchQuery$ | async)?.original"
|
||||
[placeholder]="searchPlaceholder$ | async"
|
||||
[hideIcons]="true"
|
||||
[minimumChars]="minChars"
|
||||
(focusout)="onSearchFocusOut()"
|
||||
(valueChanged)="onSearchValueChanged($event)"
|
||||
>
|
||||
<i *ngIf="regexError" class="regexp al-icon al-ico-error-circle pointer" [smTooltip]="regexError" [matTooltipPosition]="'below'"></i>
|
||||
<i
|
||||
class="regexp al-ico-regex pointer"
|
||||
smClickStopPropagation
|
||||
[smTooltip]="'Regex'" [matTooltipPosition]="'below'"
|
||||
[class.active]="regExp"
|
||||
(click)="toggleRegExp()"></i>
|
||||
(click)="toggleRegExp(); search.searchBarInput.nativeElement.focus();"></i>
|
||||
</sm-search>
|
||||
</span>
|
||||
<ng-container *ngIf="searchActive">
|
||||
|
||||
@@ -33,6 +33,12 @@
|
||||
color: $blue-900;
|
||||
background-color: $blue-280;
|
||||
}
|
||||
|
||||
&.al-ico-error-circle {
|
||||
width: 0;
|
||||
right: 48px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
i.fa {
|
||||
@@ -52,4 +58,10 @@
|
||||
i.fa-times {
|
||||
margin-right:4px;
|
||||
}
|
||||
|
||||
::ng-deep sm-search.regex-error input {
|
||||
padding-right: 56px !important;
|
||||
border-color: $failed-red !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,37 +1,38 @@
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {ActivatedRoute, NavigationEnd, Router} from '@angular/router';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {SetIsSearching, setSearchQuery} from '../../common-search.actions';
|
||||
import {ICommonSearchState, selectIsSearching, selectPlaceholder, selectSearchQuery} from '../../common-search.reducer';
|
||||
import {setSearching, setSearchQuery} from '../../common-search.actions';
|
||||
import {SearchState, selectIsSearching, selectPlaceholder, selectSearchQuery} from '../../common-search.reducer';
|
||||
import {Observable} from 'rxjs';
|
||||
import {debounceTime, filter, tap} from 'rxjs/operators';
|
||||
import {SearchComponent} from '../../../shared/ui-components/inputs/search/search.component';
|
||||
import {SearchComponent} from '@common/shared/ui-components/inputs/search/search.component';
|
||||
|
||||
@Component({
|
||||
selector : 'sm-common-search',
|
||||
selector: 'sm-common-search',
|
||||
templateUrl: './common-search.component.html',
|
||||
styleUrls : ['./common-search.component.scss'],
|
||||
styleUrls: ['./common-search.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class CommonSearchComponent implements OnInit {
|
||||
public searchQuery$: Observable<ICommonSearchState['searchQuery']>;
|
||||
public searchQuery$: Observable<SearchState['searchQuery']>;
|
||||
public isSearching$: Observable<boolean>;
|
||||
public searchPlaceholder$: Observable<string>;
|
||||
public searchActive: boolean;
|
||||
|
||||
|
||||
|
||||
@ViewChild(SearchComponent) searchElem: SearchComponent;
|
||||
public regExp: boolean = false;
|
||||
private closeTimer: number;
|
||||
private queryString: string;
|
||||
minChars = 3;
|
||||
public regexError: boolean;
|
||||
|
||||
constructor(private store: Store<any>, private router: Router, private route: ActivatedRoute, private cdr: ChangeDetectorRef) {}
|
||||
constructor(private store: Store<any>, private router: Router, private route: ActivatedRoute, private cdr: ChangeDetectorRef) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.searchQuery$ = this.store.select(selectSearchQuery).pipe(tap(searchQuery => this.regExp = searchQuery?.regExp));
|
||||
this.isSearching$ = this.store.select(selectIsSearching);
|
||||
this.searchQuery$ = this.store.select(selectSearchQuery).pipe(tap(searchQuery => this.regExp = searchQuery?.regExp));
|
||||
this.isSearching$ = this.store.select(selectIsSearching);
|
||||
this.searchPlaceholder$ = this.store.select(selectPlaceholder).pipe(debounceTime(0));
|
||||
this.router.events.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe(() => {
|
||||
@@ -42,24 +43,40 @@ export class CommonSearchComponent implements OnInit {
|
||||
|
||||
onSearchValueChanged(query: string) {
|
||||
this.queryString = query;
|
||||
this.search();
|
||||
this.cdr.detectChanges();
|
||||
this.store.dispatch(setSearchQuery({query: this.regExp? this.queryString: this.queryString.trim(), regExp: this.regExp}));
|
||||
}
|
||||
|
||||
private search() {
|
||||
try {
|
||||
if (this.regExp) {
|
||||
new RegExp(this.queryString);
|
||||
}
|
||||
this.regexError = null;
|
||||
this.store.dispatch(setSearchQuery({
|
||||
query: this.regExp ? this.queryString : this.queryString.trim(),
|
||||
regExp: this.regExp,
|
||||
original: this.queryString
|
||||
}));
|
||||
} catch (e) {
|
||||
this.regexError = e.message?.replace(/:.+:/, ':');
|
||||
}
|
||||
}
|
||||
|
||||
openSearch() {
|
||||
window.clearTimeout(this.closeTimer);
|
||||
this.searchElem.searchBarInput.nativeElement.focus();
|
||||
this.store.dispatch(new SetIsSearching(true));
|
||||
this.store.dispatch(setSearching({payload: true}));
|
||||
}
|
||||
|
||||
onSearchFocusOut() {
|
||||
if (!this.searchElem.searchBarInput.nativeElement.value) {
|
||||
this.closeTimer = window.setTimeout(() => this.store.dispatch(new SetIsSearching(false)), 200);
|
||||
this.closeTimer = window.setTimeout(() => this.store.dispatch(setSearching({payload: false})), 200);
|
||||
}
|
||||
}
|
||||
|
||||
private setSearchActive() {
|
||||
let route = this.route.snapshot;
|
||||
let route = this.route.snapshot;
|
||||
let showSearch = false;
|
||||
while (route.firstChild) {
|
||||
route = route.firstChild;
|
||||
@@ -73,7 +90,7 @@ export class CommonSearchComponent implements OnInit {
|
||||
|
||||
clearSearch() {
|
||||
this.searchElem.clear();
|
||||
this.store.dispatch(new SetIsSearching(false));
|
||||
this.store.dispatch(setSearching({payload: false}));
|
||||
document.body.focus();
|
||||
}
|
||||
|
||||
@@ -81,7 +98,7 @@ export class CommonSearchComponent implements OnInit {
|
||||
this.regExp = !this.regExp;
|
||||
window.clearTimeout(this.closeTimer);
|
||||
if (this.queryString?.length >= this.minChars) {
|
||||
this.store.dispatch(setSearchQuery({query: this.queryString, regExp: this.regExp}));
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
@import "shared/ui-components/styles/material-palette";
|
||||
@import "assets/fonts/trains-icons.scss";
|
||||
@import "layout/layout";
|
||||
@import "shared/ui-components/styles/overrides/viewer-iterations-slider";
|
||||
|
||||
@include mat.core();
|
||||
//@import "../webapp-common/shared/ui-components/styles/material-theme.scss";
|
||||
@@ -161,8 +162,8 @@ h5.al-header {
|
||||
}
|
||||
|
||||
span.highlight-text {
|
||||
background: $neon-yellow-betterinchrome;
|
||||
border: 1px solid darken($neon-yellow-betterinchrome, 5%);
|
||||
background: $neon-yellow;
|
||||
border: 1px solid darken($neon-yellow, 5%);
|
||||
border-radius: 4px;
|
||||
padding: 0 2px;
|
||||
|
||||
@@ -458,6 +459,10 @@ body .clean-list {
|
||||
&.validation {
|
||||
background-color: #ff001f;
|
||||
}
|
||||
|
||||
&.break-line {
|
||||
white-space: pre-line;
|
||||
}
|
||||
}
|
||||
|
||||
&.parameter-tooltip {
|
||||
@@ -563,7 +568,7 @@ as-split {
|
||||
|
||||
$type-colors: (
|
||||
string: #ff8400,
|
||||
number: $neon-yellow-betterinchrome,
|
||||
number: $neon-yellow,
|
||||
boolean: #b938a4,
|
||||
date: #05668D,
|
||||
);
|
||||
@@ -637,7 +642,7 @@ $type-colors: (
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.image-displayer-dialog {
|
||||
.image-viewer-dialog {
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
@@ -744,7 +749,7 @@ button.btn.button-outline-dark {
|
||||
.sm-card-list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: $projects-header-padding 6px $projects-header-padding * 0.5 0;
|
||||
padding-top: $projects-header-padding;
|
||||
|
||||
.recent-title {
|
||||
display: flex;
|
||||
|
||||
@@ -2,6 +2,7 @@ import {MessageSeverityEnum, VIEW_PREFIX} from '~/app.constants';
|
||||
import {createAction, props} from '@ngrx/store';
|
||||
import {omit} from 'lodash/fp';
|
||||
import {HttpErrorResponse} from '@angular/common/http';
|
||||
import {Ace} from 'ace-builds';
|
||||
|
||||
export const setAutoRefresh = createAction(
|
||||
VIEW_PREFIX + '[set auto refresh]',
|
||||
@@ -51,6 +52,14 @@ export const visibilityChanged = createAction(
|
||||
props<{visible: boolean}>()
|
||||
);
|
||||
|
||||
export const saveAceCaretPosition = createAction(
|
||||
VIEW_PREFIX + '[save ace caret position]',
|
||||
props<{id: string; position: Ace.Point}>()
|
||||
);
|
||||
|
||||
export const resetAceCaretsPositions = createAction(VIEW_PREFIX + '[reset ace carets positions]');
|
||||
|
||||
|
||||
export const addMessage = createAction(
|
||||
VIEW_PREFIX + '[add message]',
|
||||
(severity: MessageSeverityEnum, msg: string, userActions?: {actions: any[]; name: string}[], suppressNextMessages?: boolean) =>
|
||||
|
||||
@@ -61,6 +61,11 @@ export const setSelectedProject = createAction(
|
||||
props<{ project: Project }>()
|
||||
);
|
||||
|
||||
export const setSelectedProjectStats = createAction(
|
||||
PROJECTS_PREFIX + '[set selected project statistics]',
|
||||
props<{ project: Project }>()
|
||||
);
|
||||
|
||||
export const resetSelectedProject = createAction(
|
||||
PROJECTS_PREFIX + 'RESET_SELECTED_PROJECT'
|
||||
);
|
||||
|
||||
@@ -27,7 +27,10 @@ export const setSelectedWorkspaceTab = createAction(
|
||||
);
|
||||
|
||||
|
||||
export const setFilterByUser = createAction(USERS_PREFIX +'SET_FILTERED_BY_USER', props<{showOnlyUserWork: boolean}>());
|
||||
export const setFilterByUser = createAction(
|
||||
USERS_PREFIX +'SET_FILTERED_BY_USER',
|
||||
props<{showOnlyUserWork: boolean}>()
|
||||
);
|
||||
|
||||
export const setUserWorkspacesFromUser = createAction(USERS_PREFIX + ' set user workspaces from current user');
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export class LayoutEffects {
|
||||
}
|
||||
let resultMessage: string;
|
||||
const subcode = get('error.meta.result_subcode', action.serverError);
|
||||
if (subcode) {
|
||||
if (subcode || subcode === 0) {
|
||||
resultMessage = `Error ${subcode} : ${get('error.meta.result_msg', action.serverError)}`;
|
||||
}
|
||||
this.alertDialogRef = this.dialog.open(AlertDialogComponent, {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Action, Store} from '@ngrx/store';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {ApiProjectsService} from '~/business-logic/api-services/projects.service';
|
||||
import * as actions from '../actions/projects.actions';
|
||||
import {catchError, filter, map, mergeMap, switchMap, withLatestFrom} from 'rxjs/operators';
|
||||
import {catchError, expand, filter, map, mergeMap, reduce, switchMap, withLatestFrom} from 'rxjs/operators';
|
||||
import {requestFailed} from '../actions/http.actions';
|
||||
import {activeLoader, deactivateLoader, setServerError} from '../actions/layout.actions';
|
||||
import {setSelectedModels} from '../../models/actions/models-view.actions';
|
||||
@@ -12,12 +12,12 @@ import {MatDialog} from '@angular/material/dialog';
|
||||
import {ApiOrganizationService} from '~/business-logic/api-services/organization.service';
|
||||
import {OrganizationGetTagsResponse} from '~/business-logic/model/organization/organizationGetTagsResponse';
|
||||
import {selectRouterParams} from '../reducers/router-reducer';
|
||||
import {forkJoin, of} from 'rxjs';
|
||||
import {EMPTY, forkJoin, of} from 'rxjs';
|
||||
import {ProjectsGetTaskTagsResponse} from '~/business-logic/model/projects/projectsGetTaskTagsResponse';
|
||||
import {ProjectsGetModelTagsResponse} from '~/business-logic/model/projects/projectsGetModelTagsResponse';
|
||||
import {
|
||||
selectAllProjectsUsers,
|
||||
selectLastUpdate,
|
||||
selectLastUpdate, selectRootProjects,
|
||||
selectSelectedMetricVariantForCurrProject,
|
||||
selectSelectedProjectId
|
||||
} from '../reducers/projects.reducer';
|
||||
@@ -27,20 +27,20 @@ import {createMetricColumn} from '@common/shared/utils/tableParamEncode';
|
||||
import {ITask} from '~/business-logic/model/al-task';
|
||||
import {TasksGetAllExRequest} from '~/business-logic/model/tasks/tasksGetAllExRequest';
|
||||
import {setSelectedExperiments} from '../../experiments/actions/common-experiments-view.actions';
|
||||
import {selectShowHidden} from '~/features/projects/projects.reducer';
|
||||
import {setActiveWorkspace} from '@common/core/actions/users.actions';
|
||||
import {ProjectsGetAllExResponse} from '~/business-logic/model/projects/projectsGetAllExResponse';
|
||||
import {Project} from '~/business-logic/model/projects/project';
|
||||
import {ApiUsersService} from '~/business-logic/api-services/users.service';
|
||||
import { get } from 'lodash/fp';
|
||||
import {get, last} from 'lodash/fp';
|
||||
import {selectProjects, selectShowHidden} from '@common/projects/common-projects.reducer';
|
||||
import {setShowHidden} from '@common/projects/common-projects.actions';
|
||||
import {ProjectsGetAllExRequest} from '~/business-logic/model/projects/projectsGetAllExRequest';
|
||||
|
||||
export const ALL_PROJECTS_OBJECT = {id: '*', name: 'All Experiments'};
|
||||
|
||||
@Injectable()
|
||||
export class ProjectsEffects {
|
||||
private pageSize: number = 500;
|
||||
private lastUpdateSoFar: string;
|
||||
private scrollId: string = null;
|
||||
|
||||
constructor(
|
||||
private actions$: Actions, private projectsApi: ApiProjectsService, private orgApi: ApiOrganizationService,
|
||||
@@ -56,31 +56,42 @@ export class ProjectsEffects {
|
||||
|
||||
getProjects$ = createEffect(() => this.actions$.pipe(
|
||||
ofType(actions.getAllSystemProjects),
|
||||
withLatestFrom(this.store.select(selectShowHidden), this.store.select(selectLastUpdate)),
|
||||
switchMap(([, showHidden, lastUpdate]) => this.projectsApi.projectsGetAllEx({
|
||||
withLatestFrom(
|
||||
this.store.select(selectShowHidden),
|
||||
this.store.select(selectLastUpdate),
|
||||
),
|
||||
switchMap(([, showHidden, lastUpdate]) => {
|
||||
const query = {
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
scroll_id: null,
|
||||
size: this.pageSize,
|
||||
scroll_id: this.scrollId,
|
||||
order_by: ['last_update'],
|
||||
last_update: lastUpdate ? [lastUpdate, null] : undefined,
|
||||
only_fields: ['name', 'company', 'parent', 'last_update'], search_hidden: showHidden
|
||||
...(lastUpdate && {last_update: [lastUpdate, null]}),
|
||||
only_fields: ['name', 'company', 'parent', 'last_update'],
|
||||
search_hidden: showHidden
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
} as any)
|
||||
.pipe(mergeMap((res: ProjectsGetAllExResponse) => {
|
||||
const resultsActions: Action[] = [actions.setAllProjects({projects: res.projects as unknown as Project[], updating: !!lastUpdate})];
|
||||
if (res.projects.length >= this.pageSize) {
|
||||
this.scrollId = res.scroll_id;
|
||||
this.lastUpdateSoFar = res.projects[res.projects.length - 1].last_update;
|
||||
resultsActions.push(actions.getAllSystemProjects());
|
||||
} else {
|
||||
resultsActions.push(actions.setLastUpdate({lastUpdate: res.projects[res.projects.length - 1]?.last_update || this.lastUpdateSoFar || lastUpdate}));
|
||||
this.scrollId = null;
|
||||
this.lastUpdateSoFar = null;
|
||||
}
|
||||
return resultsActions;
|
||||
})
|
||||
)
|
||||
)
|
||||
} as ProjectsGetAllExRequest;
|
||||
return this.projectsApi.projectsGetAllEx(query)
|
||||
.pipe(
|
||||
expand((res: ProjectsGetAllExResponse) => res.scroll_id && res.projects.length >= this.pageSize ?
|
||||
this.projectsApi.projectsGetAllEx({
|
||||
...query,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
scroll_id: res.scroll_id,
|
||||
}) :
|
||||
EMPTY
|
||||
),
|
||||
reduce((acc, res: ProjectsGetAllExResponse) => acc.concat(res.projects), []),
|
||||
);
|
||||
}),
|
||||
withLatestFrom(this.store.select(selectRootProjects)),
|
||||
mergeMap(([projects, rootProjects]) => [
|
||||
actions.setAllProjects({
|
||||
projects: projects as Project[],
|
||||
updating: rootProjects.length > 0
|
||||
}),
|
||||
actions.setLastUpdate({lastUpdate: last(projects)?.last_update})
|
||||
])
|
||||
));
|
||||
|
||||
resetProjects$ = createEffect(() => this.actions$.pipe(
|
||||
@@ -204,7 +215,7 @@ export class ProjectsEffects {
|
||||
const end = started + (task.active_duration ?? 0) * 1000;
|
||||
return {
|
||||
id: task.id,
|
||||
y: get(col.id, task),
|
||||
y: get(col.id, task), // col.id is a path (e.g.) last_metric.x.max_value, must use lodash get
|
||||
x: end,
|
||||
name: task.name,
|
||||
status: task.status,
|
||||
@@ -218,7 +229,7 @@ export class ProjectsEffects {
|
||||
));
|
||||
|
||||
resetRootProjects = createEffect(() => this.actions$.pipe(
|
||||
ofType(setActiveWorkspace, actions.refetchProjects),
|
||||
ofType(setActiveWorkspace, actions.refetchProjects, setShowHidden),
|
||||
mergeMap(() => [
|
||||
actions.resetProjects(),
|
||||
actions.getAllSystemProjects()
|
||||
@@ -228,8 +239,10 @@ export class ProjectsEffects {
|
||||
getAllProjectsUsersEffect = createEffect(() => this.actions$.pipe(
|
||||
ofType(actions.getAllSystemProjects),
|
||||
switchMap(() => this.usersApi.usersGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
order_by: ['name'],
|
||||
only_fields: ['name'],
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}, null, 'body', true).pipe(
|
||||
mergeMap(res => [actions.setAllProjectUsers(res)]),
|
||||
catchError(error => [
|
||||
@@ -247,10 +260,12 @@ export class ProjectsEffects {
|
||||
switchMap(([action, all]) => (!action.projectId || action.projectId === '*' ?
|
||||
of({users: all}) :
|
||||
this.usersApi.usersGetAllEx({
|
||||
order_by: ['name'],
|
||||
only_fields: ['name'],
|
||||
active_in_projects: [action.projectId]
|
||||
}, null, 'body', true)).pipe(
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
order_by: ['name'],
|
||||
only_fields: ['name'],
|
||||
active_in_projects: [action.projectId]
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}, null, 'body', true)).pipe(
|
||||
mergeMap(res => [actions.setProjectUsers(res)]),
|
||||
catchError(error => [
|
||||
requestFailed(error),
|
||||
@@ -262,9 +277,11 @@ export class ProjectsEffects {
|
||||
getExtraUsersEffect = createEffect(() => this.actions$.pipe(
|
||||
ofType(actions.getFilteredUsers),
|
||||
switchMap(action => this.usersApi.usersGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
order_by: ['name'],
|
||||
only_fields: ['name'],
|
||||
id: action.filteredUsers || []
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}, null, 'body', true).pipe(
|
||||
mergeMap(res => [
|
||||
actions.setProjectExtraUsers(res),
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '../actions/common-auth.actions';
|
||||
import {CredentialKey} from '~/business-logic/model/auth/credentialKey';
|
||||
import {inBucket} from '@common/settings/admin/base-admin.service';
|
||||
import {filter, map, takeWhile, timeoutWith} from 'rxjs/operators';
|
||||
import {filter, map, takeWhile, timeout} from 'rxjs/operators';
|
||||
|
||||
export interface Credentials {
|
||||
Bucket?: string;
|
||||
@@ -31,11 +31,11 @@ export interface AuthState {
|
||||
showLocalFilePopup: boolean;
|
||||
localFilesPopupURLs: Array<string>;
|
||||
revokeSucceed: boolean;
|
||||
credentials: {[workSpaceId: string]: CredentialKeyExt[]};
|
||||
credentials: { [workSpaceId: string]: CredentialKeyExt[] };
|
||||
newCredential: CredentialKeyExt;
|
||||
dontShowAgainForBucketEndpoint: string;
|
||||
s3BucketCredentials: { bucketCredentials: Credentials[] };
|
||||
signedUrls: {[url: string]: {signed: string; expires: number}};
|
||||
signedUrls: { [url: string]: { signed: string; expires: number } };
|
||||
}
|
||||
|
||||
export const initAuth: AuthState = {
|
||||
@@ -54,12 +54,12 @@ export const initAuth: AuthState = {
|
||||
export const selectAuth = state => state.auth as AuthState;
|
||||
|
||||
// Auth selectors
|
||||
export const selectRevokeSucceed = createSelector(selectAuth, state => state.revokeSucceed);
|
||||
export const selectCredentials = createSelector(selectAuth, state => state.credentials);
|
||||
export const selectNewCredential = createSelector(selectAuth, state => state.newCredential);
|
||||
export const selectS3BucketCredentials = createSelector(selectAuth, state => state.s3BucketCredentials);
|
||||
export const selectRevokeSucceed = createSelector(selectAuth, state => state.revokeSucceed);
|
||||
export const selectCredentials = createSelector(selectAuth, state => state.credentials);
|
||||
export const selectNewCredential = createSelector(selectAuth, state => state.newCredential);
|
||||
export const selectS3BucketCredentials = createSelector(selectAuth, state => state.s3BucketCredentials);
|
||||
export const selectS3BucketCredentialsBucketCredentials = createSelector(selectAuth, state => state.s3BucketCredentials?.bucketCredentials);
|
||||
export const selectShowLocalFilesPopUp = createSelector(selectAuth, state => state.showLocalFilePopup);
|
||||
export const selectShowLocalFilesPopUp = createSelector(selectAuth, state => state.showLocalFilePopup);
|
||||
export const selectDontShowAgainForBucketEndpoint = createSelector(selectAuth, state => state.dontShowAgainForBucketEndpoint);
|
||||
export const selectSignedUrls = createSelector(selectAuth, state => state.signedUrls);
|
||||
export const selectSignedUrl = url => createSelector(selectAuth, state => state.signedUrls[url]);
|
||||
@@ -67,11 +67,14 @@ export const getSignedUrlOrOrigin$ = (url: string, store: Store) => store.pipe(
|
||||
select(selectSignedUrl(url)),
|
||||
filter(signed => !!signed?.signed),
|
||||
map(signed => signed?.signed),
|
||||
timeoutWith(900, store.select(selectSignedUrl(url))
|
||||
.pipe(
|
||||
takeWhile( signed => signed !== null),
|
||||
map(signed => signed?.signed || url)
|
||||
)
|
||||
timeout({
|
||||
first: 900,
|
||||
with: () => store.select(selectSignedUrl(url))
|
||||
.pipe(
|
||||
takeWhile(signed => signed !== null),
|
||||
map(signed => signed?.signed || url)
|
||||
)
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
@@ -114,7 +117,9 @@ export const commonAuthReducer = [
|
||||
[action.workspaceId]: [
|
||||
...(state.credentials[action.workspaceId] || []),
|
||||
...(Object.keys(action.newCredential).length > 0 ? [{...action.newCredential, company: action.workspaceId}] : [])
|
||||
]}})),
|
||||
]
|
||||
}
|
||||
})),
|
||||
on(setCredentialLabel, (state, action) => ({
|
||||
...state,
|
||||
newCredential: {...state.newCredential, label: action.label},
|
||||
@@ -122,14 +127,18 @@ export const commonAuthReducer = [
|
||||
...state.credentials,
|
||||
[action.credential.company]: state.credentials[action.credential.company]?.map(cred =>
|
||||
cred.access_key === action.credential.access_key ? {...action.credential, label: action.label} : cred)
|
||||
}})),
|
||||
on(removeCredential, (state, action) => ({ ...state, credentials: {
|
||||
}
|
||||
})),
|
||||
on(removeCredential, (state, action) => ({
|
||||
...state, credentials: {
|
||||
...state.credentials,
|
||||
[action.workspaceId]: state.credentials[action.workspaceId].filter((cred => cred.access_key !== action.accessKey))
|
||||
}})),
|
||||
}
|
||||
})),
|
||||
on(updateAllCredentials, (state, action) => ({
|
||||
...state,
|
||||
credentials: {[action.credentials[0]?.company || action.workspace]: action.credentials, ...action.extra}, revokeSucceed: false})),
|
||||
credentials: {[action.credentials[0]?.company || action.workspace]: action.credentials, ...action.extra}, revokeSucceed: false
|
||||
})),
|
||||
on(setSignedUrl, (state, action) => ({...state, signedUrls: {...state.signedUrls, [action.url]: {signed: action.signed, expires: action.expires}}})),
|
||||
on(removeSignedUrl, (state, action) => ({...state, signedUrls: {...state.signedUrls, [action.url]: null}})),
|
||||
] as ReducerTypes<AuthState, any>[];
|
||||
|
||||
@@ -114,9 +114,15 @@ export const projectsReducer = createReducer(
|
||||
};
|
||||
}),
|
||||
on(projectsActions.setSelectedProject, (state, action) => ({...state, selectedProject: action.project, extraUsers: []})),
|
||||
on(projectsActions.setSelectedProjectStats, (state, action) => ({
|
||||
...state,
|
||||
selectedProject: {
|
||||
...state.selectedProject,
|
||||
stats: action.project?.stats
|
||||
}})),
|
||||
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))};
|
||||
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, users: [], extraUsers: []})),
|
||||
on(projectsActions.updateProjectCompleted, (state, action) => ({
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface UsersState {
|
||||
workspaces: GetCurrentUserResponseUserObjectCompany[];
|
||||
showOnlyUserWork: boolean;
|
||||
serverVersions: { server: string; api: string };
|
||||
gettingStarted: any;
|
||||
}
|
||||
|
||||
export const initUsers: UsersState = {
|
||||
@@ -28,6 +29,7 @@ export const initUsers: UsersState = {
|
||||
workspaces: [],
|
||||
showOnlyUserWork: false,
|
||||
serverVersions: null,
|
||||
gettingStarted: null
|
||||
};
|
||||
|
||||
export const users = state => state.users as UsersState;
|
||||
@@ -39,6 +41,7 @@ export const selectSelectedWorkspaceTab = createSelector(users, state => state.s
|
||||
export const selectWorkspaces = createSelector(users, state => state.workspaces);
|
||||
export const selectShowOnlyUserWork = createSelector(users, state => state.showOnlyUserWork);
|
||||
export const selectServerVersions = createSelector(users, state => state.serverVersions);
|
||||
export const selectGettingStarted = createSelector(users, state => state.gettingStarted);
|
||||
|
||||
export const usersReducerFunctions = [
|
||||
on(fetchCurrentUser, state => ({...state})),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {createReducer, createSelector, on, ReducerTypes} from '@ngrx/store';
|
||||
import * as layoutActions from '../actions/layout.actions';
|
||||
import {apiRequest, requestFailed} from '@common/core/actions/http.actions';
|
||||
import {Ace} from 'ace-builds';
|
||||
|
||||
export interface ViewState {
|
||||
loading: { [endpoint: string]: boolean };
|
||||
@@ -17,6 +18,7 @@ export interface ViewState {
|
||||
neverShowPopupAgain: string[];
|
||||
plotlyReady: boolean;
|
||||
aceReady: boolean;
|
||||
aceCaretPosition: { [key: string]: Ace.Point };
|
||||
preferencesReady: boolean;
|
||||
showUserFocus: boolean;
|
||||
}
|
||||
@@ -24,7 +26,6 @@ export interface ViewState {
|
||||
export const initViewState: ViewState = {
|
||||
loading: {},
|
||||
dialog: false,
|
||||
|
||||
notification: null,
|
||||
loggedOut: false,
|
||||
backdropActive: false,
|
||||
@@ -37,6 +38,7 @@ export const initViewState: ViewState = {
|
||||
neverShowPopupAgain: [],
|
||||
plotlyReady: false,
|
||||
aceReady: false,
|
||||
aceCaretPosition: {},
|
||||
preferencesReady: false,
|
||||
showUserFocus: false,
|
||||
};
|
||||
@@ -57,6 +59,7 @@ export const selectFirstLogin = createSelector(views, state => state.firstLogin)
|
||||
export const selectFirstLoginAt = createSelector(views, state => state.firstLoginAt);
|
||||
export const selectPlotlyReady = createSelector(views, state => state.plotlyReady);
|
||||
export const selectAceReady = createSelector(views, state => state.aceReady);
|
||||
export const selectAceCaretPosition = createSelector(views, state => state.aceCaretPosition);
|
||||
export const selectNeverShowPopups = createSelector(views, (state): string[] => state.neverShowPopupAgain);
|
||||
export const selectShowUserFocus = createSelector(views, state => state.showUserFocus);
|
||||
|
||||
@@ -77,9 +80,18 @@ export const viewReducers = [
|
||||
})),
|
||||
on(layoutActions.visibilityChanged, (state, action) => ({...state, applicationVisible: action.visible})),
|
||||
on(layoutActions.setScaleFactor, (state, action) => ({...state, scaleFactor: action.scale})),
|
||||
on(layoutActions.firstLogin, (state, action) => ({...state, firstLogin: action.first, firstLoginAt: new Date().getTime()})),
|
||||
on(layoutActions.firstLogin, (state, action) => ({
|
||||
...state,
|
||||
firstLogin: action.first,
|
||||
firstLoginAt: new Date().getTime()
|
||||
})),
|
||||
on(layoutActions.plotlyReady, (state) => ({...state, plotlyReady: true})),
|
||||
on(layoutActions.aceReady, (state) => ({...state, aceReady: true})),
|
||||
on(layoutActions.saveAceCaretPosition, (state, action) => ({
|
||||
...state,
|
||||
aceCaretPosition: {...state.aceCaretPosition, [action.id]: action.position}
|
||||
})),
|
||||
on(layoutActions.resetAceCaretsPositions, (state, action) => ({...state, aceCaretPosition: {}})),
|
||||
on(layoutActions.resetLoader, (state) => ({...state, loading: {}})),
|
||||
on(apiRequest, (state, action) => ({
|
||||
...state,
|
||||
|
||||
@@ -24,7 +24,7 @@ export class RefreshService {
|
||||
),
|
||||
filter(([, auto, visible]) => auto && visible)
|
||||
)
|
||||
.subscribe(() => this._tick.next(null))
|
||||
.subscribe(() => this._tick.next(null));
|
||||
}
|
||||
|
||||
trigger(auto = false) {
|
||||
|
||||
@@ -1,86 +1,85 @@
|
||||
import {Action, createAction, props} from '@ngrx/store';
|
||||
import {ISmAction} from '../core/models/actions';
|
||||
import {SEARCH_ACTIONS} from './dashboard-search.consts';
|
||||
import {createAction, props} from '@ngrx/store';
|
||||
import {SEARCH_PREFIX} from './dashboard-search.consts';
|
||||
import {Project} from '~/business-logic/model/projects/project';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {Model} from '~/business-logic/model/models/model';
|
||||
import {ActiveSearchLink} from '~/features/dashboard-search/dashboard-search.consts';
|
||||
import {DASHBOARD_PREFIX} from '@common/dashboard/common-dashboard.const';
|
||||
|
||||
|
||||
export const searchSetTerm = createAction(
|
||||
SEARCH_ACTIONS.SET_TERM,
|
||||
props<{query: string; regExp?: boolean; force?: boolean}>()
|
||||
SEARCH_PREFIX + 'SET_TERM',
|
||||
props<{ query: string; regExp?: boolean; force?: boolean }>()
|
||||
);
|
||||
|
||||
export const searchStart = createAction(
|
||||
SEARCH_ACTIONS.SEARCH_START,
|
||||
props<{query: string; regExp?: boolean; force?: boolean}>()
|
||||
SEARCH_PREFIX + 'SEARCH_START',
|
||||
props<{ query: string; regExp?: boolean; force?: boolean; activeLink: ActiveSearchLink }>()
|
||||
);
|
||||
|
||||
export class SearchError implements ISmAction {
|
||||
readonly type = SEARCH_ACTIONS.SEARCH_ERROR;
|
||||
}
|
||||
|
||||
export class SearchClear implements Action {
|
||||
readonly type = SEARCH_ACTIONS.SEARCH_CLEAR;
|
||||
}
|
||||
|
||||
export class SearchActivate implements Action {
|
||||
readonly type = SEARCH_ACTIONS.ACTIVATE;
|
||||
}
|
||||
|
||||
export class SearchDeactivate implements Action {
|
||||
readonly type = SEARCH_ACTIONS.DEACTIVATE;
|
||||
}
|
||||
export const searchClear = createAction(SEARCH_PREFIX + 'SEARCH_CLEAR');
|
||||
export const searchActivate = createAction(SEARCH_PREFIX + 'ACTIVATE');
|
||||
export const searchDeactivate = createAction(SEARCH_PREFIX + 'DEACTIVATE');
|
||||
|
||||
export const searchProjects = createAction(
|
||||
SEARCH_ACTIONS.SEARCH_PROJECTS,
|
||||
props<{query: string; regExp?: boolean}>()
|
||||
SEARCH_PREFIX + 'SEARCH_PROJECTS',
|
||||
props<{ query: string; regExp?: boolean }>()
|
||||
);
|
||||
|
||||
export const searchPipelines = createAction(
|
||||
SEARCH_ACTIONS.SEARCH_PIPELINES,
|
||||
props<{query: string; regExp?: boolean}>()
|
||||
SEARCH_PREFIX + 'SEARCH_PIPELINES',
|
||||
props<{ query: string; regExp?: boolean }>()
|
||||
);
|
||||
|
||||
export const searchOpenDatasets = createAction(
|
||||
SEARCH_PREFIX + 'SEARCH_OPEN_"DATASETS',
|
||||
props<{ query: string; regExp?: boolean }>()
|
||||
);
|
||||
|
||||
export const setPipelinesResults = createAction(
|
||||
'Set Pipelines Results',
|
||||
props<{pipelines: Project[]}>()
|
||||
props<{ pipelines: Project[]; scrollId: string }>()
|
||||
);
|
||||
|
||||
export class SetProjectsResults implements ISmAction {
|
||||
public type = SEARCH_ACTIONS.SET_PROJECTS;
|
||||
public payload: { projects: Array<Project> };
|
||||
|
||||
constructor(projects: Array<Project>) {
|
||||
this.payload = {projects};
|
||||
}
|
||||
}
|
||||
|
||||
export const searchExperiments= createAction(
|
||||
SEARCH_ACTIONS.SEARCH_EXPERIMENTS,
|
||||
props<{query: string; regExp?: boolean}>()
|
||||
export const setOpenDatasetsResults = createAction(
|
||||
'Set open datasets Results',
|
||||
props<{ openDatasets: Project[]; scrollId: string }>()
|
||||
);
|
||||
|
||||
export class SetExperimentsResults implements ISmAction {
|
||||
public type = SEARCH_ACTIONS.SET_EXPERIMENTS;
|
||||
public payload: { experiments: Array<Task> };
|
||||
export const setProjectsResults = createAction(SEARCH_PREFIX + 'SET_PROJECTS',
|
||||
props<{ projects: Project[]; scrollId: string }>()
|
||||
);
|
||||
|
||||
constructor(experiments: Array<Task>) {
|
||||
this.payload = {experiments};
|
||||
}
|
||||
}
|
||||
export const searchExperiments = createAction(
|
||||
SEARCH_PREFIX + 'SEARCH_EXPERIMENTS',
|
||||
props<{ query: string; regExp?: boolean }>()
|
||||
);
|
||||
|
||||
export const setExperimentsResults = createAction(
|
||||
SEARCH_PREFIX + 'SET_EXPERIMENTS',
|
||||
props<{ experiments: Task[]; scrollId: string }>()
|
||||
);
|
||||
|
||||
export const searchModels = createAction(
|
||||
SEARCH_ACTIONS.SEARCH_MODELS,
|
||||
props<{query: string; regExp?: boolean}>()
|
||||
SEARCH_PREFIX + 'SEARCH_MODELS',
|
||||
props<{ query: string; regExp?: boolean }>()
|
||||
);
|
||||
|
||||
export class SetModelsResults implements ISmAction {
|
||||
public type = SEARCH_ACTIONS.SET_MODELS;
|
||||
public payload: { models: Array<Model> };
|
||||
export const setModelsResults = createAction(
|
||||
SEARCH_PREFIX + 'SET_MODELS',
|
||||
props<{ models: Model[]; scrollId: string }>()
|
||||
);
|
||||
|
||||
constructor(models: Array<Model>) {
|
||||
this.payload = {models};
|
||||
}
|
||||
}
|
||||
export const setResultsCount = createAction(
|
||||
SEARCH_PREFIX + 'SET_COUNTS',
|
||||
props<{ counts: Map<ActiveSearchLink, number> }>()
|
||||
);
|
||||
export const getCurrentPageResults = createAction(
|
||||
DASHBOARD_PREFIX + '[get current page results]',
|
||||
props<{ activeLink: ActiveSearchLink }>()
|
||||
);
|
||||
export const getResultsCount = createAction(
|
||||
DASHBOARD_PREFIX + '[get results count]',
|
||||
props<{ query: string; regExp?: boolean; force?: boolean }>());
|
||||
|
||||
export const clearSearchResults = createAction(DASHBOARD_PREFIX + '[clear search results]');
|
||||
|
||||
@@ -1,22 +1,5 @@
|
||||
const SEARCH_PREFIX = 'SEARCH_';
|
||||
export const SEARCH_PAGE_SIZE = 1000;
|
||||
|
||||
export const SEARCH_ACTIONS = {
|
||||
SET_TERM : SEARCH_PREFIX + 'SET_TERM',
|
||||
SEARCH_START : SEARCH_PREFIX + 'SEARCH_START',
|
||||
SEARCH_PROJECTS : SEARCH_PREFIX + 'SEARCH_PROJECTS',
|
||||
SEARCH_PIPELINES : SEARCH_PREFIX + 'SEARCH_PIPELINES',
|
||||
SEARCH_EXPERIMENTS: SEARCH_PREFIX + 'SEARCH_EXPERIMENTS',
|
||||
SEARCH_MODELS : SEARCH_PREFIX + 'SEARCH_MODELS',
|
||||
SEARCH_COMPLETE : SEARCH_PREFIX + 'SEARCH_COMPLETE',
|
||||
SEARCH_ERROR : SEARCH_PREFIX + 'SEARCH_ERROR',
|
||||
SEARCH_CLEAR : SEARCH_PREFIX + 'SEARCH_CLEAR',
|
||||
SET_PROJECTS : SEARCH_PREFIX + 'SET_PROJECTS',
|
||||
SET_MODELS : SEARCH_PREFIX + 'SET_MODELS',
|
||||
SET_EXPERIMENTS : SEARCH_PREFIX + 'SET_EXPERIMENTS',
|
||||
ACTIVATE : SEARCH_PREFIX + 'ACTIVATE',
|
||||
DEACTIVATE : SEARCH_PREFIX + 'DEACTIVATE'
|
||||
};
|
||||
export const SEARCH_PREFIX = 'SEARCH_';
|
||||
export const SEARCH_PAGE_SIZE = 12;
|
||||
|
||||
export const EXPERIMENT_SEARCH_ONLY_FIELDS = ['name', 'created', 'status', 'type', 'user.name', 'id', 'company'];
|
||||
|
||||
|
||||
@@ -2,28 +2,73 @@ import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {activeLoader, deactivateLoader} from '../core/actions/layout.actions';
|
||||
import {
|
||||
SearchActivate,
|
||||
SearchClear,
|
||||
getCurrentPageResults,
|
||||
getResultsCount,
|
||||
searchActivate,
|
||||
searchClear,
|
||||
searchExperiments,
|
||||
searchModels,
|
||||
searchOpenDatasets,
|
||||
searchPipelines,
|
||||
searchProjects,
|
||||
searchSetTerm,
|
||||
searchStart,
|
||||
SetExperimentsResults,
|
||||
SetModelsResults, setPipelinesResults,
|
||||
SetProjectsResults
|
||||
setExperimentsResults,
|
||||
setModelsResults, setOpenDatasetsResults,
|
||||
setPipelinesResults,
|
||||
setProjectsResults
|
||||
} from './dashboard-search.actions';
|
||||
import {EXPERIMENT_SEARCH_ONLY_FIELDS, SEARCH_ACTIONS, SEARCH_PAGE_SIZE} from './dashboard-search.consts';
|
||||
import {EXPERIMENT_SEARCH_ONLY_FIELDS, SEARCH_PAGE_SIZE} from './dashboard-search.consts';
|
||||
import {ApiProjectsService} from '~/business-logic/api-services/projects.service';
|
||||
import {requestFailed} from '../core/actions/http.actions';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectActiveSearch} from './dashboard-search.reducer';
|
||||
import {selectActiveSearch, selectSearchScrollIds, selectSearchTerm} from './dashboard-search.reducer';
|
||||
import {ProjectsGetAllExRequest} from '~/business-logic/model/projects/projectsGetAllExRequest';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
import {ApiModelsService} from '~/business-logic/api-services/models.service';
|
||||
import {catchError, mergeMap, map, switchMap, withLatestFrom} from 'rxjs/operators';
|
||||
import {escapeRegex} from '../shared/utils/shared-utils';
|
||||
import {isEqual} from 'lodash/fp';
|
||||
import {activeSearchLink} from '~/features/dashboard-search/dashboard-search.consts';
|
||||
import {EmptyAction} from '~/app.constants';
|
||||
|
||||
export const getEntityStatQuery = action => ({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
projects: {
|
||||
_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['basename', 'id']
|
||||
},
|
||||
system_tags: ['-pipeline', '-dataset'],
|
||||
},
|
||||
tasks: {_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['name', 'id']
|
||||
},
|
||||
type: ['__$not', 'annotation_manual', '__$not', 'annotation', '__$not', 'dataset_import'],
|
||||
system_tags: ['-archived', '-pipeline', '-dataset']
|
||||
},
|
||||
models: {_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['name', 'id']
|
||||
}},
|
||||
datasets: {_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['basename', 'id']
|
||||
},
|
||||
search_hidden: true,
|
||||
system_tags: ['dataset'],
|
||||
name: '/\\.datasets/'
|
||||
},
|
||||
pipelines: {_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['basename', 'id']
|
||||
},
|
||||
search_hidden: true,
|
||||
system_tags: ['pipeline']
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
});
|
||||
|
||||
@Injectable()
|
||||
export class DashboardSearchEffects {
|
||||
@@ -36,98 +81,158 @@ export class DashboardSearchEffects {
|
||||
) {
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
activeLoader = createEffect(() => this.actions.pipe(
|
||||
ofType(SEARCH_ACTIONS.SEARCH_PROJECTS, SEARCH_ACTIONS.SEARCH_MODELS, SEARCH_ACTIONS.SEARCH_EXPERIMENTS, SEARCH_ACTIONS.SEARCH_PIPELINES),
|
||||
ofType(searchProjects, searchModels, searchExperiments, searchPipelines),
|
||||
map(action => activeLoader(action.type))
|
||||
));
|
||||
// add actions for each search
|
||||
|
||||
startSearch = createEffect(() => this.actions.pipe(
|
||||
ofType(searchStart.type),
|
||||
withLatestFrom(this.store.select(selectActiveSearch)),
|
||||
mergeMap(([action, active]: [ReturnType<typeof searchStart>, boolean]) => {
|
||||
ofType(searchStart),
|
||||
withLatestFrom(
|
||||
this.store.select(selectActiveSearch),
|
||||
this.store.select(selectSearchTerm)),
|
||||
mergeMap(([action, active, term]) => {
|
||||
const actionsToFire = [];
|
||||
if (!active) {
|
||||
actionsToFire.push(new SearchClear());
|
||||
actionsToFire.push(new SearchActivate());
|
||||
actionsToFire.push(searchClear());
|
||||
actionsToFire.push(searchActivate());
|
||||
}
|
||||
if (!isEqual(term, action)) {
|
||||
actionsToFire.push(getResultsCount(action));
|
||||
actionsToFire.push(searchSetTerm(action));
|
||||
}
|
||||
actionsToFire.push(searchSetTerm(action));
|
||||
actionsToFire.push(searchProjects(action));
|
||||
actionsToFire.push(searchPipelines(action));
|
||||
actionsToFire.push(searchExperiments(action));
|
||||
actionsToFire.push(searchModels(action));
|
||||
return actionsToFire;
|
||||
})
|
||||
));
|
||||
|
||||
getCurrentPageResults = createEffect(() => this.actions.pipe(
|
||||
ofType(getCurrentPageResults),
|
||||
withLatestFrom(
|
||||
this.store.select(selectSearchTerm)),
|
||||
map(([action, term]) => {
|
||||
switch (action.activeLink) {
|
||||
case activeSearchLink.experiments:
|
||||
return searchExperiments(term);
|
||||
case activeSearchLink.models:
|
||||
return searchModels(term);
|
||||
case activeSearchLink.projects:
|
||||
return searchProjects(term);
|
||||
case activeSearchLink.pipelines:
|
||||
return searchPipelines(term);
|
||||
case activeSearchLink.openDatasets:
|
||||
return searchOpenDatasets(term);
|
||||
}
|
||||
return new EmptyAction();
|
||||
}
|
||||
)
|
||||
));
|
||||
|
||||
searchProjects = createEffect(() => this.actions.pipe(
|
||||
ofType(searchProjects.type),
|
||||
switchMap((action: ReturnType<typeof searchProjects>) => this.projectsApi.projectsGetAllEx({
|
||||
ofType(searchProjects),
|
||||
withLatestFrom(this.store.select(selectSearchScrollIds)),
|
||||
switchMap(([action, scrollIds]) => this.projectsApi.projectsGetAllEx({
|
||||
_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query) + '[^/]*$'}),
|
||||
fields: ['name', 'id']
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['basename', 'id']
|
||||
},
|
||||
include_stats_filter: {system_tags: ['-pipeline']},
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
system_tags: ['-pipeline', '-dataset'],
|
||||
stats_for_state: ProjectsGetAllExRequest.StatsForStateEnum.Active,
|
||||
scroll_id: null,
|
||||
scroll_id: scrollIds?.[activeSearchLink.projects] || null,
|
||||
size: SEARCH_PAGE_SIZE,
|
||||
include_stats: true,
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination']
|
||||
} as ProjectsGetAllExRequest).pipe(
|
||||
mergeMap(res => [new SetProjectsResults(res.projects), deactivateLoader(action.type)]),
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'basename']
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [setProjectsResults({projects: res.projects, scrollId: res.scroll_id}), deactivateLoader(action.type)]),
|
||||
catchError(error => [deactivateLoader(action.type), requestFailed(error)])))
|
||||
));
|
||||
|
||||
searchPipelines = createEffect(() => this.actions.pipe(
|
||||
ofType(searchPipelines.type),
|
||||
switchMap((action: ReturnType<typeof searchPipelines>) => this.projectsApi.projectsGetAllEx({
|
||||
ofType(searchPipelines),
|
||||
withLatestFrom(this.store.select(selectSearchScrollIds)),
|
||||
switchMap(([action, scrollIds]) => this.projectsApi.projectsGetAllEx({
|
||||
_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query) + '[^/]*$'}),
|
||||
fields: ['name', 'id']
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['basename', 'id']
|
||||
},
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
search_hidden: true,
|
||||
shallow_search: false,
|
||||
system_tags: ['pipeline'],
|
||||
stats_for_state: ProjectsGetAllExRequest.StatsForStateEnum.Active,
|
||||
scroll_id: null,
|
||||
scroll_id: scrollIds?.[activeSearchLink.pipelines] || null,
|
||||
size: SEARCH_PAGE_SIZE,
|
||||
include_stats: true,
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'tags', 'system_tags']
|
||||
} as ProjectsGetAllExRequest).pipe(
|
||||
mergeMap(res => [setPipelinesResults({pipelines:res.projects}), deactivateLoader(action.type)]),
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'tags', 'system_tags', 'basename']
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [setPipelinesResults({pipelines: res.projects, scrollId: res.scroll_id}), deactivateLoader(action.type)]),
|
||||
catchError(error => [deactivateLoader(action.type), requestFailed(error)])))
|
||||
));
|
||||
|
||||
searchOpenDatasets = createEffect(() => this.actions.pipe(
|
||||
ofType(searchOpenDatasets),
|
||||
withLatestFrom(this.store.select(selectSearchScrollIds)),
|
||||
switchMap(([action, scrollIds]) => this.projectsApi.projectsGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['basename', 'id']
|
||||
},
|
||||
search_hidden: true,
|
||||
shallow_search: false,
|
||||
system_tags: ['dataset'],
|
||||
name: '/\\.datasets/',
|
||||
stats_for_state: ProjectsGetAllExRequest.StatsForStateEnum.Active,
|
||||
scroll_id: scrollIds?.[activeSearchLink.openDatasets] || null,
|
||||
size: SEARCH_PAGE_SIZE,
|
||||
include_stats: true,
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'tags', 'system_tags', 'basename']
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [setOpenDatasetsResults({openDatasets: res.projects, scrollId: res.scroll_id}), deactivateLoader(action.type)]),
|
||||
catchError(error => [deactivateLoader(action.type), requestFailed(error)])))
|
||||
));
|
||||
|
||||
|
||||
searchModels = createEffect(() => this.actions.pipe(
|
||||
ofType(searchModels.type),
|
||||
switchMap((action: ReturnType<typeof searchModels>) => this.modelsApi.modelsGetAllEx({
|
||||
ofType(searchModels),
|
||||
withLatestFrom(this.store.select(selectSearchScrollIds)),
|
||||
switchMap(([action, scrollIds]) => this.modelsApi.modelsGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['name', 'id']
|
||||
},
|
||||
scroll_id: null,
|
||||
scroll_id: scrollIds?.[activeSearchLink.models] || null,
|
||||
size: SEARCH_PAGE_SIZE,
|
||||
system_tags: ['-archived'],
|
||||
only_fields: ['labels', 'ready', 'created', 'framework', 'user.name', 'name', 'parent.name', 'task.name', 'id', 'company']
|
||||
include_stats: true,
|
||||
only_fields: ['ready', 'created', 'framework', 'user.name', 'name', 'parent.name', 'task.name', 'id', 'company']
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [new SetModelsResults(res.models), deactivateLoader(action.type)]),
|
||||
mergeMap(res => [setModelsResults({models: res.models, scrollId: res.scroll_id}), deactivateLoader(action.type)]),
|
||||
catchError(error => [deactivateLoader(action.type), requestFailed(error)])))
|
||||
));
|
||||
|
||||
searchExperiments = createEffect(() => this.actions.pipe(
|
||||
ofType(searchExperiments.type),
|
||||
switchMap((action: ReturnType<typeof searchExperiments>) => this.experimentsApi.tasksGetAllEx({
|
||||
ofType(searchExperiments),
|
||||
withLatestFrom(this.store.select(selectSearchScrollIds)),
|
||||
switchMap(([action, scrollIds]) => this.experimentsApi.tasksGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
fields: ['name', 'id']
|
||||
},
|
||||
scroll_id: null,
|
||||
scroll_id: scrollIds?.[activeSearchLink.experiments] || null,
|
||||
size: SEARCH_PAGE_SIZE,
|
||||
only_fields: EXPERIMENT_SEARCH_ONLY_FIELDS,
|
||||
type: ['__$not', 'annotation_manual', '__$not', 'annotation', '__$not', 'dataset_import'],
|
||||
system_tags: ['-archived', '-pipeline']
|
||||
system_tags: ['-archived', '-pipeline', '-dataset']
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [new SetExperimentsResults(res.tasks), deactivateLoader(action.type)]),
|
||||
mergeMap(res => [setExperimentsResults({experiments: res.tasks, scrollId: res.scroll_id}), deactivateLoader(action.type)]),
|
||||
catchError(error => [deactivateLoader(action.type), requestFailed(error)])))
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,74 +1,96 @@
|
||||
import {Project} from '../../business-logic/model/projects/project';
|
||||
import {User} from '../../business-logic/model/users/user';
|
||||
import {Task} from '../../business-logic/model/tasks/task';
|
||||
import {createFeatureSelector, createSelector} from '@ngrx/store';
|
||||
import {SEARCH_ACTIONS} from './dashboard-search.consts';
|
||||
import {Model} from '../../business-logic/model/models/model';
|
||||
import {searchSetTerm, setPipelinesResults} from './dashboard-search.actions';
|
||||
import {ICommonSearchState} from '../common-search/common-search.reducer';
|
||||
import {createFeatureSelector, createSelector, ReducerTypes, on, createReducer} from '@ngrx/store';
|
||||
import {Project} from '~/business-logic/model/projects/project';
|
||||
import {User} from '~/business-logic/model/users/user';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {Model} from '~/business-logic/model/models/model';
|
||||
import {
|
||||
clearSearchResults,
|
||||
searchActivate,
|
||||
searchClear,
|
||||
searchDeactivate,
|
||||
searchSetTerm, setExperimentsResults, setModelsResults, setOpenDatasetsResults,
|
||||
setPipelinesResults,
|
||||
setProjectsResults, setResultsCount
|
||||
} from './dashboard-search.actions';
|
||||
import {SearchState} from '../common-search/common-search.reducer';
|
||||
import {ActiveSearchLink, activeSearchLink} from '~/features/dashboard-search/dashboard-search.consts';
|
||||
|
||||
export interface ISearchState {
|
||||
export interface DashboardSearchState {
|
||||
projects: Project[];
|
||||
experiments: Task[];
|
||||
models: Model[];
|
||||
pipelines: Project[];
|
||||
openDatasets: Project[];
|
||||
users: User[];
|
||||
resultsCounter: number;
|
||||
term: ICommonSearchState['searchQuery'];
|
||||
term: SearchState['searchQuery'];
|
||||
forceSearch: boolean;
|
||||
active: boolean;
|
||||
resultsCount: Map<ActiveSearchLink, number>;
|
||||
scrollIds: Map<ActiveSearchLink, string>;
|
||||
}
|
||||
|
||||
|
||||
export const searchInitialState: ISearchState = {
|
||||
export const searchInitialState: DashboardSearchState = {
|
||||
term: null,
|
||||
forceSearch: false,
|
||||
projects: [],
|
||||
pipelines: [],
|
||||
openDatasets: [],
|
||||
users: [],
|
||||
experiments: [],
|
||||
models: [],
|
||||
resultsCounter: 0,
|
||||
resultsCount: null,
|
||||
scrollIds: null,
|
||||
active: false
|
||||
};
|
||||
|
||||
export function dashboardSearchReducer<ActionReducer>(state: ISearchState = searchInitialState, action) {
|
||||
switch (action.type) {
|
||||
case SEARCH_ACTIONS.ACTIVATE:
|
||||
return {...state, active: true};
|
||||
case SEARCH_ACTIONS.DEACTIVATE:
|
||||
return {...state, active: false, term: searchInitialState.term, forceSearch: false};
|
||||
case searchSetTerm.type: {
|
||||
const act = action as ReturnType<typeof searchSetTerm>;
|
||||
return {...state, term: act, forceSearch: act.force};
|
||||
}
|
||||
case SEARCH_ACTIONS.SET_PROJECTS:
|
||||
return {...state, projects: action.payload.projects, resultsCounter: state.resultsCounter + 1};
|
||||
case setPipelinesResults.type:
|
||||
return {...state, pipelines: action.pipelines, resultsCounter: state.resultsCounter + 1};
|
||||
case SEARCH_ACTIONS.SET_EXPERIMENTS:
|
||||
return {...state, experiments: action.payload.experiments, resultsCounter: state.resultsCounter + 1};
|
||||
case SEARCH_ACTIONS.SET_MODELS:
|
||||
return {...state, models: action.payload.models, resultsCounter: state.resultsCounter + 1};
|
||||
case SEARCH_ACTIONS.SEARCH_START:
|
||||
return {...state, resultsCounter: 0};
|
||||
case SEARCH_ACTIONS.SEARCH_CLEAR:
|
||||
return {
|
||||
...state,
|
||||
...searchInitialState
|
||||
};
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
export const dashboardSearchReducers = [
|
||||
on(searchActivate, (state) => ({...state, active: true})),
|
||||
on(searchDeactivate, (state) => ({...state, active: false, term: searchInitialState.term, forceSearch: false, scrollIds: null, resultsCount: null})),
|
||||
on(searchSetTerm, (state, action) => ({...state, term: action, forceSearch: action.force, scrollIds: null})),
|
||||
on(setProjectsResults, (state, action) => ({
|
||||
...state,
|
||||
projects: action.scrollId === state.scrollIds?.[activeSearchLink.projects] ? state.projects.concat(action.projects) : action.projects,
|
||||
scrollIds: {...state.scrollIds, [activeSearchLink.projects]: action.scrollId}})),
|
||||
on(setPipelinesResults, (state, action) => ({
|
||||
...state,
|
||||
pipelines: action.scrollId === state.scrollIds?.[activeSearchLink.pipelines] ? state.pipelines.concat(action.pipelines) : action.pipelines,
|
||||
scrollIds: {...state.scrollIds, [activeSearchLink.pipelines]: action.scrollId}})),
|
||||
on(setOpenDatasetsResults, (state, action) => ({
|
||||
...state,
|
||||
openDatasets: action.scrollId === state.scrollIds?.[activeSearchLink.openDatasets] ? state.openDatasets.concat(action.openDatasets) : action.openDatasets,
|
||||
scrollIds: {...state.scrollIds, [activeSearchLink.openDatasets]: action.scrollId}})),
|
||||
on(setExperimentsResults, (state, action) => ({
|
||||
...state,
|
||||
experiments: action.scrollId === state.scrollIds?.[activeSearchLink.experiments] ? state.experiments.concat(action.experiments) : action.experiments,
|
||||
scrollIds: {...state.scrollIds, [activeSearchLink.experiments]: action.scrollId}})),
|
||||
on(setModelsResults, (state, action) => ({
|
||||
...state,
|
||||
models: action.scrollId === state.scrollIds?.[activeSearchLink.models] ? state.models.concat(action.models) : action.models,
|
||||
scrollIds: {...state.scrollIds, [activeSearchLink.models]: action.scrollId}})),
|
||||
on(setResultsCount, (state, action) => ({...state, resultsCount: action.counts})),
|
||||
on(clearSearchResults, (state) => ({
|
||||
...state,
|
||||
[activeSearchLink.models]: [],
|
||||
[activeSearchLink.experiments]: [],
|
||||
[activeSearchLink.pipelines]: [],
|
||||
[activeSearchLink.projects]: [],
|
||||
})),
|
||||
on(searchClear, (state) => ({...state, ...searchInitialState})),
|
||||
] as ReducerTypes<DashboardSearchState, any>[];
|
||||
|
||||
export const dashboardSearchReducer = createReducer(
|
||||
searchInitialState,
|
||||
...dashboardSearchReducers
|
||||
);
|
||||
|
||||
export const selectSearch = createFeatureSelector<ISearchState>('search');
|
||||
export const selectProjectsResults = createSelector(selectSearch, (state: ISearchState): Array<Project> => state.projects);
|
||||
export const selectExperimentsResults = createSelector(selectSearch, (state: ISearchState): Array<Task> => state.experiments);
|
||||
export const selectModelsResults = createSelector(selectSearch, (state: ISearchState): Array<Model> => state.models);
|
||||
export const selectPipelinesResults = createSelector(selectSearch, (state: ISearchState): Array<Project> => state.pipelines);
|
||||
export const selectActiveSearch = createSelector(selectSearch, (state: ISearchState): boolean => state.term?.query?.length >= 3 || state.forceSearch);
|
||||
export const selectSearchTerm = createSelector(selectSearch, (state: ISearchState) => state.term);
|
||||
export const selectResultsCounter = createSelector(selectSearch, (state: ISearchState): number => state.resultsCounter);
|
||||
export const selectSearch = createFeatureSelector<DashboardSearchState>('search');
|
||||
export const selectProjectsResults = createSelector(selectSearch, (state: DashboardSearchState): Array<Project> => state.projects);
|
||||
export const selectExperimentsResults = createSelector(selectSearch, (state: DashboardSearchState): Array<Task> => state.experiments);
|
||||
export const selectModelsResults = createSelector(selectSearch, (state: DashboardSearchState): Array<Model> => state.models);
|
||||
export const selectPipelinesResults = createSelector(selectSearch, (state: DashboardSearchState): Array<Project> => state.pipelines);
|
||||
export const selectDatasetsResults = createSelector(selectSearch, (state: DashboardSearchState): Array<Project> => state.openDatasets);
|
||||
export const selectActiveSearch = createSelector(selectSearch, (state: DashboardSearchState): boolean => state.term?.query?.length >= 3 || state.forceSearch);
|
||||
export const selectSearchTerm = createSelector(selectSearch, (state: DashboardSearchState): SearchState['searchQuery'] => state.term);
|
||||
export const selectResultsCount = createSelector(selectSearch, (state: DashboardSearchState): Map<ActiveSearchLink, number> => state.resultsCount);
|
||||
export const selectSearchScrollIds = createSelector(selectSearch, (state: DashboardSearchState): Map<ActiveSearchLink, string> => state.scrollIds);
|
||||
|
||||
@@ -4,4 +4,7 @@
|
||||
<ng-container *ngTemplateOutlet="cardTemplate; context: {result}"></ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="load-more" *ngIf="showLoadMoreButton && (resultRows$ | async).length">
|
||||
<button (click)="loadMoreClicked.emit()" class="btn btn-cml-primary load-more-btn">LOAD MORE</button>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
|
||||
@@ -13,3 +13,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.load-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
|
||||
.load-more-btn {
|
||||
padding: 8px 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,22 +22,33 @@ export class SearchResultsComponent {
|
||||
public resultRows$: Observable<any[][]>;
|
||||
public trackById = trackById;
|
||||
public rowWidth = 300;
|
||||
private _cardTemplate: TemplateRef<any>;
|
||||
|
||||
@Input() set cardTemplate(cardTemplate: TemplateRef<any>) {
|
||||
this.viewPort?.scrollToIndex(0);
|
||||
this._cardTemplate = cardTemplate;
|
||||
}
|
||||
|
||||
get cardTemplate() {
|
||||
return this._cardTemplate;
|
||||
}
|
||||
|
||||
@Input() cardTemplate: TemplateRef<any>;
|
||||
@Input() set results(results: any[]) {
|
||||
this.results$.next(results);
|
||||
this.viewPort?.scrollToIndex(0);
|
||||
}
|
||||
|
||||
@Input() cardHeight = 246;
|
||||
@Input() showLoadMoreButton = false;
|
||||
@Output() resultClicked = new EventEmitter<any>();
|
||||
@ViewChild(CdkVirtualScrollViewport) viewPort : CdkVirtualScrollViewport;
|
||||
@Output() loadMoreClicked = new EventEmitter();
|
||||
@ViewChild(CdkVirtualScrollViewport) viewPort: CdkVirtualScrollViewport;
|
||||
|
||||
constructor(private store: Store, private breakpointObserver: BreakpointObserver) {
|
||||
this.store.select(selectScaleFactor)
|
||||
.pipe(take(1), map(factor => 100 / factor))
|
||||
.subscribe(factor => {
|
||||
const points = {} as {[point: string]: number};
|
||||
[2,3,4,5,6].forEach(num =>
|
||||
const points = {} as { [point: string]: number };
|
||||
[2, 3, 4, 5, 6].forEach(num =>
|
||||
points[`(min-width: ${num === 2 ? 0 : ((num - 2) * 24 + (num - 1) * CARD_WIDTH + SIDE_NAV_PLUS_PAD) * factor}px) and ` +
|
||||
`(max-width: ${num === 6 ? 20000 : ((num - 1) * 24 + num * CARD_WIDTH + SIDE_NAV_PLUS_PAD) * factor}px)`] = num);
|
||||
this.cardLayoutChange$ = breakpointObserver.observe(Object.keys(points));
|
||||
@@ -46,7 +57,7 @@ export class SearchResultsComponent {
|
||||
.pipe(map(([match, results]) => {
|
||||
const point = Object.entries(match.breakpoints).find(([, val]) => val);
|
||||
const cards = point ? points[point[0]] - 1 : 3;
|
||||
this.rowWidth = cards * CARD_WIDTH + (cards - 1) * 24
|
||||
this.rowWidth = cards * CARD_WIDTH + (cards - 1) * 24;
|
||||
return chunk(cards, results);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -15,3 +15,4 @@ export const setRecentExperiments = createAction(
|
||||
DASHBOARD_PREFIX + '[set recent experiments]',
|
||||
props<{experiments: IRecentTask[]}>()
|
||||
);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ export class CommonDashboardEffects {
|
||||
page_size: CARDS_IN_ROW,
|
||||
active_users: (showOnlyUserWork ? [user.id] : null),
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination']
|
||||
} as ProjectsGetAllExRequest).pipe(
|
||||
}).pipe(
|
||||
mergeMap(({projects}) => [setRecentProjects({projects}), deactivateLoader(action.type)]),
|
||||
catchError(error => [deactivateLoader(action.type), requestFailed(error)])
|
||||
)
|
||||
|
||||
@@ -1,59 +1,99 @@
|
||||
import {InitSearch, ResetSearch} from '../common-search/common-search.actions';
|
||||
import {skip} from 'rxjs/operators';
|
||||
import {initSearch, resetSearch} from '../common-search/common-search.actions';
|
||||
import {filter, skip} from 'rxjs/operators';
|
||||
import {Model} from '~/business-logic/model/models/model';
|
||||
import {SearchDeactivate, searchStart} from '../dashboard-search/dashboard-search.actions';
|
||||
import {clearSearchResults, getCurrentPageResults, searchClear, searchDeactivate, searchStart} from '../dashboard-search/dashboard-search.actions';
|
||||
import {IRecentTask} from './common-dashboard.reducer';
|
||||
import {ITask} from '~/business-logic/model/al-task';
|
||||
import {Observable} from 'rxjs';
|
||||
import {ICommonSearchState, selectSearchQuery} from '../common-search/common-search.reducer';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {SearchState, selectSearchQuery} from '../common-search/common-search.reducer';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {
|
||||
selectActiveSearch, selectExperimentsResults, selectModelsResults, selectPipelinesResults, selectProjectsResults,
|
||||
selectResultsCounter,
|
||||
selectActiveSearch, selectDatasetsResults, selectExperimentsResults, selectModelsResults, selectPipelinesResults, selectProjectsResults, selectResultsCount, selectSearchScrollIds,
|
||||
selectSearchTerm
|
||||
} from '../dashboard-search/dashboard-search.reducer';
|
||||
import {Project} from '~/business-logic/model/projects/project';
|
||||
import {setSelectedProjectId} from '../core/actions/projects.actions';
|
||||
import {isExample} from '../shared/utils/shared-utils';
|
||||
import {ActiveSearchLink} from '~/features/dashboard/containers/dashboard-search/dashboard-search.component';
|
||||
import {activeLinksList, ActiveSearchLink, activeSearchLink} from '~/features/dashboard-search/dashboard-search.consts';
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
export abstract class DashboardSearchComponentBase {
|
||||
abstract store;
|
||||
abstract router;
|
||||
@Component({
|
||||
selector: 'sm-dashboard-search-base',
|
||||
template: `<sm-search-results-page
|
||||
*ngIf="activeSearch$ | async"
|
||||
(projectSelected)="projectCardClicked($event)"
|
||||
(experimentSelected)="taskSelected($event)"
|
||||
(modelSelected)="modelSelected($event)"
|
||||
(pipelineSelected)="pipelineSelected($event)"
|
||||
(activeLinkChanged)="activeLinkChanged($event)"
|
||||
(openDatasetSelected)="openDatasetCardClicked($event)"
|
||||
(loadMoreClicked)="loadMore()"
|
||||
[projectsList]="projectsResults$ | async"
|
||||
[pipelinesList]="pipelinesResults$ | async"
|
||||
[datasetsList]="datasetsResults$ | async"
|
||||
[experimentsList]="experimentsResults$ | async"
|
||||
[modelsList]="modelsResults$ | async"
|
||||
[activeLink]="activeLink"
|
||||
[resultsCount]="resultsCount$ | async">
|
||||
</sm-search-results-page>`,
|
||||
})
|
||||
export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
|
||||
public activeLink = 'projects' as ActiveSearchLink;
|
||||
private searchSubs;
|
||||
public searchQuery$: Observable<ICommonSearchState['searchQuery']>;
|
||||
private allResultsSubscription: Subscription;
|
||||
public searchQuery$: Observable<SearchState['searchQuery']>;
|
||||
public activeSearch$: Observable<boolean>;
|
||||
protected readonly resultsCounter$: Observable<number>;
|
||||
public modelsResults$: Observable<Array<Model>>;
|
||||
public projectsResults$: Observable<Array<Project>>;
|
||||
public experimentsResults$: Observable<any>;
|
||||
public searchTerm$: Observable<ICommonSearchState['searchQuery']>;
|
||||
public searchTerm$: Observable<SearchState['searchQuery']>;
|
||||
public pipelinesResults$: Observable<Project[]>;
|
||||
public datasetsResults$: Observable<Project[]>;
|
||||
private scrollIds: Map<ActiveSearchLink, string>;
|
||||
public resultsCount$: Observable<Map<ActiveSearchLink, number>>;
|
||||
|
||||
constructor(store: Store<any>){
|
||||
constructor(public store: Store<any>, public router: Router){
|
||||
this.searchQuery$ = store.select(selectSearchQuery);
|
||||
this.activeSearch$ = store.select(selectActiveSearch);
|
||||
this.resultsCounter$ = store.select(selectResultsCounter);
|
||||
this.modelsResults$ = store.select(selectModelsResults);
|
||||
this.pipelinesResults$ = store.select(selectPipelinesResults);
|
||||
this.datasetsResults$ = store.select(selectDatasetsResults);
|
||||
this.projectsResults$ = store.select(selectProjectsResults);
|
||||
this.experimentsResults$ = store.select(selectExperimentsResults);
|
||||
this.searchTerm$ = store.select(selectSearchTerm);
|
||||
this.resultsCount$ = store.select(selectResultsCount);
|
||||
this.syncAppSearch();
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.allResultsSubscription = this.resultsCount$.pipe(
|
||||
filter(resultsCount => !!resultsCount),
|
||||
).subscribe((resultsCount) => {
|
||||
return this.setFirstActiveLink(resultsCount);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.store.dispatch(searchClear());
|
||||
this.searchTermChanged('');
|
||||
this.stopSyncSearch();
|
||||
this.allResultsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
stopSyncSearch() {
|
||||
this.store.dispatch(new ResetSearch());
|
||||
this.store.dispatch(resetSearch());
|
||||
this.searchSubs.unsubscribe();
|
||||
}
|
||||
|
||||
syncAppSearch() {
|
||||
this.store.dispatch(new InitSearch('Search for all'));
|
||||
this.store.dispatch(initSearch({payload: 'Search for all'}));
|
||||
|
||||
this.searchSubs = this.searchQuery$
|
||||
.pipe(skip(1))
|
||||
.subscribe(query => this.searchTermChanged(query?.query, query?.regExp));
|
||||
|
||||
this.searchSubs.add(this.store.select(selectSearchScrollIds).subscribe(scrollIds => this.scrollIds = scrollIds));
|
||||
}
|
||||
|
||||
public modelSelected(model: Model) {
|
||||
@@ -64,10 +104,10 @@ export abstract class DashboardSearchComponentBase {
|
||||
|
||||
public searchTermChanged(term: string, regExp?: boolean) {
|
||||
if (term && term.length > 0) {
|
||||
this.store.dispatch(searchStart({query:term, regExp, force: term.length < 3}));
|
||||
this.store.dispatch(searchStart({query:term, regExp, force: term.length < 3, activeLink: this.activeLink}));
|
||||
} else {
|
||||
this.activeLink = 'projects';
|
||||
this.store.dispatch(new SearchDeactivate());
|
||||
this.activeLink = activeSearchLink.projects;
|
||||
this.store.dispatch(searchDeactivate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,11 +115,17 @@ export abstract class DashboardSearchComponentBase {
|
||||
this.router.navigateByUrl(`projects/${project.id}`);
|
||||
this.store.dispatch(setSelectedProjectId({projectId: project.id, example: isExample(project)}));
|
||||
}
|
||||
|
||||
pipelineSelected(project: Project) {
|
||||
this.router.navigateByUrl(`pipelines/${project.id}/experiments`);
|
||||
this.store.dispatch(setSelectedProjectId({projectId: project.id, example: isExample(project)}));
|
||||
}
|
||||
|
||||
public openDatasetCardClicked(project: Project) {
|
||||
this.router.navigateByUrl(`datasets/simple/${project.id}/experiments`);
|
||||
this.store.dispatch(setSelectedProjectId({projectId: project.id, example: isExample(project)}));
|
||||
}
|
||||
|
||||
public taskSelected(task: IRecentTask | ITask) {
|
||||
// TODO ADD task.id to route
|
||||
const projectId = task.project ? task.project.id : '*';
|
||||
@@ -89,14 +135,26 @@ export abstract class DashboardSearchComponentBase {
|
||||
|
||||
public activeLinkChanged(activeLink) {
|
||||
this.activeLink = activeLink;
|
||||
if (!this.scrollIds?.[activeLink]) {
|
||||
this.store.dispatch(getCurrentPageResults({activeLink}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setFirstActiveLink(allResults, tabsIndexes) {
|
||||
if (!(allResults[tabsIndexes.indexOf(this.activeLink)].length > 0)) {
|
||||
const firstTabIndex = allResults.findIndex(list => list.length > 0);
|
||||
setFirstActiveLink(resultsCount) {
|
||||
if (resultsCount[this.activeLink] > 0) {
|
||||
this.activeLinkChanged(this.activeLink);
|
||||
} else {
|
||||
const firstTabIndex = activeLinksList.findIndex(activeLink => resultsCount[activeLink.name] > 0);
|
||||
if (firstTabIndex > -1) {
|
||||
this.activeLink = tabsIndexes[firstTabIndex];
|
||||
this.activeLinkChanged(activeLinksList[firstTabIndex].name);
|
||||
} else {
|
||||
this.store.dispatch(clearSearchResults());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadMore() {
|
||||
this.store.dispatch(getCurrentPageResults({activeLink: this.activeLink}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<div [id]="step.stepId" class="step-container pointer"
|
||||
[class.selected]="selected">
|
||||
<div class="step-part step-title" [class]="step?.data?.status">
|
||||
<div
|
||||
class="title"
|
||||
smShowTooltipIfEllipsis
|
||||
[smTooltip]="step?.data?.name ?? step.name + step?.data?.version ? ' v' + step.data.version : ''"
|
||||
matTooltipPosition="above"
|
||||
>{{step?.data?.name ?? step.name}}<ng-container *ngIf="step?.data?.version"> v{{step.data.version}}</ng-container></div>
|
||||
<i class="al-icon sm-md al-ico-console" (click)="openConsole.emit()"></i>
|
||||
</div>
|
||||
<div class="step-part step-footer" [class]="step?.data?.status">
|
||||
<div *ngIf="step?.data?.job_size">{{step.data.job_size | filesize: fileSizeConfigStorage}}</div>
|
||||
<div class="d-flex-center" *ngIf="step?.data?.last_update">
|
||||
<i class="al-icon al-ico-upload sm mr-1"></i>{{step.data.last_update * 1000 | timeAgo}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
:host {
|
||||
flex: 0 0 220px;
|
||||
|
||||
.step-footer {
|
||||
font-size: 12px;
|
||||
}
|
||||
.title {
|
||||
max-width: 162px; // use for ellipsis
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DatasetVersionStepComponent } from './dataset-version-step.component';
|
||||
|
||||
describe('DatasetVersionStepComponent', () => {
|
||||
let component: DatasetVersionStepComponent;
|
||||
let fixture: ComponentFixture<DatasetVersionStepComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ DatasetVersionStepComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DatasetVersionStepComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Component} from '@angular/core';
|
||||
import {
|
||||
PipelineControllerStepComponent
|
||||
} from '@common/pipelines-controller/pipeline-controller-step/pipeline-controller-step.component';
|
||||
import { fileSizeConfigStorage } from '@common/shared/pipes/filesize.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-dataset-version-step',
|
||||
templateUrl: './dataset-version-step.component.html',
|
||||
styleUrls: ['../../pipelines-controller/pipeline-controller-step/pipeline-controller-step.component.scss', './dataset-version-step.component.scss']
|
||||
})
|
||||
export class DatasetVersionStepComponent extends PipelineControllerStepComponent{
|
||||
fileSizeConfigStorage = fileSizeConfigStorage;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<sm-card class="project-card"
|
||||
(click)="projectClicked()"
|
||||
[isExample]="!['All Experiments'].includes(project.name) && !isRootProject && (!project.company || ! project.company.id)"
|
||||
[isFolder]="false"
|
||||
[oneTabMode]="true"
|
||||
[subFolderTitle]="project?.sub_projects?.length + ' sub projects'"
|
||||
>
|
||||
<div header-content [class.rename-state]="editName.active">
|
||||
<div class="d-flex justify-content-between align-items-center card-name">
|
||||
<sm-inline-edit #editName
|
||||
class="title edit-name dark"
|
||||
[originalText]="project.name | shortProjectName"
|
||||
[editable]="true"
|
||||
pattern="^[^/]+$"
|
||||
[inlineDisabled]="true"
|
||||
(textChanged)="prepareProjectNameForChange($event)"
|
||||
(inlineActiveStateChanged)="projectNameEditActiveChanged($event)"
|
||||
|
||||
>
|
||||
<span class="project-name"
|
||||
matTooltipPosition="above"
|
||||
[smTooltip]="project.name"
|
||||
>{{project.name | shortProjectName}}</span>
|
||||
</sm-inline-edit>
|
||||
<sm-pipeline-card-menu
|
||||
class="menu-wrapper"
|
||||
*ngIf="!hideMenu"
|
||||
[project]="project"
|
||||
[allTags]="allTags"
|
||||
(run)="run.emit()"
|
||||
(rename)="editName.inlineActivated()"
|
||||
(addTag)="addTag.emit($event)"
|
||||
(delete)="delete.emit()"
|
||||
></sm-pipeline-card-menu>
|
||||
</div>
|
||||
<div *ngIf="project.last_update; else: noRun" class="last-run">Updated {{project.last_update | timeAgo}}</div>
|
||||
<ng-template #noRun><div class="last-run"></div></ng-template>
|
||||
</div>
|
||||
<div class="d-flex justify-content-around w-100">
|
||||
<sm-circle-counter
|
||||
[counter]="project.stats?.active?.total_tasks"
|
||||
label="VERSIONS"
|
||||
></sm-circle-counter>
|
||||
<sm-circle-counter
|
||||
[counter]="[
|
||||
{value: project?.dataset_stats?.file_count | NA, label: 'FILES'},
|
||||
{value: project?.dataset_stats?.total_size | filesize : fileSizeConfigStorage | NA, label: 'SIZE'}
|
||||
]"
|
||||
label="LATEST VERSION"
|
||||
></sm-circle-counter>
|
||||
</div>
|
||||
<div footer class="footer-tags">
|
||||
<sm-tag-list
|
||||
*ngIf="!hideMenu; else: ReadOnlyTags"
|
||||
class="w-100"
|
||||
[tags]="project.tags"
|
||||
(remove)="removeTag.emit($event)"
|
||||
smClickStopPropagation
|
||||
></sm-tag-list>
|
||||
<ng-template #ReadOnlyTags>
|
||||
<sm-tag-list [tags]="project.tags"></sm-tag-list>
|
||||
</ng-template>
|
||||
</div>
|
||||
</sm-card>
|
||||
@@ -0,0 +1,40 @@
|
||||
@import "../../shared/ui-components/styles/variables";
|
||||
|
||||
:host {
|
||||
cursor: pointer;
|
||||
|
||||
.card-name {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.project-name {
|
||||
max-width: 280px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
color: $blue-200;
|
||||
}
|
||||
|
||||
.last-run {
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
color: $blue-300;
|
||||
}
|
||||
.title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.footer-tags {
|
||||
display: flex;
|
||||
min-height: 38px;
|
||||
background-color: $blue-600;
|
||||
padding: 0 16px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.rename-state .last-run {
|
||||
visibility: hidden;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SimpleDatasetCardComponent } from './simple-dataset-card.component';
|
||||
|
||||
describe('SimpleDatasetCardComponent', () => {
|
||||
let component: SimpleDatasetCardComponent;
|
||||
let fixture: ComponentFixture<SimpleDatasetCardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SimpleDatasetCardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SimpleDatasetCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {PipelineCardComponent} from '@common/pipelines/pipeline-card/pipeline-card.component';
|
||||
import { fileSizeConfigStorage } from '@common/shared/pipes/filesize.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-simple-dataset-card',
|
||||
templateUrl: './simple-dataset-card.component.html',
|
||||
styleUrls: ['./simple-dataset-card.component.scss']
|
||||
})
|
||||
export class SimpleDatasetCardComponent extends PipelineCardComponent{
|
||||
fileSizeConfigStorage = {...fileSizeConfigStorage, spacer: '', round: 1};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<mat-menu #menu="matMenu" [hasBackdrop]="false" class="line-item" xPosition="before">
|
||||
<div
|
||||
class="action-tooltip"
|
||||
#menuHesitate="hesitate"
|
||||
[delay]="1000"
|
||||
[action]="'leave'"
|
||||
(smHesitate)="menu.closed.emit();"
|
||||
>
|
||||
<p class="command">{{command}}</p>
|
||||
<div class="w-100 d-flex flex-row-reverse">
|
||||
<div
|
||||
class="d-flex-center copy-button pointer"
|
||||
ngxClipboard
|
||||
[cbContent]="command"
|
||||
(cbOnSuccess)="$event.event.stopPropagation(); copied()"
|
||||
>Copy command</div><i class="al-icon al-ico-success sm mr-1" [class.visible]="copySuccess"></i>
|
||||
</div>
|
||||
</div>
|
||||
</mat-menu>
|
||||
<span
|
||||
*ngIf="command"
|
||||
class="d-flex-center download-button"
|
||||
[delay]="1000" [action]="'leave'"
|
||||
(smHesitate)="menuHesitate.hesitateStatus && menu.closed.emit()"
|
||||
>
|
||||
<i class="al-icon al-ico-download pointer line-item"
|
||||
#idElement
|
||||
[matMenuTriggerFor]="menu"
|
||||
(click)="openMenu(); menuHesitate.hesitateStatus = true"
|
||||
></i>
|
||||
</span>
|
||||
<sm-table
|
||||
[columns]="columns"
|
||||
[tableData]="tableData"
|
||||
[selectionMode]="null"
|
||||
[scrollable]="true"
|
||||
></sm-table>
|
||||
@@ -0,0 +1,62 @@
|
||||
@import 'variables.scss';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
.download-button {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
top: 12px;
|
||||
z-index: 21;
|
||||
color: $blue-300;
|
||||
}
|
||||
|
||||
::ng-deep sm-table {
|
||||
th:nth-child(1) {padding-left: 24px !important;}
|
||||
td:nth-child(1) {padding-left: 24px !important;}
|
||||
}
|
||||
}
|
||||
|
||||
.action-tooltip {
|
||||
max-width: 400px;
|
||||
background: $purple;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16);
|
||||
font-size: 11px;
|
||||
|
||||
div, p {
|
||||
color: $white;
|
||||
|
||||
&.command {
|
||||
background-color: $black;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
}
|
||||
}
|
||||
|
||||
div.copy-button {
|
||||
color: $light-periwinkle-two;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
&:hover {
|
||||
color: $white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.al-ico-success {
|
||||
background-color: $pipeline-queued;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
transition: opacity 200ms;
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SimpleDatasetVersionContentComponent } from './simple-dataset-version-content.component';
|
||||
|
||||
describe('SimpleDatasetVersionContentComponent', () => {
|
||||
let component: SimpleDatasetVersionContentComponent;
|
||||
let fixture: ComponentFixture<SimpleDatasetVersionContentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SimpleDatasetVersionContentComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SimpleDatasetVersionContentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import {ChangeDetectionStrategy, Component, Input, ViewChild} from '@angular/core';
|
||||
import {MatMenuTrigger} from '@angular/material/menu';
|
||||
import {ISmCol} from '@common/shared/ui-components/data/table/table.consts';
|
||||
import {fileSizeConfigStorage, FileSizePipe} from '@common/shared/pipes/filesize.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-simple-dataset-version-content',
|
||||
templateUrl: './simple-dataset-version-content.component.html',
|
||||
styleUrls: ['./simple-dataset-version-content.component.scss']
|
||||
})
|
||||
export class SimpleDatasetVersionContentComponent {
|
||||
public columns: ISmCol[];
|
||||
public tableData: string[][];
|
||||
public command: string;
|
||||
private ngFile = new FileSizePipe();
|
||||
|
||||
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
|
||||
copySuccess: boolean;
|
||||
|
||||
@Input() set id(id: string) {
|
||||
this.command = `clearml-data get --id ${id}`;
|
||||
this.copySuccess = false;
|
||||
}
|
||||
|
||||
@Input() set data(csv: string) {
|
||||
const lines = csv?.split('\n') ?? [];
|
||||
const header = lines.splice(0, 1)[0] ?? '';
|
||||
this.columns = header.split(/, ?/).map((caption, index) => ({
|
||||
id: `${index}`,
|
||||
header: caption,
|
||||
style: {width: index === 1 ? '5px' : '300px'}
|
||||
}));
|
||||
const tableData = lines.map(line => line.split(/, ?/));
|
||||
if (Number(tableData[0]?.[1]) && this.columns[1]?.header?.includes('ize')) {
|
||||
tableData.forEach( line => line[1] = this.ngFile.transform(parseInt(line[1], 10) || 0, fileSizeConfigStorage) as string);
|
||||
}
|
||||
this.tableData = tableData;
|
||||
|
||||
}
|
||||
|
||||
openMenu() {
|
||||
this.trigger.openMenu();
|
||||
}
|
||||
|
||||
copied() {
|
||||
this.copySuccess = true;
|
||||
window.setTimeout(() => this.copySuccess = false, 3000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<mat-expansion-panel [expanded]="true">
|
||||
<mat-expansion-panel-header>
|
||||
<div class="expand-header">VERSION INFO</div>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="panel-body" *ngIf="entity">
|
||||
<div class="header">
|
||||
<div class="name ellipsis">{{entity.name}}<ng-container *ngIf="entity?.runtime?.version"> v{{entity.runtime.version}}</ng-container></div>
|
||||
<span class="status" [class]="entity.status">{{entity.status | replaceViaMapPipe:convertStatusMap | replaceViaMapPipe:convertStatusMapBase}}</span>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="param">
|
||||
<div class="key">ID</div>
|
||||
<div class="value d-flex align-item-center justify-content-between" [smTooltip]="entity.id" smShowTooltipIfEllipsis>
|
||||
<span>{{entity.id?.slice(0, 8)}}</span>
|
||||
<i
|
||||
class="pointer al-icon al-ico-copy-to-clipboard sm"
|
||||
ngxClipboard
|
||||
[cbContent]="entity?.id"
|
||||
(cbOnSuccess)="copyToClipboard()"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="param continue">
|
||||
<div class="key">Size</div>
|
||||
<div class="value" [smTooltip]="entity?.runtime?.ds_total_size + ' (original)'" smShowTooltipIfEllipsis>{{$any(entity?.runtime?.ds_total_size) | filesize : fileSizeConfigStorage || '-'}}<span class="comment">(original)</span></div>
|
||||
</div>
|
||||
<div class="param">
|
||||
<div class="key"></div>
|
||||
<div class="value" [smTooltip]="entity?.runtime?.ds_total_size_compressed + ' (compressed)'" smShowTooltipIfEllipsis>{{$any(entity?.runtime?.ds_total_size_compressed) | filesize : fileSizeConfigStorage || '-'}}<span class="comment">(compressed)</span></div>
|
||||
</div>
|
||||
<div class="param">
|
||||
<div class="key">File count</div>
|
||||
<div class="value" [smTooltip]="entity?.runtime?.ds_file_count" smShowTooltipIfEllipsis>{{entity?.runtime?.ds_file_count ?? '-'}}</div>
|
||||
</div>
|
||||
<div class="param">
|
||||
<div class="key">Link count</div>
|
||||
<div class="value" [smTooltip]="entity?.runtime?.ds_link_count" smShowTooltipIfEllipsis>{{entity?.runtime?.ds_link_count ?? '-'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="header">FILES CHANGED</div>
|
||||
<div class="param">
|
||||
<div class="key">Added</div>
|
||||
<div class="value" [smTooltip]="entity?.runtime?.ds_change_add " smShowTooltipIfEllipsis>{{entity?.runtime?.ds_change_add ?? '-'}}</div>
|
||||
</div>
|
||||
<div class="param">
|
||||
<div class="key">Modified</div>
|
||||
<div class="value" [smTooltip]="entity?.runtime?.ds_change_modify " smShowTooltipIfEllipsis>{{entity?.runtime?.ds_change_modify ?? '-'}}</div>
|
||||
</div>
|
||||
<div class="param">
|
||||
<div class="key">Removed</div>
|
||||
<div class="value" [smTooltip]="entity?.runtime?.ds_change_remove " smShowTooltipIfEllipsis>{{entity?.runtime?.ds_change_remove ?? '-'}}</div>
|
||||
</div>
|
||||
<div class="param">
|
||||
<div class="key">Size</div>
|
||||
<div class="value"
|
||||
*ngIf="entity?.runtime?.ds_change_size"
|
||||
[smTooltip]="$any(entity?.runtime?.ds_change_size) | filesize: fileSizeConfigStorage"
|
||||
smShowTooltipIfEllipsis
|
||||
>{{$any(entity?.runtime?.ds_change_size) | filesize: fileSizeConfigStorage}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer *ngIf="entity?.id">
|
||||
<a
|
||||
class="arr-link"
|
||||
target="_blank"
|
||||
[href]="'/projects/' + project + '/experiments/' + entity?.id + '/output/execution'">
|
||||
Task information<i class="al-icon al-ico-link-arrow sm"></i>
|
||||
</a>
|
||||
</footer>
|
||||
</mat-expansion-panel>
|
||||
@@ -0,0 +1,35 @@
|
||||
@import "variables";
|
||||
|
||||
:host {
|
||||
.header {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
padding-bottom: 18px;
|
||||
color: $blue-100;
|
||||
border-bottom: solid 1px $dark-border;
|
||||
}
|
||||
|
||||
.al-ico-copy-to-clipboard {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.value:hover {
|
||||
.al-ico-copy-to-clipboard {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
.param {
|
||||
&.continue {
|
||||
border-bottom: unset;
|
||||
}
|
||||
|
||||
.comment {
|
||||
color: $blue-300;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SimpleDatasetVersionDetailsComponent } from './simple-dataset-version-details.component';
|
||||
|
||||
describe('SimpleDatasetVersionDetailsComponent', () => {
|
||||
let component: SimpleDatasetVersionDetailsComponent;
|
||||
let fixture: ComponentFixture<SimpleDatasetVersionDetailsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SimpleDatasetVersionDetailsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SimpleDatasetVersionDetailsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {PipelineInfoComponent} from '@common/pipelines-controller/pipeline-details/pipeline-info.component';
|
||||
import { fileSizeConfigStorage } from '@common/shared/pipes/filesize.pipe';
|
||||
import {DATASETS_STATUS_LABEL, EXPERIMENTS_STATUS_LABELS} from '~/features/experiments/shared/experiments.const';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-simple-dataset-version-details',
|
||||
templateUrl: './simple-dataset-version-details.component.html',
|
||||
styleUrls: ['./simple-dataset-version-details.component.scss', '../../pipelines-controller/pipeline-details/pipeline-info.component.scss']
|
||||
})
|
||||
export class SimpleDatasetVersionDetailsComponent extends PipelineInfoComponent {
|
||||
public fileSizeConfigStorage = fileSizeConfigStorage;
|
||||
|
||||
public convertStatusMap = DATASETS_STATUS_LABEL;
|
||||
public convertStatusMapBase = EXPERIMENTS_STATUS_LABELS;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<sm-simple-dataset-version-details [entity]="selected$ | async" [project]="projectId$ | async" [step]="selectedEntity"></sm-simple-dataset-version-details>
|
||||
<div class="console-button">
|
||||
<button class="btn btn-cml-primary d-flex align-items-center" (click)="toggleDetails()">
|
||||
<i class="al-icon al-ico-console sm mr-3"></i>DETAILS
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
#diagramContainer
|
||||
class="pipeline-container"
|
||||
[class.extend]="showLog"
|
||||
(click)="selectStep()"
|
||||
>
|
||||
<ng-container *ngIf="dagModel$ | async as dagModel">
|
||||
<div *ngFor="let row of dagModel | reverse" class="level" [style.width.px]="chartWidth">
|
||||
<sm-dataset-version-step
|
||||
#taskEl
|
||||
*ngFor="let step of row | uniqueBy: 'stepId'; trackBy: trackByStepId" [step]="step"
|
||||
[selected]="selectedEntity && selectedEntity.id === step?.id"
|
||||
(click)="$event.stopPropagation(); !taskEl.selected && selectStep(step)"
|
||||
(openConsole)="openLog()"
|
||||
></sm-dataset-version-step>
|
||||
</div>
|
||||
<svg class="arrows"
|
||||
*ngIf="chartWidth"
|
||||
[attr.viewBox]="'0 0 ' + chartWidth + ' ' + (50 + 132 * dagModel?.length)"
|
||||
[style.width.px]="chartWidth"
|
||||
[style.height.px]="50 + 132 * dagModel?.length"
|
||||
>
|
||||
<g
|
||||
*ngFor="let arrow of arrows; trackBy: trackArrows"
|
||||
[class.selected]="arrow.selected"
|
||||
>
|
||||
<path [attr.d]="arrow.path" fill="none" stroke-width="2"></path>
|
||||
<polygon
|
||||
points="0,-6 12,0, 0,6"
|
||||
[attr.transform]="arrow.headTransform"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="results-panel" [class.extend]="showLog" [class.maximized]="maximizeResults">
|
||||
<ng-container *ngIf="showLog">
|
||||
<div class="header toggle">
|
||||
<div class="log-name">
|
||||
<i class="al-icon al-ico-console mr-2"></i>
|
||||
<span *ngIf="(selected$ | async) as selected">
|
||||
{{selected?.name}}<ng-container *ngIf="selected?.runtime?.version"> v{{selected.runtime.version}}</ng-container>
|
||||
</span>
|
||||
</div>
|
||||
<sm-button-toggle
|
||||
[value]="detailsPanelMode"
|
||||
[options]="[
|
||||
{label: 'CONTENT', value: statusOption.content},
|
||||
{label: 'PREVIEW', value: statusOption.preview},
|
||||
{label: 'CONSOLE', value: statusOption.log}
|
||||
]"
|
||||
(valueChanged)="detailsPanelMode = $event"
|
||||
></sm-button-toggle>
|
||||
<div class="close">
|
||||
<i class="al-icon pointer" [class]="maximizeResults ? 'al-ico-min-panel' : 'al-ico-max-panel'" (click)="toggleResultSize()"></i>
|
||||
<i class="al-icon al-ico-dialog-x pointer ml-4" (click)="openLog(false)"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div [ngSwitch]="detailsPanelMode" class="content">
|
||||
<sm-experiment-output-log
|
||||
*ngSwitchCase="statusOption.log"
|
||||
[experiment]="selected$ | async"
|
||||
[isDarkTheme]="true"
|
||||
[showHeader]="false"
|
||||
></sm-experiment-output-log>
|
||||
<sm-simple-dataset-version-preview *ngSwitchCase="statusOption.preview" [selected]="(selected$ | async)">
|
||||
</sm-simple-dataset-version-preview>
|
||||
<sm-simple-dataset-version-content
|
||||
*ngSwitchCase="statusOption.content"
|
||||
class="h-100"
|
||||
[id]="selectedEntity?.data?.job_id"
|
||||
[data]="(selected$ | async)?.configuration?.['Dataset Content']?.value"
|
||||
></sm-simple-dataset-version-content>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user