mirror of
https://github.com/clearml/clearml-web
synced 2025-06-26 18:27:02 +00:00
@@ -8,28 +8,16 @@
|
||||
"files": [
|
||||
"*.ts"
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": [
|
||||
"tsconfig.json"
|
||||
],
|
||||
"createDefaultProgram": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/ng-cli-compat",
|
||||
"plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@angular-eslint/recommended",
|
||||
"plugin:@angular-eslint/template/process-inline-templates",
|
||||
"plugin:@ngrx/recommended-requiring-type-checking"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": "error",
|
||||
"no-debugger": "error",
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "sm",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
@@ -38,49 +26,15 @@
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/consistent-type-definitions": "error",
|
||||
"@typescript-eslint/dot-notation": "off",
|
||||
"@typescript-eslint/member-ordering": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"off",
|
||||
{
|
||||
"accessibility": "explicit"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-inferrable-types": [
|
||||
"off",
|
||||
{
|
||||
"ignoreParameters": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/no-use-before-define": "error",
|
||||
"brace-style": [
|
||||
"error",
|
||||
"1tbs"
|
||||
],
|
||||
"eqeqeq": [
|
||||
"off",
|
||||
"smart"
|
||||
],
|
||||
"id-blacklist": "off",
|
||||
"id-match": "off",
|
||||
"max-len": [
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"code": 2000
|
||||
"type": "element",
|
||||
"prefix": "sm",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"no-bitwise": "off",
|
||||
"no-shadow": [
|
||||
"off",
|
||||
{
|
||||
"hoist": "all"
|
||||
}
|
||||
],
|
||||
"no-underscore-dangle": "off",
|
||||
"valid-typeof": "error"
|
||||
"@ngrx/prefer-effect-callback-in-block-statement": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -88,7 +42,8 @@
|
||||
"*.html"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/template/recommended"
|
||||
"plugin:@angular-eslint/template/recommended",
|
||||
"plugin:@angular-eslint/template/accessibility"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/template/use-track-by-function": "warn"
|
||||
|
||||
18
README.md
18
README.md
@@ -2,16 +2,22 @@
|
||||
|
||||
## Building the UI from source
|
||||
### Prerequisite
|
||||
* a linux machine
|
||||
* Node 16 with latest npm
|
||||
* clone the project to your local machine
|
||||
* Node 18 with latest npm
|
||||
* use git to clone the project to your local machine
|
||||
|
||||
### build
|
||||
* `cd` to the root of the project
|
||||
### Build
|
||||
* `cd clearml-web` to the root of the project
|
||||
* run `npm ci` to install required node modules
|
||||
* run `npm run build`
|
||||
|
||||
## Application Structure
|
||||
|
||||
### Development
|
||||
During development, the development server will need to proxy an API server. to achieve that:
|
||||
* in [proxy.config.js](proxy.config.js) update the list of targets in line 3 with a working API server URI.
|
||||
* Angular is already configured to use this proxy configuration
|
||||
* If more than 1 API server is configured `apiBaseUrl` should be updated with the server enumeration in [environment.ts](src%2Fenvironments%2Fenvironment.ts)
|
||||
|
||||
Start the development server: `npm run start`
|
||||
|
||||
#### Business Logic module
|
||||
Contains ClearML logic. api calls and ClearML objects (e.g tasks, models) and ClearML logic function (e.g isTaskHidden)
|
||||
|
||||
10
angular.json
10
angular.json
@@ -43,8 +43,7 @@
|
||||
"src/fonts.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/ngx-markdown-editor/assets/highlight.js/highlight.min.js",
|
||||
"node_modules/ngx-markdown-editor/assets/marked.min.js"
|
||||
"node_modules/ngx-markdown-editor/assets/highlight.js/highlight.min.js"
|
||||
],
|
||||
"allowedCommonJsDependencies": [
|
||||
"ansi-to-html",
|
||||
@@ -65,7 +64,9 @@
|
||||
"dom-to-image",
|
||||
"ace-builds",
|
||||
"hocon-parser",
|
||||
"taira"
|
||||
"taira",
|
||||
"base-64",
|
||||
"export-to-csv"
|
||||
],
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
@@ -319,5 +320,8 @@
|
||||
"@angular-eslint/schematics:library": {
|
||||
"setParserOptionsProject": true
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
|
||||
20374
package-lock.json
generated
20374
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
114
package.json
114
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clearml-webapp",
|
||||
"version": "1.12.0",
|
||||
"version": "1.13.0",
|
||||
"license": "",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
@@ -19,34 +19,33 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.2.8",
|
||||
"@angular/cdk": "^15.2.8",
|
||||
"@angular/common": "^15.2.8",
|
||||
"@angular/compiler": "^15.2.8",
|
||||
"@angular/core": "^15.2.8",
|
||||
"@angular/forms": "^15.2.8",
|
||||
"@angular/material": "^15.2.8",
|
||||
"@angular/platform-browser": "^15.2.8",
|
||||
"@angular/platform-browser-dynamic": "^15.2.8",
|
||||
"@angular/platform-server": "^15.2.8",
|
||||
"@angular/router": "^15.2.8",
|
||||
"@angular/service-worker": "^15.2.8",
|
||||
"@angular/youtube-player": "^15.2.8",
|
||||
"@aws-sdk/client-s3": "^3.360.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.360.0",
|
||||
"@ctrl/ngx-github-buttons": "^8.0.0",
|
||||
"@angular/animations": "^16.1.5",
|
||||
"@angular/cdk": "16.1.5",
|
||||
"@angular/common": "^16.1.5",
|
||||
"@angular/compiler": "^16.1.5",
|
||||
"@angular/core": "^16.1.5",
|
||||
"@angular/forms": "^16.1.5",
|
||||
"@angular/material": "16.1.5",
|
||||
"@angular/platform-browser": "^16.1.5",
|
||||
"@angular/platform-browser-dynamic": "^16.1.5",
|
||||
"@angular/platform-server": "^16.1.5",
|
||||
"@angular/router": "^16.1.5",
|
||||
"@angular/service-worker": "^16.1.5",
|
||||
"@angular/youtube-player": "^16.1.5",
|
||||
"@aws-sdk/client-s3": "^3.370.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.370.0",
|
||||
"@ctrl/ngx-github-buttons": "^9.0.0",
|
||||
"@ctrl/tinycolor": "^4.0.1",
|
||||
"@ngneat/dag": "^2.0.0",
|
||||
"@ngrx/effects": "^15.4.0",
|
||||
"@ngrx/entity": "^15.4.0",
|
||||
"@ngrx/router-store": "^15.4.0",
|
||||
"@ngrx/store": "^15.4.0",
|
||||
"ace-builds": "^1.18.0",
|
||||
"angular-google-tag-manager": "^1.7.0",
|
||||
"@ngrx/effects": "^16.1.0",
|
||||
"@ngrx/entity": "^16.1.0",
|
||||
"@ngrx/router-store": "^16.1.0",
|
||||
"@ngrx/store": "^16.1.0",
|
||||
"ace-builds": "^1.23.4",
|
||||
"angular-resizable-element": "^7.0.2",
|
||||
"angular-split": "^15.0.0",
|
||||
"angular2-csv": "^0.2.9",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"bootstrap": "^5.2.3",
|
||||
"bootstrap": "^5.3.0",
|
||||
"britecharts": "^3.0.0-alpha-6.1.6",
|
||||
"curved-arrows": "^0.1.0",
|
||||
"d3-selection": "^3.0.0",
|
||||
@@ -54,6 +53,8 @@
|
||||
"d3-zoom": "^3.0.0",
|
||||
"diff": "^5.1.0",
|
||||
"dom-to-image": "^2.6.0",
|
||||
"dompurify": "^3.0.6",
|
||||
"export-to-csv": "^0.2.1",
|
||||
"filesize": "^10.0.7",
|
||||
"has-ansi": "^5.0.1",
|
||||
"hocon-parser": "^1.0.1",
|
||||
@@ -61,53 +62,54 @@
|
||||
"localforage": "^1.10.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucene": "^2.1.1",
|
||||
"marked": "^7.0.5",
|
||||
"ngx-clipboard": "^16.0.0",
|
||||
"ngx-color-picker": "^14.0.0",
|
||||
"ngx-device-detector": "^5.0.1",
|
||||
"ngx-device-detector": "^6.0.2",
|
||||
"ngx-markdown-editor": "^5.3.1",
|
||||
"ngx-print": "^1.3.1",
|
||||
"ngx-window-token": "^7.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"primeicons": "^6.0.1",
|
||||
"primeng": "^15.4.1",
|
||||
"primeng": "^16.2.0",
|
||||
"process": "^0.11.10",
|
||||
"rxjs": "^7.8.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"string-to-color": "^2.2.2",
|
||||
"taira": "^3.2.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"tslib": "^2.5.0",
|
||||
"url": "^0.11.0",
|
||||
"tslib": "^2.6.0",
|
||||
"url": "^0.11.1",
|
||||
"uuid": "^9.0.0",
|
||||
"zone.js": "^0.12.0"
|
||||
"zone.js": "~0.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.2.6",
|
||||
"@angular-devkit/core": "^15.2.6",
|
||||
"@angular-devkit/schematics": "^15.2.6",
|
||||
"@angular-devkit/schematics-cli": "^15.2.5",
|
||||
"@angular-eslint/builder": "^15.2.1",
|
||||
"@angular-eslint/eslint-plugin": "^15.2.1",
|
||||
"@angular-eslint/eslint-plugin-template": "^15.2.1",
|
||||
"@angular-eslint/schematics": "15.2.1",
|
||||
"@angular-eslint/template-parser": "^15.2.1",
|
||||
"@angular/cli": "^15.2.6",
|
||||
"@angular/compiler-cli": "^15.2.8",
|
||||
"@angular/language-service": "^15.2.8",
|
||||
"@angular-devkit/build-angular": "^16.1.4",
|
||||
"@angular-devkit/core": "^16.1.4",
|
||||
"@angular-devkit/schematics": "^16.1.4",
|
||||
"@angular-devkit/schematics-cli": "^16.1.4",
|
||||
"@angular-eslint/builder": "16.1.2",
|
||||
"@angular-eslint/eslint-plugin": "16.1.2",
|
||||
"@angular-eslint/eslint-plugin-template": "16.1.2",
|
||||
"@angular-eslint/schematics": "16.1.2",
|
||||
"@angular-eslint/template-parser": "^16.1.2",
|
||||
"@angular/cli": "^16.1.4",
|
||||
"@angular/compiler-cli": "^16.1.5",
|
||||
"@angular/language-service": "^16.1.5",
|
||||
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||
"@ngrx/schematics": "^15.4.0",
|
||||
"@ngrx/store-devtools": "^15.4.0",
|
||||
"@ngrx/eslint-plugin": "^16.2.0",
|
||||
"@ngrx/schematics": "^16.1.0",
|
||||
"@ngrx/store-devtools": "^16.1.0",
|
||||
"@types/d3-selection": "^3.0.5",
|
||||
"@types/lodash-es": "^4.17.7",
|
||||
"@types/lodash-es": "^4.17.8",
|
||||
"@types/node": "^18.16.0",
|
||||
"@types/plotly.js": "^2.12.18",
|
||||
"@types/plotly.js": "^2.12.24",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/uuid": "^9.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.1",
|
||||
"@typescript-eslint/parser": "^5.59.1",
|
||||
"codelyzer": "^6.0.2",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-jsdoc": "41.1.2",
|
||||
"eslint-plugin-prefer-arrow": "1.2.3",
|
||||
"typescript": "~4.9.5"
|
||||
"@typescript-eslint/eslint-plugin": "6.7.0",
|
||||
"@typescript-eslint/parser": "6.7.0",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-jsdoc": "^46.6.0",
|
||||
"eslint-plugin-prefer-arrow": "^1.2.3",
|
||||
"typescript": "~5.1.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {ApiUsersService} from './business-logic/api-services/users.service';
|
||||
import {selectCurrentUser} from '@common/core/reducers/users-reducer';
|
||||
import {Component, OnDestroy, OnInit, ViewEncapsulation, HostListener, Renderer2, Injector} from '@angular/core';
|
||||
import {ActivatedRoute, NavigationEnd, Router, Params, RouterEvent} from '@angular/router';
|
||||
import {ActivatedRoute, NavigationEnd, Router} from '@angular/router';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {selectBreadcrumbs, selectLoggedOut} from '@common/core/reducers/view.reducer';
|
||||
import {Store} from '@ngrx/store';
|
||||
@@ -9,11 +9,11 @@ import {selectRouterParams, selectRouterUrl} from '@common/core/reducers/router-
|
||||
import {ApiProjectsService} from './business-logic/api-services/projects.service';
|
||||
import {Project} from './business-logic/model/projects/project';
|
||||
import {getAllSystemProjects, setSelectedProjectId, updateProject} from '@common/core/actions/projects.actions';
|
||||
import {selectSelectedProject} from '@common/core/reducers/projects.reducer';
|
||||
import {selectRouterProjectId, selectSelectedProject} from '@common/core/reducers/projects.reducer';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {getTutorialBucketCredentials} from '@common/core/actions/common-auth.actions';
|
||||
import {termsOfUseAccepted} from '@common/core/actions/users.actions';
|
||||
import {distinctUntilChanged, filter, map, tap} from 'rxjs/operators';
|
||||
import {distinctUntilChanged, filter, tap} from 'rxjs/operators';
|
||||
import * as routerActions from './webapp-common/core/actions/router.actions';
|
||||
import {combineLatest, Observable, Subscription} from 'rxjs';
|
||||
import {ServerUpdatesService} from '@common/shared/services/server-updates.service';
|
||||
@@ -24,14 +24,13 @@ import {UiUpdatesService} from '@common/shared/services/ui-updates.service';
|
||||
import {UsageStatsService} from './core/services/usage-stats.service';
|
||||
import {dismissSurvey} from './core/actions/layout.actions';
|
||||
import {getScaleFactor} from '@common/shared/utils/shared-utils';
|
||||
import {User} from './business-logic/model/users/user';
|
||||
import {ConfigurationService} from '@common/shared/services/configuration.service';
|
||||
import {GoogleTagManagerService} from 'angular-google-tag-manager';
|
||||
import {selectIsSharedAndNotOwner} from './features/experiments/reducers';
|
||||
import {TipsService} from '@common/shared/services/tips.service';
|
||||
import {USER_PREFERENCES_KEY} from '@common/user-preferences';
|
||||
import {Environment} from '../environments/base';
|
||||
import {loadExternalLibrary} from '@common/shared/utils/load-external-library';
|
||||
import {User} from '~/business-logic/model/users/user';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-root',
|
||||
@@ -40,17 +39,14 @@ import {loadExternalLibrary} from '@common/shared/utils/load-external-library';
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
public loggedOut$: Observable<any>;
|
||||
public activeFeature: string;
|
||||
public loggedOut$: Observable<boolean>;
|
||||
private urlSubscription: Subscription;
|
||||
public selectedProject$: Observable<Project>;
|
||||
public projectId: string;
|
||||
public isWorkersContext: boolean;
|
||||
public updatesAvailable$: Observable<any>;
|
||||
private selectedProjectFromUrl$: Observable<string>;
|
||||
public updatesAvailable$: Observable<string>;
|
||||
private breadcrumbsSubscription: Subscription;
|
||||
private selectedCurrentUserSubscription: Subscription;
|
||||
private selectedCurrentUser$: Observable<any>;
|
||||
public showNotification: boolean = true;
|
||||
public demo = ConfigurationService.globalEnvironment.demo;
|
||||
public isLoginContext: boolean;
|
||||
@@ -77,7 +73,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private titleService: Title,
|
||||
private store: Store<any>,
|
||||
private store: Store,
|
||||
private projectsApi: ApiProjectsService,
|
||||
private userService: ApiUsersService,
|
||||
public serverUpdatesService: ServerUpdatesService,
|
||||
@@ -93,16 +89,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
this.isSharedAndNotOwner$ = this.store.select(selectIsSharedAndNotOwner);
|
||||
this.selectedProject$ = this.store.select(selectSelectedProject);
|
||||
this.updatesAvailable$ = this.store.select(selectAvailableUpdates);
|
||||
this.selectedCurrentUser$ = this.store.select(selectCurrentUser);
|
||||
this.selectedProjectFromUrl$ = this.store.select(selectRouterParams)
|
||||
.pipe(
|
||||
filter((params: Params) => !!params),
|
||||
map(params => params?.projectId || null)
|
||||
);
|
||||
|
||||
if (ConfigurationService.globalEnvironment.GTM_ID) {
|
||||
this.gtmService = injector.get(GoogleTagManagerService);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -129,7 +115,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
this.router.events
|
||||
.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe(
|
||||
(item: RouterEvent) => {
|
||||
(item: NavigationEnd) => {
|
||||
const gtmTag = {
|
||||
event: 'page',
|
||||
pageName: item.url
|
||||
@@ -138,8 +124,8 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
this.store.dispatch(routerActions.navigationEnd());
|
||||
});
|
||||
|
||||
this.selectedCurrentUserSubscription = this.selectedCurrentUser$.pipe(
|
||||
tap(user => this.currentUser = user), // should not be filtered
|
||||
this.selectedCurrentUserSubscription = this.store.select(selectCurrentUser).pipe(
|
||||
tap(user => this.currentUser = user as unknown as User),
|
||||
filter(user => !!user?.id),
|
||||
distinctUntilChanged((prev, next) => prev?.id === next?.id)
|
||||
)
|
||||
@@ -158,27 +144,23 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
});
|
||||
|
||||
this.selectedProjectFromUrl$.subscribe((projectId: string) => {
|
||||
this.store.select(selectRouterProjectId).subscribe((projectId: string) => {
|
||||
this.store.dispatch(setSelectedProjectId({projectId}));
|
||||
});
|
||||
|
||||
this.urlSubscription = combineLatest([this.store.select(selectRouterUrl), this.store.select(selectRouterParams)])
|
||||
this.urlSubscription = combineLatest([
|
||||
this.store.select(selectRouterUrl),
|
||||
this.store.select(selectRouterParams)
|
||||
])
|
||||
.subscribe(([url, params]) => {
|
||||
this.projectId = params?.projectId;
|
||||
this.isLoginContext = url && url.includes('login');
|
||||
this.isWorkersContext = url && url.includes('workers-and-queues');
|
||||
if (this.projectId) {
|
||||
try { // TODO: refactor to a better solution after all navbar are declared...
|
||||
this.activeFeature = url.split(this.projectId)[1].split('/')[1];
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.breadcrumbsSubscription = this.store.select(selectBreadcrumbs).subscribe(breadcrumbs => {
|
||||
const crumbs = breadcrumbs.flat().map(breadcrumb=> breadcrumb.name);
|
||||
this.titleService.setTitle(`${this.title ? this.title + '-' : ''} ${crumbs.join(' / ')}`);
|
||||
const crumbs = breadcrumbs.flat().filter((breadcrumb => !!breadcrumb?.name)).map(breadcrumb => breadcrumb.name);
|
||||
crumbs.length> 0 && this.titleService.setTitle(`${this.title ? this.title + '-' : ''} ${crumbs.join(' / ')}`);
|
||||
});
|
||||
|
||||
if (window.localStorage.getItem('disableHidpi') !== 'true') {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {CommonLayoutModule} from '@common/layout/layout.module';
|
||||
import {HTTP_INTERCEPTORS} from '@angular/common/http';
|
||||
import {WebappInterceptor} from '@common/core/interceptors/webapp-interceptor';
|
||||
import {CustomReuseStrategy} from '@common/core/router-reuse-strategy';
|
||||
import {loadUserAndPreferences, UserPreferences} from '@common/user-preferences';
|
||||
import {UserPreferences} from '@common/user-preferences';
|
||||
import {AngularSplitModule} from 'angular-split';
|
||||
import {NotifierModule} from '@common/angular-notifier';
|
||||
import {LayoutModule} from './layout/layout.module';
|
||||
@@ -23,6 +23,8 @@ import {ProjectsSharedModule} from './features/projects/shared/projects-shared.m
|
||||
import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field';
|
||||
import {LoginService} from '~/shared/services/login.service';
|
||||
import {ExperimentSharedModule} from '~/features/experiments/shared/experiment-shared.module';
|
||||
import {loadUserAndPreferences} from '~/core/app-init';
|
||||
import {MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltipDefaultOptions} from '@angular/material/tooltip';
|
||||
|
||||
@NgModule({
|
||||
declarations : [AppComponent],
|
||||
@@ -69,11 +71,9 @@ import {ExperimentSharedModule} from '~/features/experiments/shared/experiment-s
|
||||
{provide: HTTP_INTERCEPTORS, useClass: WebappInterceptor, multi: true},
|
||||
{provide: RouteReuseStrategy, useClass: CustomReuseStrategy},
|
||||
{
|
||||
provide: 'googleTagManagerId',
|
||||
deps: [ConfigurationService],
|
||||
useFactory: (confService: ConfigurationService) =>
|
||||
confService.getStaticEnvironment().GTM_ID
|
||||
},
|
||||
provide: MAT_TOOLTIP_DEFAULT_OPTIONS,
|
||||
useValue: {position: 'above'} as MatTooltipDefaultOptions
|
||||
}
|
||||
],
|
||||
bootstrap : [AppComponent],
|
||||
exports : []
|
||||
|
||||
@@ -66,6 +66,8 @@ import { ModelsUpdateResponse } from '../model/models/modelsUpdateResponse';
|
||||
|
||||
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
||||
import { Configuration } from '../configuration';
|
||||
import {ModelsUpdateTagsRequest} from '~/business-logic/model/models/modelsUpdateTagsRequest';
|
||||
import {ModelsUpdateTagsResponse} from '~/business-logic/model/models/modelsUpdateTagsResponse';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@@ -101,7 +103,7 @@ export class ApiModelsService {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Add or update model metadata
|
||||
* @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.
|
||||
@@ -146,7 +148,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Archive models
|
||||
* @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.
|
||||
@@ -191,7 +193,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Create a new model not associated with a task
|
||||
* @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.
|
||||
@@ -236,7 +238,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Delete a model.
|
||||
* @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.
|
||||
@@ -281,7 +283,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Delete models
|
||||
* @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.
|
||||
@@ -326,7 +328,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Delete metadata from model
|
||||
* @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.
|
||||
@@ -371,7 +373,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Edit an existing model
|
||||
* @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.
|
||||
@@ -416,7 +418,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get all models
|
||||
* @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.
|
||||
@@ -461,7 +463,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get all models
|
||||
* @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.
|
||||
@@ -506,7 +508,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Gets model information
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -551,7 +553,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get all models
|
||||
* @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.
|
||||
@@ -596,7 +598,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Gets model information
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -641,7 +643,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get the list of frameworks used in the company models
|
||||
* @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.
|
||||
@@ -686,7 +688,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Convert public models to private
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -731,7 +733,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Convert company models to public
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -776,7 +778,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Move models to a project
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -821,7 +823,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Publish models
|
||||
* @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.
|
||||
@@ -866,7 +868,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Set the model ready flag to True. If the model is an output model of a task then try to publish the task.
|
||||
* @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.
|
||||
@@ -911,7 +913,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Unarchive models
|
||||
* @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.
|
||||
@@ -956,7 +958,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Update a model
|
||||
* @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.
|
||||
@@ -1001,7 +1003,7 @@ export class ApiModelsService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Create or update a new model for a task
|
||||
* @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.
|
||||
@@ -1045,4 +1047,48 @@ export class ApiModelsService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add/Remove multiple tags from multiple models
|
||||
* @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 modelsUpdateTags(request: ModelsUpdateTagsRequest, 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 modelsUpdateTags.');
|
||||
}
|
||||
|
||||
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<ModelsUpdateTagsResponse>(`${this.basePath}/models.update_tags`,
|
||||
request,
|
||||
{
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: headers,
|
||||
observe: observe,
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ import { TasksValidateRequest } from '../model/tasks/tasksValidateRequest';
|
||||
|
||||
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
||||
import { Configuration } from '../configuration';
|
||||
import {TasksUpdateTagsResponse} from '~/business-logic/model/tasks/tasksUpdateTagsResponse';
|
||||
import {TasksUpdateTagsRequest} from '~/business-logic/model/tasks/tasksUpdateTagsRequest';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@@ -150,7 +152,7 @@ export class ApiTasksService {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Update existing artifacts (search by key/mode) and add new ones
|
||||
* @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.
|
||||
@@ -195,7 +197,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Add or update task model
|
||||
* @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.
|
||||
@@ -240,7 +242,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Archive tasks. If a task is queued it will first be dequeued and then archived.
|
||||
* @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.
|
||||
@@ -285,7 +287,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Archive 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.
|
||||
@@ -330,7 +332,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Clone an existing task
|
||||
* @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.
|
||||
@@ -375,7 +377,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Indicates that task is closed
|
||||
* @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.
|
||||
@@ -420,7 +422,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Signal a task has completed
|
||||
* @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.
|
||||
@@ -465,7 +467,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Create a new task
|
||||
* @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.
|
||||
@@ -510,7 +512,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Delete a task along with any information stored for it (statistics, frame updates etc.) Unless Force flag is provided, operation will fail if task has objects associated with it - i.e. children tasks and projects. Models that refer to the deleted task will be updated with a task ID indicating a deleted task.
|
||||
* @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.
|
||||
@@ -555,7 +557,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Delete existing artifacts (search by key/mode)
|
||||
* @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.
|
||||
@@ -600,7 +602,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Delete task configuration items
|
||||
* @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.
|
||||
@@ -645,7 +647,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Delete task hyper parameters
|
||||
* @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.
|
||||
@@ -690,7 +692,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Delete 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.
|
||||
@@ -735,7 +737,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Delete models from task
|
||||
* @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.
|
||||
@@ -780,7 +782,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Remove a task from its queue. Fails if task status is not queued.
|
||||
* @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.
|
||||
@@ -825,7 +827,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Dequeue 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.
|
||||
@@ -870,7 +872,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Edit task\'s details.
|
||||
* @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.
|
||||
@@ -915,7 +917,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Add or update task configuration
|
||||
* @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.
|
||||
@@ -960,7 +962,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Add or update task hyper parameters
|
||||
* @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.
|
||||
@@ -1005,7 +1007,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Adds a task into a queue. Fails if task state is not \'created\'. Fails if the following parameters in the task were not filled: * execution.script.repository * execution.script.entrypoint
|
||||
* @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.
|
||||
@@ -1050,7 +1052,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Enqueue 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.
|
||||
@@ -1095,7 +1097,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Indicates that task has failed
|
||||
* @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.
|
||||
@@ -1140,7 +1142,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get all the company\'s tasks and all public 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.
|
||||
@@ -1185,7 +1187,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get all the company\'s tasks and all public 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.
|
||||
@@ -1230,7 +1232,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Gets task information
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -1275,7 +1277,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get all the company\'s tasks and all public 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.
|
||||
@@ -1320,7 +1322,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get the list of task configuration items names
|
||||
* @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.
|
||||
@@ -1365,7 +1367,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get the list of task configurations
|
||||
* @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.
|
||||
@@ -1410,7 +1412,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get the list of task hyper parameters
|
||||
* @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.
|
||||
@@ -1455,7 +1457,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Get the list of task types used in the specified projects
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -1500,7 +1502,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Convert public tasks to private
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -1545,7 +1547,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Convert company tasks to public
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -1590,7 +1592,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Move tasks to a project
|
||||
* @param request request body
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -1635,7 +1637,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Refresh the task\'s last update time
|
||||
* @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.
|
||||
@@ -1680,7 +1682,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Mark a task status as published. For Annotation tasks - if any changes were committed by this task, a new version in the dataset together with an output view are created. For Training tasks - if a model was created, it should be set to ready.
|
||||
* @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.
|
||||
@@ -1725,7 +1727,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Publish 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.
|
||||
@@ -1770,7 +1772,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Reset a task to its initial state, along with any information stored for it (statistics, frame updates etc.).
|
||||
* @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.
|
||||
@@ -1815,7 +1817,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Reset 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.
|
||||
@@ -1860,7 +1862,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Set the script requirements for a task
|
||||
* @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.
|
||||
@@ -1905,7 +1907,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Mark a task status as in_progress. Optionally allows to set the task\'s execution progress.
|
||||
* @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.
|
||||
@@ -1950,7 +1952,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Request to stop a running task
|
||||
* @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.
|
||||
@@ -1995,7 +1997,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Request to stop running 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.
|
||||
@@ -2040,7 +2042,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Signal a task has stopped
|
||||
* @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.
|
||||
@@ -2085,7 +2087,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Unarchive 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.
|
||||
@@ -2130,7 +2132,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Update task\'s runtime parameters
|
||||
* @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.
|
||||
@@ -2175,7 +2177,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Updates a batch of tasks. Headers Content type should be \'application/json- lines\'.
|
||||
* @param requests Json encoded newline-terminated lines, each representing an event in the batch and uses the same parameters used for tasks.update
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
@@ -2220,7 +2222,7 @@ export class ApiTasksService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Validate task properties (before create)
|
||||
* @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.
|
||||
@@ -2264,4 +2266,48 @@ export class ApiTasksService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add/Remove multiple tags from multiple 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 tasksUpdateTags(request: TasksUpdateTagsRequest, 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 tasksUpdateTags.');
|
||||
}
|
||||
|
||||
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<TasksUpdateTagsResponse>(`${this.basePath}/tasks.update_tags`,
|
||||
request,
|
||||
{
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: headers,
|
||||
observe: observe,
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
* See: https://github.com/angular/angular/issues/11058#issuecomment-247367318
|
||||
*/
|
||||
export class CustomHttpUrlEncodingCodec extends HttpUrlEncodingCodec {
|
||||
encodeKey(k: string): string {
|
||||
override encodeKey(k: string): string {
|
||||
k = super.encodeKey(k);
|
||||
return k.replace(/\+/gi, '%2B');
|
||||
}
|
||||
encodeValue(v: string): string {
|
||||
|
||||
override encodeValue(v: string): string {
|
||||
v = super.encodeValue(v);
|
||||
return v.replace(/\+/gi, '%2B');
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@ export interface DebugImagesResponseIterations {
|
||||
* Iteration number
|
||||
*/
|
||||
iter?: number;
|
||||
events?: Array<object>;
|
||||
events?: Array<any>;
|
||||
}
|
||||
|
||||
@@ -17,4 +17,5 @@ export interface EventsGetTaskSingleValueMetricsRequest {
|
||||
* List of task Task IDs
|
||||
*/
|
||||
tasks: Array<string>;
|
||||
model_events: boolean;
|
||||
}
|
||||
|
||||
@@ -18,5 +18,6 @@ export interface EventsGetTaskSingleValueMetricsResponseTasks {
|
||||
* Task ID
|
||||
*/
|
||||
task?: string;
|
||||
values?: Array<EventsGetTaskSingleValueMetricsResponseValues>;
|
||||
task_name?: string;
|
||||
values?: Array<EventsGetTaskSingleValueMetricsResponseValues>;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ export interface Model {
|
||||
/**
|
||||
* Model last update time
|
||||
*/
|
||||
last_update?: string;
|
||||
last_update?: Date;
|
||||
last_change?: Date;
|
||||
/**
|
||||
* Task ID of task in which the model was created
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* models
|
||||
* 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 ModelsUpdateTagsRequest {
|
||||
/**
|
||||
* IDs of the models to update
|
||||
*/
|
||||
ids?: Array<string>;
|
||||
/**
|
||||
* User tags to add
|
||||
*/
|
||||
add_tags?: Array<string>;
|
||||
/**
|
||||
* User tags to remove
|
||||
*/
|
||||
remove_tags?: Array<string>;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* models
|
||||
* 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 ModelsUpdateTagsResponse {
|
||||
/**
|
||||
* The number of updated models
|
||||
*/
|
||||
updated?: number;
|
||||
}
|
||||
@@ -33,4 +33,13 @@ export interface ProjectsGetHyperparamValuesRequest {
|
||||
* If set to \'true\' and the project field is set then the result includes hyper parameters values from the subproject tasks
|
||||
*/
|
||||
include_subprojects?: boolean;
|
||||
/**
|
||||
* Page number
|
||||
*/
|
||||
page?: number;
|
||||
/**
|
||||
* Page size
|
||||
*/
|
||||
page_size?: number;
|
||||
pattern?: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* tasks
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* All the terms in \'all\' condition are combined with \'and\' operation
|
||||
*/
|
||||
export interface TasksGetAllExRequestAll {
|
||||
include?: Array<string>;
|
||||
exclude?: Array<string>;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* tasks
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* All the terms in \'any\' condition are combined with \'or\' operation
|
||||
*/
|
||||
export interface TasksGetAllExRequestAny {
|
||||
include?: Array<string>;
|
||||
exclude?: Array<string>;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* tasks
|
||||
* 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 { TasksGetAllExRequestAll } from '././tasksGetAllExRequestAll';
|
||||
import { TasksGetAllExRequestAny } from '././tasksGetAllExRequestAny';
|
||||
|
||||
|
||||
/**
|
||||
* Filter on a field that includes combination of \'any\' or \'all\' included and excluded terms
|
||||
*/
|
||||
export interface TasksGetAllExRequestFilters {
|
||||
any?: TasksGetAllExRequestAny;
|
||||
all?: TasksGetAllExRequestAll;
|
||||
/**
|
||||
* The operation between \'any\' and \'all\' parts of the filter if both are provided
|
||||
*/
|
||||
op?: TasksGetAllExRequestFilters.OpEnum;
|
||||
}
|
||||
export namespace TasksGetAllExRequestFilters {
|
||||
export type OpEnum = 'and' | 'or';
|
||||
export const OpEnum = {
|
||||
And: 'and' as OpEnum,
|
||||
Or: 'or' as OpEnum
|
||||
}
|
||||
}
|
||||
28
src/app/business-logic/model/tasks/tasksUpdateTagsRequest.ts
Normal file
28
src/app/business-logic/model/tasks/tasksUpdateTagsRequest.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* tasks
|
||||
* 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 TasksUpdateTagsRequest {
|
||||
/**
|
||||
* IDs of the tasks to update
|
||||
*/
|
||||
ids?: Array<string>;
|
||||
/**
|
||||
* User tags to add
|
||||
*/
|
||||
add_tags?: Array<string>;
|
||||
/**
|
||||
* User tags to remove
|
||||
*/
|
||||
remove_tags?: Array<string>;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* tasks
|
||||
* 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 TasksUpdateTagsResponse {
|
||||
/**
|
||||
* The number of updated tasks
|
||||
*/
|
||||
updated?: number;
|
||||
}
|
||||
@@ -15,4 +15,6 @@
|
||||
export interface GetCurrentUserResponseUserObjectCompany {
|
||||
id?: string;
|
||||
name?: string;
|
||||
tier?: any;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* users
|
||||
* 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 UsersGetInvitesResponseInvites {
|
||||
/**
|
||||
* Invite id
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* Company id
|
||||
*/
|
||||
company?: string;
|
||||
/**
|
||||
* Company name
|
||||
*/
|
||||
company_name?: string;
|
||||
/**
|
||||
* Invite creation time (UTC)
|
||||
*/
|
||||
created?: string;
|
||||
/**
|
||||
* Invite expiration time (UTC)
|
||||
*/
|
||||
expires?: string;
|
||||
/**
|
||||
* Invited emails
|
||||
*/
|
||||
emails?: Array<string>;
|
||||
}
|
||||
11
src/app/core/app-init.ts
Normal file
11
src/app/core/app-init.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import {LoginService} from '~/shared/services/login.service';
|
||||
import {ConfigurationService} from '@common/shared/services/configuration.service';
|
||||
|
||||
export const loadUserAndPreferences = (
|
||||
loginService: LoginService,
|
||||
confService: ConfigurationService,
|
||||
): () => Promise<any> => (): Promise<any> => new Promise((resolve) => {
|
||||
confService.initConfigurationService().subscribe(() =>
|
||||
loginService.initCredentials().subscribe(() => loginService.loginFlow(resolve))
|
||||
);
|
||||
});
|
||||
@@ -61,6 +61,8 @@ const syncedKeys = [
|
||||
'projects.selectedProject',
|
||||
'rootProjects.showHidden',
|
||||
'rootProjects.hideExamples',
|
||||
'rootProjects.mainPageTagsFilter',
|
||||
'rootProjects.mainPageTagsFilterMatchMode',
|
||||
'rootProjects.defaultNestedModeForFeature',
|
||||
'views.availableUpdates',
|
||||
'views.showSurvey',
|
||||
@@ -92,7 +94,7 @@ export const localStorageReducer = (reducer: ActionReducer<any>): ActionReducer<
|
||||
return nextState;
|
||||
};
|
||||
|
||||
const userPrefMetaFactory = (userPreferences: UserPreferences): MetaReducer<any>[] => [
|
||||
const userPrefMetaFactory = (userPreferences: UserPreferences): MetaReducer[] => [
|
||||
(reducer: ActionReducer<any>) =>
|
||||
createUserPrefReducer('users', ['activeWorkspace', 'showOnlyUserWork'], [USERS_PREFIX], userPreferences, reducer),
|
||||
(reducer: ActionReducer<any>) =>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {Actions, concatLatestFrom, createEffect, ofType} from '@ngrx/effects';
|
||||
import * as actions from '../../webapp-common/core/actions/projects.actions';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectSelectedProjectId, selectShowHidden} from '@common/core/reducers/projects.reducer';
|
||||
import {catchError, finalize, mergeMap, switchMap, withLatestFrom} from 'rxjs/operators';
|
||||
import {catchError, finalize, mergeMap, switchMap} from 'rxjs/operators';
|
||||
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';
|
||||
@@ -26,15 +26,14 @@ export class ProjectsEffects {
|
||||
|
||||
getSelectedProject = createEffect(() => this.actions$.pipe(
|
||||
ofType(actions.setSelectedProjectId),
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectSelectedProjectId),
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectShowOnlyUserWork),
|
||||
this.store.select(selectShowHidden),
|
||||
this.store.select(selectRouterConfig),
|
||||
),
|
||||
]),
|
||||
switchMap(([action, selectedProjectId, user, showOnlyUserWork, showHidden, conf]) => {
|
||||
const customProjectType = conf[0] !== 'projects';
|
||||
if (!action.projectId) {
|
||||
return [
|
||||
deactivateLoader(action.type),
|
||||
@@ -51,6 +50,7 @@ export class ProjectsEffects {
|
||||
actions.getCompanyTags(),
|
||||
deactivateLoader(action.type)];
|
||||
} else {
|
||||
const customProjectType = conf?.[0] !== 'projects';
|
||||
this.fetchingExampleExperiment = action.example && action.projectId;
|
||||
return this.projectsApi.projectsGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
@@ -28,7 +28,7 @@ export class NestedDatasetsPageComponent extends CommonProjectsPageComponent {
|
||||
hideMenu = false;
|
||||
entityType = ProjectTypeEnum.datasets;
|
||||
|
||||
projectCardClicked(data: { hasSubProjects: boolean; id: string; name: string }) {
|
||||
override projectCardClicked(data: { hasSubProjects: boolean; id: string; name: string }) {
|
||||
if (data.hasSubProjects) {
|
||||
this.router.navigate(['simple', data.id, 'projects'], {relativeTo: this.route.parent?.parent});
|
||||
} else {
|
||||
@@ -50,7 +50,7 @@ export class NestedDatasetsPageComponent extends CommonProjectsPageComponent {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
protected getExtraProjects(selectedProjectId, selectedProject) {
|
||||
protected override getExtraProjects(selectedProjectId, selectedProject) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import {ConfigurationItem} from '../../../business-logic/model/tasks/configurati
|
||||
import {GetCurrentUserResponseUserObjectCompany} from '../../../business-logic/model/users/getCurrentUserResponseUserObjectCompany';
|
||||
import {Queue} from '../../../business-logic/model/queues/queue';
|
||||
import {Container} from '../../../business-logic/model/tasks/container';
|
||||
import {Output} from '~/business-logic/model/tasks/output';
|
||||
|
||||
/**
|
||||
* an extended object of task that includes projection, will come from the server as an api response.
|
||||
@@ -57,7 +58,7 @@ export interface ISelectedExperiment {
|
||||
/**
|
||||
* an object that will transfrom from the ISelectedExperiment response to more comfortable object to display.
|
||||
*/
|
||||
export interface IExperimentInfo extends Omit<Task, 'id' | 'user' | 'project' | 'company' | 'execution' | 'container'> {
|
||||
export interface IExperimentInfo extends Omit<Task, 'id' | 'user' | 'project' | 'company' | 'execution' | 'container'|'output'> {
|
||||
id?: string;
|
||||
model?: IExperimentModelInfo;
|
||||
execution?: IExecutionForm;
|
||||
@@ -70,9 +71,10 @@ export interface IExperimentInfo extends Omit<Task, 'id' | 'user' | 'project' |
|
||||
project?: Project;
|
||||
company?: GetCurrentUserResponseUserObjectCompany;
|
||||
container?: Container;
|
||||
output?: ISelectedExperimentOutput
|
||||
}
|
||||
|
||||
export interface ISelectedExperimentOutput {
|
||||
export interface ISelectedExperimentOutput extends Omit<Output, 'model'> {
|
||||
destination?: string;
|
||||
model?: {
|
||||
id: Model['id'];
|
||||
|
||||
@@ -16,4 +16,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<router-outlet class="outlet"></router-outlet>
|
||||
<div class="content">
|
||||
<router-outlet class="outlet"></router-outlet>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {HTTP} from '~/app.constants';
|
||||
|
||||
export const isFileserverUrl = (url: string) =>
|
||||
url.startsWith(HTTP.FILE_BASE_URL);
|
||||
url?.startsWith(HTTP.FILE_BASE_URL);
|
||||
|
||||
export const convertToReverseProxy = (url: string) => {
|
||||
try {
|
||||
|
||||
@@ -262,7 +262,10 @@ export class NotifierContainerComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
Promise.all(stepPromises).then(() => {
|
||||
if (numberOfNotifications > this.config.behaviour.stacking) {
|
||||
if (typeof this.config.behaviour.stacking === 'number' ?
|
||||
numberOfNotifications > this.config.behaviour.stacking :
|
||||
!this.config.behaviour.stacking
|
||||
) {
|
||||
this.removeNotificationFromList(this.notifications[0]);
|
||||
}
|
||||
this.tempPromiseResolver(null);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
@font-face {
|
||||
font-family: '#{$icomoon-font-family}';
|
||||
src: url('./#{$icomoon-font-family}.ttf?n9hc0z') format('truetype');
|
||||
src: url('./#{$icomoon-font-family}.ttf?wj7mzq') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
@@ -23,20 +23,45 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
.al-ico-tune {
|
||||
&:before {
|
||||
content: $al-ico-tune;
|
||||
}
|
||||
}
|
||||
|
||||
.al-ico-equal-outline {
|
||||
&:before {
|
||||
content: $al-ico-equal-outline;
|
||||
}
|
||||
}
|
||||
.al-ico-maximize {
|
||||
&:before {
|
||||
content: $al-ico-maximize;
|
||||
}
|
||||
}
|
||||
.al-ico-gpu-card {
|
||||
&:before {
|
||||
content: $al-ico-gpu-card;
|
||||
}
|
||||
}
|
||||
.al-ico-legend {
|
||||
&:before {
|
||||
content: $al-ico-legend;
|
||||
}
|
||||
}
|
||||
.al-ico-model-filled {
|
||||
&:before {
|
||||
content: $al-ico-model-filled;
|
||||
content: $al-ico-model-filled;
|
||||
}
|
||||
}
|
||||
.al-ico-type-report {
|
||||
&:before {
|
||||
content: $al-ico-type-report;
|
||||
content: $al-ico-type-report;
|
||||
}
|
||||
}
|
||||
.al-ico-info-circle-outline {
|
||||
&:before {
|
||||
content: $al-ico-info-circle-outline;
|
||||
content: $al-ico-info-circle-outline;
|
||||
}
|
||||
}
|
||||
.al-ico-ghost {
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,11 @@
|
||||
$icomoon-font-family: "trains" !default;
|
||||
$icomoon-font-path: "fonts" !default;
|
||||
|
||||
$al-ico-tune: "\e9f8";
|
||||
$al-ico-equal-outline: "\e9f7";
|
||||
$al-ico-maximize: "\e9f6";
|
||||
$al-ico-gpu-card: "\e9f4";
|
||||
$al-ico-legend: "\e9f3";
|
||||
$al-ico-model-filled: "\e9f2";
|
||||
$al-ico-type-report: "\e9f1";
|
||||
$al-ico-info-circle-outline: "\e9f0";
|
||||
|
||||
3
src/app/webapp-common/assets/icons/failed-sm.svg
Normal file
3
src/app/webapp-common/assets/icons/failed-sm.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10.62" height="10.62" viewBox="0 0 10.62 10.62">
|
||||
<polygon points="9.24 2.24 8.39 1.39 5.31 4.46 2.24 1.39 1.39 2.24 4.46 5.31 1.39 8.39 2.24 9.24 5.31 6.16 8.39 9.24 9.24 8.39 6.16 5.31 9.24 2.24" fill="#e2001b"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 271 B |
3
src/app/webapp-common/assets/icons/no-table-data.svg
Normal file
3
src/app/webapp-common/assets/icons/no-table-data.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="242" viewBox="0 0 300 242">
|
||||
<path d="m279.18,0H20.82C9.33.01.01,9.33,0,20.82v200.01c.01,11.49,9.33,20.81,20.82,20.82h258.36c11.49-.01,20.81-9.33,20.82-20.82V20.82C299.99,9.33,290.67.01,279.18,0ZM103.29,230.93v-26.35h89.77v26.35h-89.77Zm0-37.35v-26.55h89.77v26.55h-89.77Zm-11-139.21v26.55H10.71v-26.55h81.58Zm111.77,26.55v-26.55h85.23v14.51h0v12.04h-85.23Zm85.23,11v26.55h-85.23v-26.55h85.23Zm-96.23-37.55v9.62s0,0,0,.01c0,0,0,0,0,0v16.91h-89.77v-26.55h89.77Zm0,37.55v26.55h-89.77v-26.55h89.77Zm-100.77,26.55H10.71v-26.55h81.58v26.55Zm-81.58,11h81.58v26.55H10.71v-26.55Zm92.58,0h89.77v26.55h-89.77v-26.55Zm100.77,0h85.23v26.55h-85.23v-26.55ZM10.71,167.03h81.58v26.55H10.71v-26.55Zm193.35,0h85.23v26.55h-85.23v-26.55ZM10.71,220.83v-16.25h81.58v26.35H20.82c-5.56-.05-10.06-4.54-10.11-10.1Zm268.52,10.1s-.03,0-.05,0h0s-75.12,0-75.12,0v-26.35h85.23v16.55c-.07,5.48-4.57,9.87-10.06,9.8Z" opacity="0.1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 967 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13.12" height="15" viewBox="0 0 13.12 15">
|
||||
<path d="m8.57,13.04c.16.16.24.36.24.59s-.08.43-.24.59-.37.24-.64.24-.48-.08-.64-.24-.24-.36-.24-.59.08-.43.24-.59.37-.24.64-.24.48.08.64.24Zm.2-6.03h-1.68l.19,5.06h1.3l.19-5.06Zm4.16-.22c-.12-.21-.3-.38-.51-.51L2.12.19C1.46-.21.6,0,.2.67c-.13.22-.2.48-.2.74v12.19c0,.77.62,1.4,1.39,1.41.26,0,.51-.07.73-.2l3.88-2.29v-1.63l-4.33,2.56c-.09.05-.19.02-.24-.07-.02-.03-.02-.06-.02-.09V1.71c0-.1.08-.18.18-.18.03,0,.06,0,.09.02l9.79,5.79c.08.05.11.16.06.24-.01.02-.03.04-.06.06l-1.46.87v1.63l2.43-1.44c.67-.39.89-1.25.51-1.91Z" fill="#8492C2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 639 B |
3
src/app/webapp-common/assets/icons/running-notify.svg
Normal file
3
src/app/webapp-common/assets/icons/running-notify.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13.12" height="15" viewBox="0 0 13.12 15">
|
||||
<path d="m8.57,13.04c.16.16.24.36.24.59s-.08.43-.24.59-.37.24-.64.24-.48-.08-.64-.24-.24-.36-.24-.59.08-.43.24-.59.37-.24.64-.24.48.08.64.24Zm.2-6.03h-1.68l.19,5.06h1.3l.19-5.06Zm4.16-.22c-.12-.21-.3-.38-.51-.51L2.12.19C1.46-.21.6,0,.2.67c-.13.22-.2.48-.2.74v12.19c0,.77.62,1.4,1.39,1.41.26,0,.51-.07.73-.2l3.88-2.29v-1.63l-4.33,2.56c-.09.05-.19.02-.24-.07-.02-.03-.02-.06-.02-.09V1.71c0-.1.08-.18.18-.18.03,0,.06,0,.09.02l9.79,5.79c.08.05.11.16.06.24-.01.02-.03.04-.06.06l-1.46.87v1.63l2.43-1.44c.67-.39.89-1.25.51-1.91Z" fill="#50e3c3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 638 B |
@@ -50,7 +50,7 @@ export const getSingleValues = createAction('[App] getSingleValues', props<{
|
||||
export const setPlotData = createAction('[App] setPlot', props<{ data: ReportsApiMultiplotsResponse }>());
|
||||
export const setScalarData = createAction('[App] setScalar', props<{ data: ExtFrame[] }>());
|
||||
export const setSampleData = createAction('[App] setSample', props<{ data: DebugSample }>());
|
||||
export const setSingleValues = createAction('[App] setSingleValues', props<{ data: SingleValueTaskMetrics }>());
|
||||
export const setSingleValues = createAction('[App] setSingleValues', props<{ data: SingleValueTaskMetrics[] }>());
|
||||
export const setParallelCoordinateExperiments = createAction('[App] setParcoor', props<{ data: Task[] }>());
|
||||
|
||||
export const reportsPlotlyReady = createAction('[App] plotly ready');
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<a class="webapp-link" [href]="webappLink" target="_blank" [class.dark-theme]="isDarkTheme">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path d="m2.67,14.66c-.74,0-1.33-.6-1.33-1.33h0V2.67c0-.74.6-1.33,1.33-1.33h5.33v1.33H2.67v10.67h10.67v-5.33h1.34v5.33c0,.74-.6,1.33-1.34,1.33H2.67Zm4.86-7.14l4.86-4.86h-1.72v-1.33h4v4h-1.33v-1.72l-4.86,4.86-.95-.94Z" fill="#8693be"/>
|
||||
<path d="m2.67,14.66c-.74,0-1.33-.6-1.33-1.33h0V2.67c0-.74.6-1.33,1.33-1.33h5.33v1.33H2.67v10.67h10.67v-5.33h1.34v5.33c0,.74-.6,1.33-1.34,1.33H2.67Zm4.86-7.14l4.86-4.86h-1.72v-1.33h4v4h-1.33v-1.72l-4.86,4.86-.95-.94Z" fill="#8492c2"/>
|
||||
</svg>
|
||||
<span class="webapp-link_tooltip">View original resource</span>
|
||||
</a>
|
||||
@@ -53,14 +53,14 @@
|
||||
></sm-parallel-coordinates-graph>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'single'">
|
||||
<div *ngSwitchCase="'single'" class="single-value-summary-section">
|
||||
<sm-single-value-summary-table
|
||||
*ngIf="singleValueData && singleValueData[0]"
|
||||
[data]="singleValueData"
|
||||
[experimentName]="singleValueData[0]?.metric"
|
||||
[darkTheme]="isDarkTheme"
|
||||
class="single-value-summary-table"></sm-single-value-summary-table>
|
||||
</ng-container>
|
||||
></sm-single-value-summary-table>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
</ng-container>
|
||||
|
||||
@@ -11,7 +11,18 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.single-value-summary-section {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 12px;
|
||||
|
||||
sm-single-value-summary-table {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.show-text {
|
||||
cursor: pointer;
|
||||
|
||||
@@ -20,7 +20,7 @@ import {ExtFrame} from '@common/shared/single-graph/plotly-graph-base';
|
||||
import {DebugSample} from '@common/shared/debug-sample/debug-sample.reducer';
|
||||
import {getSignedUrl, setS3Credentials} from '@common/core/actions/common-auth.actions';
|
||||
import {ConfigurationService} from '@common/shared/services/configuration.service';
|
||||
import {_mergeVariants, convertMultiPlots, mergeMultiMetricsGroupedVariant, prepareMultiPlots, tryParseJson} from '@common/tasks/tasks.utils';
|
||||
import {_mergeVariants, convertMultiPlots, createMultiSingleValuesChart, mergeMultiMetricsGroupedVariant, prepareGraph, prepareMultiPlots, tryParseJson} from '@common/tasks/tasks.utils';
|
||||
import {selectSignedUrl} from '@common/core/reducers/common-auth-reducer';
|
||||
import {loadExternalLibrary} from '@common/shared/utils/load-external-library';
|
||||
import {ImageViewerComponent} from '@common/shared/debug-sample/image-viewer/image-viewer.component';
|
||||
@@ -47,7 +47,6 @@ export class AppComponent implements OnInit {
|
||||
title = 'report-widgets';
|
||||
public plotData: ExtFrame;
|
||||
public frame: DebugSample;
|
||||
public parcoords: any;
|
||||
public plotLoaded: boolean;
|
||||
private environment: Environment;
|
||||
public activated: boolean = false;
|
||||
@@ -193,8 +192,8 @@ export class AppComponent implements OnInit {
|
||||
const newGraphs = convertMultiPlots(merged);
|
||||
const originalObject = this.searchParams.get('objects');
|
||||
const series = this.searchParams.get('series');
|
||||
this.plotData = Object.values(newGraphs)[0]?.find(a => originalObject === (a.task ?? a.data[0].task)) ??
|
||||
Object.values(newGraphs)[0].find(a => (a.data[0] as any).seriesName === series) ??
|
||||
this.plotData = series ? Object.values(newGraphs)[0].find(a => (a.data[0] as any).seriesName === series) :
|
||||
Object.values(newGraphs)[0]?.find(a => originalObject === (a.task ?? a.data[0].task)) ??
|
||||
Object.values(newGraphs)[0]?.[0];
|
||||
}
|
||||
|
||||
@@ -297,7 +296,14 @@ export class AppComponent implements OnInit {
|
||||
filter(singleValueData => !!singleValueData),
|
||||
take(1)
|
||||
).subscribe(singleValueData => {
|
||||
this.singleValueData = singleValueData.values;
|
||||
const objects = this.searchParams.getAll('objects');
|
||||
if (objects.length > 1) {
|
||||
this.type = 'scalar';
|
||||
const singleValuesData = createMultiSingleValuesChart(singleValueData);
|
||||
this.plotData = prepareGraph(singleValuesData.data, singleValuesData.layout, {}, {type: 'singleValue'});
|
||||
} else {
|
||||
this.singleValueData = singleValueData[0].values;
|
||||
}
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
}
|
||||
@@ -306,9 +312,11 @@ export class AppComponent implements OnInit {
|
||||
this.activated = true;
|
||||
await this.waitForVisibility();
|
||||
this.singleGraphHeight = window.innerHeight;
|
||||
!['sample', 'single'].includes(this.type) && loadExternalLibrary(this.store, this.environment.plotlyURL, reportsPlotlyReady);
|
||||
const models = this.searchParams.has('models') || this.searchParams.get('objectType') === 'model';
|
||||
const objects = this.searchParams.getAll('objects');
|
||||
if ((!['sample', 'single'].includes(this.type) || objects.length > 1)) {
|
||||
loadExternalLibrary(this.store, this.environment.plotlyURL, reportsPlotlyReady);
|
||||
}
|
||||
const queryParams = {
|
||||
tasks: objects.length > 0 ? objects : this.searchParams.getAll('tasks'),
|
||||
metrics: this.searchParams.getAll('metrics'),
|
||||
@@ -400,7 +408,7 @@ export class AppComponent implements OnInit {
|
||||
const isCompare = entityIds.length > 1;
|
||||
let url = `${window.location.origin.replace('4201', '4200')}/projects/${project ?? '*'}/`;
|
||||
if (isCompare) {
|
||||
url += `${isModels ? 'compare-models;ids=' : 'compare-experiments;ids='}${entityIds.join(',')}/
|
||||
url += `${isModels ? 'compare-models;ids=' : 'compare-experiments;ids='}${entityIds.filter(id => !!id).join(',')}/
|
||||
${this.getComparePath(this.type)}?metricPath=${metricPath}&metricName=lala${variants.map(par => `¶ms=${par}`).join('')}`;
|
||||
} else {
|
||||
url += `${isModels ? 'models/' : 'experiments/'}${entityIds}/${this.getOutputPath(isModels, this.type)}`;
|
||||
@@ -428,6 +436,7 @@ ${this.getComparePath(this.type)}?metricPath=${metricPath}&metricName=lala${vari
|
||||
return 'info-output/debugImages';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private getComparePath(type: WidgetTypes) {
|
||||
|
||||
@@ -145,7 +145,7 @@ export class AppEffects {
|
||||
})
|
||||
),
|
||||
mergeMap((res: { data: ReportsGetTaskDataResponse }) => [
|
||||
setSingleValues({data: res.data.single_value_metrics[0]}),
|
||||
setSingleValues({data: res.data.single_value_metrics}),
|
||||
setTaskData({sourceProject: (res.data.tasks[0]?.project as any).id, sourceTasks: res.data.tasks.map(t => t.id)})
|
||||
]
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ export const appFeatureKey = 'app';
|
||||
export interface State {
|
||||
plotData: MetricsPlotEvent[] | ReportsApiMultiplotsResponse;
|
||||
sampleData: DebugSample;
|
||||
singleValuesData: SingleValueTaskMetrics;
|
||||
singleValuesData: SingleValueTaskMetrics[];
|
||||
parallelCoordinateData: Task[];
|
||||
scaleFactor: number;
|
||||
plotlyReady: boolean;
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
import {BASE_ENV, Environment} from './base';
|
||||
/*
|
||||
1 https://api1.rnd.dev2.allegro.ai (community)
|
||||
2 https://api2.rnd.dev2.allegro.ai (enterprise)
|
||||
3 https://api.qa.hosted.allegro.ai
|
||||
4 https://api.allegro-master.hosted.allegro.ai
|
||||
5 https://api.allegro.ai
|
||||
6 https://api2.qa.hosted.allegro.ai
|
||||
7 https://api1.testing2.dev2.allegro.ai
|
||||
8 https://api2.testing2.dev2.allegro.ai
|
||||
9 https://api.vimeo.hosted.allegro.ai
|
||||
10 https://api.maxq.hosted.allegro.ai
|
||||
11 https://api.community-master.hosted.allegro.ai
|
||||
12 https://api.dev.hosted.allegro.ai
|
||||
13 https://api.staging.hosted.allegro.ai
|
||||
14 https://api.clear.ml
|
||||
15 https://api.dev.hosted.clear.ml
|
||||
16 https://api.deloitte.hosted.allegro.ai
|
||||
17 https://api.trains-master.hosted.allegro.ai
|
||||
*/
|
||||
'https://api.allegro-master.hosted.allegro.ai', // 1
|
||||
'https://api.community-master.hosted.allegro.ai', // 2
|
||||
'https://api.allegro.ai', // 3
|
||||
'https://api.clear.ml', // 4
|
||||
'https://api1.rnd.dev2.allegro.ai', // 5
|
||||
'https://api2.rnd.dev2.allegro.ai', // 6
|
||||
'https://api.dev.hosted.allegro.ai', // 7
|
||||
'https://api.dev.hosted.clear.ml', // 8
|
||||
'https://api.staging.hosted.allegro.ai', // 9
|
||||
'https://api.neuralguard.hosted.allegro.ai', // 10
|
||||
*/
|
||||
|
||||
export const environment = {
|
||||
...BASE_ENV,
|
||||
production: false,
|
||||
baseUrl: 'localhost:4200',
|
||||
autoLogin: false,
|
||||
apiBaseUrl: 'service/4/api',
|
||||
apiBaseUrl: 'service/1/api',
|
||||
// communityServer: true,
|
||||
accountAdministration: true,
|
||||
fileBaseUrl: 'https://files.allegro.ai',
|
||||
|
||||
@@ -128,6 +128,7 @@ $sm-dark-theme: mat.define-dark-theme((
|
||||
@include mat.dialog-theme($theme);
|
||||
@include mat.slider-theme($sm-neon-theme);
|
||||
@include mat.form-field-theme($theme);
|
||||
@include mat.tooltip-theme($theme);
|
||||
|
||||
@include mat.select-theme($sm-dark-theme);
|
||||
|
||||
@@ -142,6 +143,7 @@ $sm-dark-theme: mat.define-dark-theme((
|
||||
html, body {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
--mat-select-trigger-text-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -167,6 +169,13 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mat-mdc-dialog-container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
@@ -289,6 +298,7 @@ sm-debug-image-snippet {
|
||||
.image-var {
|
||||
top: 8px;
|
||||
bottom: unset;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,6 +330,9 @@ sm-debug-image-snippet {
|
||||
.mat-mdc-select-panel {
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
--mdc-typography-body1-font-size: 14px;
|
||||
--mdc-typography-body1-line-height: 16px;
|
||||
--mat-select-trigger-text-size: 14px;
|
||||
|
||||
&.light-theme {
|
||||
--mdc-theme-surface: white;
|
||||
@@ -329,6 +342,8 @@ sm-debug-image-snippet {
|
||||
&.dark, &.black, &.dark-theme {
|
||||
--mdc-theme-surface: #000;
|
||||
border: 1px solid $blue-500;
|
||||
--mat-select-panel-background-color: #000;
|
||||
--mat-option-label-text-color: #{$blue-200};
|
||||
|
||||
.mat-mdc-option {
|
||||
--mdc-theme-text-primary-on-background: #{$blue-200};
|
||||
@@ -337,7 +352,7 @@ sm-debug-image-snippet {
|
||||
|
||||
.mat-mdc-option {
|
||||
--mdc-typography-body1-line-height: 36px;
|
||||
--mdc-typography-body1-font-size: 14px;
|
||||
--mat-menu-item-label-text-size: 14px;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
@@ -372,8 +387,8 @@ sm-debug-image-snippet {
|
||||
|
||||
.dark-theme, .light-theme {
|
||||
.mat-mdc-form-field {
|
||||
--mdc-typography-body1-font-size: 14px;
|
||||
--mdc-typography-body1-line-height: 16px;
|
||||
--mat-menu-item-label-text-line-height: 16;
|
||||
--mat-menu-item-label-text-size: 14px;
|
||||
|
||||
&.mat-form-field-appearance-outline {
|
||||
&.black {
|
||||
@@ -511,3 +526,26 @@ input[type=number] {
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: transparent
|
||||
}
|
||||
|
||||
.mat-mdc-tooltip {
|
||||
&.sm-tooltip {
|
||||
--mdc-plain-tooltip-container-color: #{$purple};
|
||||
|
||||
.mdc-tooltip__surface {
|
||||
max-width: 400px;
|
||||
font-size: 11px;
|
||||
line-height: 1.55;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
&.break-line {
|
||||
.mdc-tooltip__surface {
|
||||
white-space: pre-line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.parameter-tooltip {
|
||||
margin: 6px auto 6px -74px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,27 +7,32 @@ export interface SearchState {
|
||||
searchQuery: {query: string; regExp?: boolean; original?: string};
|
||||
placeholder: string;
|
||||
active: boolean;
|
||||
initiated: boolean;
|
||||
}
|
||||
|
||||
// Todo remove selectedProjectId
|
||||
const searchInitState: SearchState = {
|
||||
isSearching: false,
|
||||
searchQuery: null,
|
||||
placeholder: null,
|
||||
active : false
|
||||
active : false,
|
||||
initiated : false
|
||||
};
|
||||
|
||||
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'})),
|
||||
on(setSearching, (state, action): SearchState => ({...state, isSearching: action.payload})),
|
||||
on(setSearchQuery, (state, action): SearchState => ({...state, searchQuery: action})),
|
||||
on(initSearch, (state, action): SearchState => ({
|
||||
...state,
|
||||
placeholder: action.payload || 'Search',
|
||||
initiated: true
|
||||
})),
|
||||
on(resetSearch, (): SearchState => ({...searchInitState, placeholder: 'Search'})),
|
||||
);
|
||||
|
||||
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 : '');
|
||||
export const selectSearchQuery = createSelector(selectCommonSearch, (state: SearchState) => state ? state.searchQuery : searchInitState.searchQuery);
|
||||
export const selectPlaceholder = createSelector(selectCommonSearch, (state: SearchState) => state ? state.placeholder : '');
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<span class="search-container" [class.open]="isSearching$ | async">
|
||||
<sm-search
|
||||
#search
|
||||
#searchComponent
|
||||
class="search-header"
|
||||
[class.regex-error]="regexError"
|
||||
[value]="(searchQuery$ | async)?.original"
|
||||
@@ -8,7 +8,7 @@
|
||||
[hideIcons]="true"
|
||||
[minimumChars]="minChars"
|
||||
(focusout)="onSearchFocusOut()"
|
||||
(valueChanged)="onSearchValueChanged($event)"
|
||||
(valueChanged)="search($event)"
|
||||
>
|
||||
<i *ngIf="regexError" class="regexp al-icon al-ico-error-circle pointer" [smTooltip]="regexError" [matTooltipPosition]="'below'"></i>
|
||||
<i
|
||||
@@ -17,7 +17,7 @@
|
||||
smClickStopPropagation
|
||||
[smTooltip]="'Regex'" [matTooltipPosition]="'below'"
|
||||
[class.active]="regExp"
|
||||
(click)="toggleRegExp(); search.searchBarInput.nativeElement.focus();"></i>
|
||||
(click)="toggleRegExp(); searchComponent.searchBarInput.nativeElement.focus();"></i>
|
||||
</sm-search>
|
||||
</span>
|
||||
<ng-container *ngIf="searchActive">
|
||||
|
||||
@@ -4,7 +4,7 @@ import {Store} from '@ngrx/store';
|
||||
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 {debounceTime, distinctUntilChanged, filter, tap} from 'rxjs/operators';
|
||||
import {SearchComponent} from '@common/shared/ui-components/inputs/search/search.component';
|
||||
|
||||
@Component({
|
||||
@@ -23,11 +23,40 @@ export class CommonSearchComponent implements OnInit {
|
||||
@ViewChild(SearchComponent) searchElem: SearchComponent;
|
||||
public regExp: boolean = false;
|
||||
private closeTimer: number;
|
||||
private queryString: string;
|
||||
minChars = 3;
|
||||
public regexError: boolean;
|
||||
|
||||
constructor(private store: Store, private router: Router, private route: ActivatedRoute, private cdr: ChangeDetectorRef) {
|
||||
this.router.events
|
||||
.pipe(
|
||||
filter(event => event instanceof NavigationEnd),
|
||||
distinctUntilChanged((pe: NavigationEnd, ce: NavigationEnd) => pe.url?.split('?')[1] === ce.url?.split('?')[1])
|
||||
).subscribe(() => {
|
||||
const query = this.route.snapshot.queryParams?.q ?? '';
|
||||
const qregex = this.route.snapshot.queryParams?.qreg === 'true';
|
||||
this.store.dispatch(setSearchQuery({
|
||||
query: qregex ? query : query.trim(),
|
||||
regExp: qregex,
|
||||
original: query
|
||||
}));
|
||||
if (query) {
|
||||
this.openSearch();
|
||||
} else {
|
||||
if (document.activeElement !== this.searchElem.searchBarInput.nativeElement) {
|
||||
this.store.dispatch(setSearching({payload: false}))
|
||||
}
|
||||
}
|
||||
})
|
||||
if( this.route.snapshot.queryParams.q) {
|
||||
const query = this.route.snapshot.queryParams?.q ?? '';
|
||||
const qregex = this.route.snapshot.queryParams?.qreg ?? false;
|
||||
this.store.dispatch(setSearchQuery({
|
||||
query: qregex ? query : query.trim(),
|
||||
regExp: qregex,
|
||||
original: query
|
||||
}));
|
||||
setTimeout( () => this.openSearch());
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -41,23 +70,23 @@ export class CommonSearchComponent implements OnInit {
|
||||
setTimeout(this.setSearchActive.bind(this));
|
||||
}
|
||||
|
||||
onSearchValueChanged(query: string) {
|
||||
this.queryString = query;
|
||||
this.search();
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
private search() {
|
||||
|
||||
public search(query: string) {
|
||||
try {
|
||||
if (this.regExp) {
|
||||
new RegExp(this.queryString);
|
||||
new RegExp(query);
|
||||
}
|
||||
this.regexError = null;
|
||||
this.store.dispatch(setSearchQuery({
|
||||
query: this.regExp ? this.queryString : this.queryString.trim(),
|
||||
regExp: this.regExp,
|
||||
original: this.queryString
|
||||
}));
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
replaceUrl: true,
|
||||
queryParamsHandling: "merge",
|
||||
queryParams: {
|
||||
q: query || undefined
|
||||
}
|
||||
})
|
||||
|
||||
} catch (e) {
|
||||
this.regexError = e.message?.replace(/:.+:/, ':');
|
||||
}
|
||||
@@ -92,13 +121,17 @@ export class CommonSearchComponent implements OnInit {
|
||||
this.searchElem.clear();
|
||||
this.store.dispatch(setSearching({payload: false}));
|
||||
document.body.focus();
|
||||
setTimeout( () =>this.searchElem.searchBarInput.nativeElement.blur(), 100);
|
||||
}
|
||||
|
||||
toggleRegExp() {
|
||||
this.regExp = !this.regExp;
|
||||
window.clearTimeout(this.closeTimer);
|
||||
if (this.queryString?.length >= this.minChars) {
|
||||
this.search();
|
||||
}
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
replaceUrl: true,
|
||||
queryParamsHandling: "merge",
|
||||
queryParams: {
|
||||
qreg: this.regExp ? this.regExp : undefined}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,9 @@ $sm-neon-theme: mat.define-dark-theme((
|
||||
|
||||
--mdc-typography-body1-letter-spacing: 0;
|
||||
--mdc-typography-button-letter-spacing: 0;
|
||||
.mat-mdc-slide-toggle{
|
||||
--mdc-switch-state-layer-size: 24px;
|
||||
}
|
||||
|
||||
.mat-mdc-checkbox .mdc-checkbox {
|
||||
--mdc-checkbox-selected-icon-color: #{$purple};
|
||||
@@ -101,8 +104,8 @@ $sm-neon-theme: mat.define-dark-theme((
|
||||
--mdc-checkbox-selected-focus-icon-color: #{$purple};
|
||||
}
|
||||
|
||||
.mat-expansion-panel-header-description, .mat-expansion-indicator:after {
|
||||
color: $white;
|
||||
.mat-expansion-panel-header-description, .mat-expansion-indicator {
|
||||
--mat-expansion-header-indicator-color: #{$blue-300};
|
||||
}
|
||||
|
||||
.link {
|
||||
@@ -131,6 +134,9 @@ $sm-neon-theme: mat.define-dark-theme((
|
||||
|
||||
--mdc-typography-body1-letter-spacing: 0;
|
||||
--mdc-typography-button-letter-spacing: 0;
|
||||
--mdc-switch-state-layer-size: 24px;
|
||||
|
||||
--bs-border-width: 1px #{$blue-300};
|
||||
|
||||
mat-progress-bar {
|
||||
border-radius: 4px;
|
||||
@@ -174,7 +180,7 @@ html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: $font-family-base, sans-serif;
|
||||
font-family: $font-family-base;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -279,7 +285,7 @@ hr {
|
||||
//}
|
||||
|
||||
mat-expansion-panel {
|
||||
box-shadow: unset;
|
||||
box-shadow: unset !important;
|
||||
|
||||
.mat-expansion-panel-header {
|
||||
margin-bottom: 0;
|
||||
@@ -388,6 +394,8 @@ button {
|
||||
|
||||
.empty-menu {
|
||||
height: 100px;
|
||||
font-size: 14px;
|
||||
--mat-menu-item-label-text-tracking: 0;
|
||||
}
|
||||
|
||||
.capital-case {
|
||||
@@ -597,9 +605,11 @@ html {
|
||||
&.cdk-keyboard-focused, &.cdk-program-focused {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
&.mat-mdc-focus-indicator {
|
||||
--mdc-list-list-item-hover-label-text-color: rgba(0, 0, 0, 0.87);
|
||||
--mat-menu-item-hover-state-layer-color: #{$blue-50};
|
||||
}
|
||||
|
||||
.mdc-list-item__primary-text {
|
||||
--mdc-list-list-item-label-text-color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
@@ -615,14 +625,13 @@ html {
|
||||
width: calc(100% - 8px);
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
--mdc-typography-body1-line-height: 20px;
|
||||
font-size: 14px;
|
||||
--mdc-typography-body1-font-size: 14px;
|
||||
--mat-menu-item-label-text-line-height: 20px;
|
||||
--mat-menu-item-label-text-size: 14px;
|
||||
padding: 0 32px 0 12px;
|
||||
border-radius: 4px;
|
||||
|
||||
.mdc-list-item__primary-text {
|
||||
--mdc-typography-body1-line-height: 20px;
|
||||
--mat-menu-item-label-text-tracking: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0 12px;
|
||||
@@ -630,10 +639,6 @@ html {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&.mat-mdc-menu-item {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
@@ -859,7 +864,7 @@ button.btn.button-outline-dark {
|
||||
|
||||
.ace_placeholder {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
color: $blue-grey !important;
|
||||
color: $blue-300 !important;
|
||||
}
|
||||
|
||||
.modebar-btn[data-attr="plotly-embedded-modebar-button"] {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {IOption} from '@common/shared/ui-components/inputs/select-autocomplete-with-chips/select-autocomplete-with-chips.component';
|
||||
import {TIME_INTERVALS} from '@common/workers-and-queues/workers-and-queues.consts';
|
||||
|
||||
export type TableSelectionState = 'All' | 'Partial' | 'None';
|
||||
|
||||
export const TIME_FORMAT_STRING = 'MMM d yyyy H:mm';
|
||||
@@ -110,3 +113,13 @@ export const MESSAGES_SEVERITY = {
|
||||
};
|
||||
|
||||
export const rootProjectsPageSize = 50;
|
||||
|
||||
export const timeFrameOptions: IOption[] = [
|
||||
{label: '3 Hours', value: (3 * TIME_INTERVALS.HOUR).toString()},
|
||||
{label: '6 Hours', value: (6 * TIME_INTERVALS.HOUR).toString()},
|
||||
{label: '12 Hours', value: (12 * TIME_INTERVALS.HOUR).toString()},
|
||||
{label: '1 Day', value: (TIME_INTERVALS.DAY).toString()},
|
||||
{label: '1 Week', value: (TIME_INTERVALS.WEEK).toString()},
|
||||
{label: '1 Month', value: (TIME_INTERVALS.MONTH).toString()}
|
||||
];
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ export const resetDontShowAgainForBucketEndpoint = createAction(AUTH_PREFIX + 'R
|
||||
export const resetShowS3Popup = createAction(AUTH_PREFIX + 'RESET_SHOW_S3_POPUP');
|
||||
export const showS3PopUp = createAction(
|
||||
AUTH_PREFIX + 'SHOW_S3_POPUP',
|
||||
props<{credentials: Credentials; credentialsError: string; isAzure: boolean}>()
|
||||
props<{credentials: Credentials; credentialsError: string; provider: 's3' | 'azure' | 'gcs'}>()
|
||||
);
|
||||
export const getTutorialBucketCredentials = createAction(AUTH_PREFIX + 'GET_TUTORIAL_BUCKET_CREDENTIALS');
|
||||
export const showLocalFilePopUp = createAction(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {VIEW_PREFIX} from '~/app.constants';
|
||||
import {createAction, props} from '@ngrx/store';
|
||||
import {Action, createAction, props} from '@ngrx/store';
|
||||
import {omit} from 'lodash-es';
|
||||
import {HttpErrorResponse} from '@angular/common/http';
|
||||
import {Ace} from 'ace-builds';
|
||||
@@ -69,7 +69,7 @@ export const resetAceCaretsPositions = createAction(VIEW_PREFIX + '[reset ace ca
|
||||
|
||||
export const addMessage = createAction(
|
||||
VIEW_PREFIX + '[add message]',
|
||||
(severity: MessageSeverityEnum, msg: string, userActions?: { actions: any[]; name: string }[], suppressNextMessages?: boolean) =>
|
||||
(severity: MessageSeverityEnum, msg: string, userActions?: { actions: Action[]; name: string }[], suppressNextMessages?: boolean) =>
|
||||
({severity, msg, userActions, suppressNextMessages})
|
||||
);
|
||||
|
||||
@@ -95,8 +95,6 @@ export const setRedactedArguments = createAction(VIEW_PREFIX + 'SET_REDACTED_ARG
|
||||
export const setHideRedactedArguments = createAction(VIEW_PREFIX + 'SET_SHOW_REDACTED_ARGUMENTS', props<{ hide: boolean }>());
|
||||
export const plotlyReady = createAction(VIEW_PREFIX + '[plotly ready]');
|
||||
export const aceReady = createAction(VIEW_PREFIX + '[ace ready]');
|
||||
export const openAppsAwarenessDialog = createAction(VIEW_PREFIX + '[apps awareness dialog]',
|
||||
props<{ appsYouTubeIntroVideoId }>()
|
||||
);
|
||||
export const openAppsAwarenessDialog = createAction(VIEW_PREFIX + '[apps awareness dialog]' );
|
||||
|
||||
|
||||
|
||||
@@ -116,6 +116,11 @@ export const setCompanyTags = createAction(
|
||||
props<{ tags: string[]; systemTags: string[] }>()
|
||||
);
|
||||
|
||||
export const addCompanyTag = createAction(
|
||||
PROJECTS_PREFIX + '[add company tag]',
|
||||
props<{tag: string}>()
|
||||
);
|
||||
|
||||
export const setMainPageTagsFilter = createAction(
|
||||
PROJECTS_PREFIX + '[set main page tags filters]',
|
||||
props<{ tags?: string[]; feature: string}>()
|
||||
@@ -222,7 +227,7 @@ export const resetTablesFilterProjectsOptions = createAction(
|
||||
|
||||
export const getTablesFilterProjectsOptions = createAction(
|
||||
PROJECTS_PREFIX + ' [get tables filter projects options]',
|
||||
props<{ searchString: string; loadMore: boolean}>()
|
||||
props<{ searchString: string; loadMore: boolean; allowPublic?: boolean}>()
|
||||
);
|
||||
|
||||
export const setTablesFilterProjectsOptions = createAction(
|
||||
|
||||
@@ -31,6 +31,7 @@ export const setURLParams = createAction(
|
||||
isDeep?: boolean;
|
||||
update?: boolean;
|
||||
version?: string;
|
||||
others?: {[key: string]: string};
|
||||
}>()
|
||||
);
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {Actions, concatLatestFrom, createEffect, ofType} from '@ngrx/effects';
|
||||
import {ApiAuthService} from '~/business-logic/api-services/auth.service';
|
||||
import * as authActions from '../actions/common-auth.actions';
|
||||
import {requestFailed} from '../actions/http.actions';
|
||||
import {activeLoader, deactivateLoader, setServerError} from '../actions/layout.actions';
|
||||
import {catchError, filter, finalize, map, mergeMap, switchMap, throttleTime, withLatestFrom} from 'rxjs/operators';
|
||||
import {AuthGetCredentialsResponse} from '~/business-logic/model/auth/authGetCredentialsResponse';
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectCurrentUser} from '../reducers/users-reducer';
|
||||
import {GetCurrentUserResponseUserObject} from '~/business-logic/model/users/getCurrentUserResponseUserObject';
|
||||
import {AdminService} from '~/shared/services/admin.service';
|
||||
import {selectDontShowAgainForBucketEndpoint, selectS3BucketCredentialsBucketCredentials, selectSignedUrl} from '@common/core/reducers/common-auth-reducer';
|
||||
import {EMPTY, of} from 'rxjs';
|
||||
import {S3AccessResolverComponent} from '@common/layout/s3-access-resolver/s3-access-resolver.component';
|
||||
import {
|
||||
S3AccessDialogData,
|
||||
S3AccessResolverComponent
|
||||
} from '@common/layout/s3-access-resolver/s3-access-resolver.component';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {setCredentialLabel} from '../actions/common-auth.actions';
|
||||
import {SignResponse} from '@common/settings/admin/base-admin-utils';
|
||||
@@ -38,7 +41,7 @@ export class CommonAuthEffects {
|
||||
getAllCredentialsEffect = createEffect(() => this.actions.pipe(
|
||||
ofType(authActions.getAllCredentials),
|
||||
switchMap(action => this.credentialsApi.authGetCredentials({}).pipe(
|
||||
withLatestFrom(this.store.select(selectCurrentUser)),
|
||||
concatLatestFrom(() => this.store.select(selectCurrentUser)),
|
||||
mergeMap(([res, user]: [AuthGetCredentialsResponse, GetCurrentUserResponseUserObject]) => [
|
||||
authActions.updateAllCredentials({credentials: res.credentials, extra: res?.['additional_credentials'], workspace: user.company.id}),
|
||||
deactivateLoader(action.type)
|
||||
@@ -103,7 +106,7 @@ export class CommonAuthEffects {
|
||||
filter(action => !!action.url),
|
||||
mergeMap(action =>
|
||||
of(action).pipe(
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() =>
|
||||
this.store.select(state => selectSignedUrl(action.url)(state))
|
||||
),
|
||||
switchMap(([, signedUrl]) =>
|
||||
@@ -115,7 +118,7 @@ export class CommonAuthEffects {
|
||||
switch (res.type) {
|
||||
case 'popup':
|
||||
this.signAfterPopup.push(action);
|
||||
return [authActions.showS3PopUp({credentials: res.bucket, isAzure: res.azure, credentialsError: null})];
|
||||
return [authActions.showS3PopUp({credentials: res.bucket, provider: res.provider, credentialsError: null})];
|
||||
case 'sign':
|
||||
return [authActions.setSignedUrl({url: action.url, signed: res.signed, expires: res.expires})];
|
||||
default:
|
||||
@@ -129,9 +132,7 @@ export class CommonAuthEffects {
|
||||
|
||||
s3popup = createEffect(() => this.actions.pipe(
|
||||
ofType(authActions.showS3PopUp),
|
||||
withLatestFrom(
|
||||
this.store.select(selectDontShowAgainForBucketEndpoint)
|
||||
),
|
||||
concatLatestFrom(() => this.store.select(selectDontShowAgainForBucketEndpoint)),
|
||||
throttleTime(500),
|
||||
filter(([action, dontShowAgain]) =>
|
||||
action?.credentials?.Bucket + action?.credentials?.Endpoint !== dontShowAgain &&
|
||||
@@ -141,10 +142,8 @@ export class CommonAuthEffects {
|
||||
if (action?.credentials?.Bucket) {
|
||||
this.openPopup[action.credentials.Bucket] = true;
|
||||
}
|
||||
return this.matDialog.open(S3AccessResolverComponent, {data: action, maxWidth: 700}).afterClosed().pipe(
|
||||
withLatestFrom(
|
||||
this.store.pipe(select(selectS3BucketCredentialsBucketCredentials)),
|
||||
),
|
||||
return this.matDialog.open(S3AccessResolverComponent, {data: action as S3AccessDialogData, maxWidth: 700}).afterClosed().pipe(
|
||||
concatLatestFrom(() => this.store.select(selectS3BucketCredentialsBucketCredentials)),
|
||||
switchMap(([data, bucketCredentials]) => {
|
||||
window.setTimeout(() => this.signAfterPopup = []);
|
||||
if (data) {
|
||||
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
OperationErrorDialogComponent
|
||||
} from '@common/shared/ui-components/overlay/operation-error-dialog/operation-error-dialog.component';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
import {createMetricColumn} from '@common/shared/utils/tableParamEncode';
|
||||
import {createMetricColumn, excludedKey} 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';
|
||||
@@ -102,6 +102,7 @@ export class ProjectsEffects {
|
||||
switchMap(([action, showHidden, scrollId, filters]) => forkJoin([
|
||||
this.projectsApi.projectsGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
allow_public: action.allowPublic,
|
||||
page_size: rootProjectsPageSize,
|
||||
size: rootProjectsPageSize,
|
||||
order_by: ['name'],
|
||||
@@ -190,7 +191,10 @@ export class ProjectsEffects {
|
||||
|
||||
resetProjectSelections$ = createEffect(() => this.actions$.pipe(
|
||||
ofType(actions.resetProjectSelection),
|
||||
mergeMap(() => [setSelectedExperiments({experiments: []}), setSelectedModels({models: []})])
|
||||
mergeMap(() => [
|
||||
setSelectedExperiments({experiments: []}),
|
||||
setSelectedModels({models: []})
|
||||
])
|
||||
));
|
||||
|
||||
updateProject$ = createEffect(() => this.actions$.pipe(
|
||||
@@ -307,7 +311,7 @@ export class ProjectsEffects {
|
||||
started: ['2000-01-01T00:00:00', null],
|
||||
status: ['-draft'],
|
||||
order_by: ['-started'],
|
||||
type: ['__$not', 'annotation_manual', '__$not', 'annotation', '__$not', 'dataset_import'],
|
||||
type: [excludedKey, 'annotation_manual', excludedKey, 'annotation', excludedKey, 'dataset_import'],
|
||||
system_tags: ['-archived'],
|
||||
scroll_id: null,
|
||||
size: 1000
|
||||
|
||||
@@ -41,7 +41,8 @@ export class RouterEffects {
|
||||
...(action.filters && {filter: encodeFilters(action.filters)}),
|
||||
...(action.isArchived !== undefined && {archive: action.isArchived ? 'true' : null}),
|
||||
...(action.isDeep && {deep: true}),
|
||||
...(action.version && {version: action.version})
|
||||
...(action.version && {version: action.version}),
|
||||
...(action.others && action.others)
|
||||
}
|
||||
} as NavigationExtras;
|
||||
this.router.navigate([], extra);
|
||||
|
||||
@@ -77,11 +77,7 @@ ${this.errorService.getErrorMsg(err?.error)}`)])
|
||||
updateCurrentUser = createEffect(() => this.actions.pipe(
|
||||
ofType(updateCurrentUser),
|
||||
mergeMap(({user}) => this.userService.usersUpdate({...user}).pipe(
|
||||
mergeMap((res: UsersUpdateResponse) => {
|
||||
if (res.updated) {
|
||||
return [setCurrentUserName({name: user.name})];
|
||||
}
|
||||
})
|
||||
mergeMap((res: UsersUpdateResponse) => res.updated ? [setCurrentUserName({name: user.name})] : [])
|
||||
)),
|
||||
catchError(err => [addMessage(MESSAGES_SEVERITY.ERROR, `Update User Failed ${this.errorService.getErrorMsg(err?.error)}`)])
|
||||
));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {inject, Injectable} from '@angular/core';
|
||||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
@@ -12,8 +12,12 @@ import {setCurrentUser} from '~/core/actions/users.action';
|
||||
@Injectable()
|
||||
export class WebappInterceptor implements HttpInterceptor {
|
||||
protected user: GetCurrentUserResponseUserObject;
|
||||
protected router: Router;
|
||||
protected store: Store;
|
||||
|
||||
constructor(protected router: Router, protected store: Store) {
|
||||
constructor() {
|
||||
this.router = inject(Router);
|
||||
this.store = inject(Store);
|
||||
this.store.select(selectCurrentUser).subscribe(user => this.user = user);
|
||||
}
|
||||
|
||||
@@ -47,9 +51,9 @@ export class WebappInterceptor implements HttpInterceptor {
|
||||
this.store.dispatch(setCurrentUser({user: null, terms_of_use: null}));
|
||||
this.router.navigate(['login'], {queryParams: {redirect: redirectUrl}, replaceUrl: true});
|
||||
}
|
||||
return throwError(err);
|
||||
return throwError(() => err);
|
||||
} else {
|
||||
return throwError(err);
|
||||
return throwError(() => err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,5 +149,9 @@ export const commonAuthReducer = [
|
||||
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}})),
|
||||
on(removeSignedUrl, (state, action) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const {[action.url]: remove, ...rest} = state.signedUrls;
|
||||
return {...state, signedUrls: rest};
|
||||
}),
|
||||
] as ReducerTypes<AuthState, any>[];
|
||||
|
||||
@@ -178,6 +178,7 @@ export const projectsReducer = createReducer(
|
||||
companyTags: action.tags,
|
||||
systemTags: action.systemTags
|
||||
})),
|
||||
on(projectsActions.addCompanyTag, (state, action): RootProjects => ({...state, companyTags: Array.from(new Set(state.companyTags.concat(action.tag))).sort()})),
|
||||
on(projectsActions.addProjectTags, (state, action): RootProjects => ({
|
||||
...state,
|
||||
projectTags: Array.from(new Set(state.projectTags.concat(action.tags))).sort()
|
||||
|
||||
@@ -39,16 +39,24 @@ export const initUsers: UsersState = {
|
||||
};
|
||||
|
||||
export const users = state => state.users as UsersState;
|
||||
export const selectSettings = createSelector(users, (state): any => state?.settings);
|
||||
export const selectSettings = createSelector(users, (state) => state?.settings);
|
||||
export const selectMaxDownloadItems = createSelector(selectSettings, (state): number => state?.max_download_items ?? 1000);
|
||||
export const selectCurrentUser = createSelector(users, state => state.currentUser);
|
||||
export const selectActiveWorkspace = createSelector(users, state => state.activeWorkspace);
|
||||
export const selectActiveWorkspaceTier = createSelector(selectActiveWorkspace, workspace => workspace?.tier);
|
||||
export const selectUserWorkspaces = createSelector(users, state => state.userWorkspaces);
|
||||
export const selectSelectedWorkspaceTab = createSelector(users, state => state.selectedWorkspaceTab);
|
||||
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 selectWorkspaceOwner = createSelector(selectActiveWorkspace, selectUserWorkspaces, (active, workspaces) => {
|
||||
if (workspaces && active) {
|
||||
const activeWs = workspaces.find(ws => ws.id === active.id);
|
||||
return activeWs?.owners?.[0]?.name || '';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
export const usersReducerFunctions = [
|
||||
on(fetchCurrentUser, state => ({...state})),
|
||||
|
||||
@@ -15,7 +15,7 @@ export const searchSetTerm = createAction(
|
||||
|
||||
export const searchStart = createAction(
|
||||
SEARCH_PREFIX + 'SEARCH_START',
|
||||
props<{ query: string; regExp?: boolean; force?: boolean; activeLink: ActiveSearchLink }>()
|
||||
props<{ query: string; regExp?: boolean }>()
|
||||
);
|
||||
|
||||
export const searchClear = createAction(SEARCH_PREFIX + 'SEARCH_CLEAR');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {Actions, concatLatestFrom, createEffect, ofType} from '@ngrx/effects';
|
||||
import {activeLoader, deactivateLoader} from '../core/actions/layout.actions';
|
||||
import {
|
||||
getCurrentPageResults,
|
||||
@@ -26,7 +26,7 @@ import {selectActiveSearch, selectSearchScrollIds, selectSearchTerm} from './das
|
||||
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 {catchError, mergeMap, map, switchMap} from 'rxjs/operators';
|
||||
import {isEqual} from 'lodash-es';
|
||||
import {activeSearchLink} from '~/features/dashboard-search/dashboard-search.consts';
|
||||
import {emptyAction} from '~/app.constants';
|
||||
@@ -35,6 +35,7 @@ import {selectCurrentUser, selectShowOnlyUserWork} from '@common/core/reducers/u
|
||||
import {selectHideExamples, selectShowHidden} from '@common/core/reducers/projects.reducer';
|
||||
import {ApiReportsService} from '~/business-logic/api-services/reports.service';
|
||||
import {Report} from '~/business-logic/model/reports/report';
|
||||
import {excludedKey} from '@common/shared/utils/tableParamEncode';
|
||||
|
||||
export const getEntityStatQuery = (action, searchHidden) => ({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
@@ -52,7 +53,7 @@ export const getEntityStatQuery = (action, searchHidden) => ({
|
||||
fields: ['name', 'id']
|
||||
},
|
||||
search_hidden: searchHidden,
|
||||
type: ['__$not', 'annotation_manual', '__$not', 'annotation', '__$not', 'dataset_import'],
|
||||
type: [excludedKey, 'annotation_manual', excludedKey, 'annotation', excludedKey, 'dataset_import'],
|
||||
system_tags: ['-archived', '-pipeline', '-dataset'],
|
||||
},
|
||||
models: {
|
||||
@@ -92,6 +93,9 @@ export const getEntityStatQuery = (action, searchHidden) => ({
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
});
|
||||
|
||||
export const orderBy = ['-last_update'];
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class DashboardSearchEffects {
|
||||
constructor(
|
||||
@@ -100,7 +104,7 @@ export class DashboardSearchEffects {
|
||||
public modelsApi: ApiModelsService,
|
||||
public experimentsApi: ApiTasksService,
|
||||
public reportsApi: ApiReportsService,
|
||||
private store: Store<any>
|
||||
private store: Store
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -111,29 +115,26 @@ export class DashboardSearchEffects {
|
||||
|
||||
startSearch = createEffect(() => this.actions.pipe(
|
||||
ofType(searchStart),
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectActiveSearch),
|
||||
this.store.select(selectSearchTerm)
|
||||
),
|
||||
]),
|
||||
mergeMap(([action, active, term]) => {
|
||||
const actionsToFire = [];
|
||||
if (!active) {
|
||||
actionsToFire.push(searchClear());
|
||||
actionsToFire.push(searchActivate());
|
||||
}
|
||||
if (!isEqual(term, action)) {
|
||||
actionsToFire.push(getResultsCount(action));
|
||||
actionsToFire.push(searchSetTerm(action));
|
||||
}
|
||||
actionsToFire.push(getResultsCount(term));
|
||||
return actionsToFire;
|
||||
})
|
||||
));
|
||||
|
||||
getCurrentPageResults = createEffect(() => this.actions.pipe(
|
||||
ofType(getCurrentPageResults),
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectSearchTerm)
|
||||
),
|
||||
]),
|
||||
map(([action, term]) => {
|
||||
switch (action.activeLink) {
|
||||
case activeSearchLink.experiments:
|
||||
@@ -156,13 +157,13 @@ export class DashboardSearchEffects {
|
||||
|
||||
searchProjects = createEffect(() => this.actions.pipe(
|
||||
ofType(searchProjects),
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectSearchScrollIds),
|
||||
this.store.select(selectShowOnlyUserWork),
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectHideExamples),
|
||||
this.store.select(selectShowHidden),
|
||||
),
|
||||
]),
|
||||
switchMap(([action, scrollIds, userFocus, user, hideExamples, showHidden]) => this.projectsApi.projectsGetAllEx({
|
||||
_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
@@ -178,7 +179,8 @@ export class DashboardSearchEffects {
|
||||
...(userFocus && {active_users: [user.id]}),
|
||||
...(hideExamples && {allow_public: false}),
|
||||
include_stats: true,
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'basename']
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'basename'],
|
||||
order_by: orderBy,
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [setProjectsResults({
|
||||
@@ -190,12 +192,12 @@ export class DashboardSearchEffects {
|
||||
|
||||
searchPipelines = createEffect(() => this.actions.pipe(
|
||||
ofType(searchPipelines),
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectSearchScrollIds),
|
||||
this.store.select(selectShowOnlyUserWork),
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectHideExamples),
|
||||
),
|
||||
]),
|
||||
switchMap(([action, scrollIds, userFocus, user, hideExamples]) => this.projectsApi.projectsGetAllEx({
|
||||
_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
@@ -211,7 +213,8 @@ export class DashboardSearchEffects {
|
||||
size: SEARCH_PAGE_SIZE,
|
||||
...(userFocus && {active_users: [user.id]}),
|
||||
include_stats: true,
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'tags', 'system_tags', 'basename']
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'tags', 'system_tags', 'basename'],
|
||||
order_by: orderBy,
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [setPipelinesResults({
|
||||
@@ -223,12 +226,12 @@ export class DashboardSearchEffects {
|
||||
|
||||
searchOpenDatasets = createEffect(() => this.actions.pipe(
|
||||
ofType(searchOpenDatasets),
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectSearchScrollIds),
|
||||
this.store.select(selectShowOnlyUserWork),
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectHideExamples),
|
||||
),
|
||||
]),
|
||||
switchMap(([action, scrollIds, userFocus, user, hideExamples]) => this.projectsApi.projectsGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
_any_: {
|
||||
@@ -247,7 +250,8 @@ export class DashboardSearchEffects {
|
||||
include_dataset_stats: true,
|
||||
stats_with_children: false,
|
||||
include_stats: true,
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'tags', 'system_tags', 'basename']
|
||||
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'tags', 'system_tags', 'basename'],
|
||||
order_by: orderBy,
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [setOpenDatasetsResults({
|
||||
@@ -260,12 +264,12 @@ export class DashboardSearchEffects {
|
||||
|
||||
searchModels = createEffect(() => this.actions.pipe(
|
||||
ofType(searchModels),
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectSearchScrollIds),
|
||||
this.store.select(selectShowOnlyUserWork),
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectHideExamples),
|
||||
),
|
||||
]),
|
||||
switchMap(([action, scrollIds, userFocus, user, hideExamples]) => this.modelsApi.modelsGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
_any_: {
|
||||
@@ -278,7 +282,8 @@ export class DashboardSearchEffects {
|
||||
...(hideExamples && {allow_public: false}),
|
||||
system_tags: ['-archived'],
|
||||
include_stats: true,
|
||||
only_fields: ['ready', 'created', 'framework', 'user.name', 'name', 'parent.name', 'task.name', 'id', 'company']
|
||||
only_fields: ['ready', 'created', 'framework', 'user.name', 'name', 'parent.name', 'task.name', 'id', 'company'],
|
||||
order_by: orderBy,
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [setModelsResults({models: res.models, scrollId: res.scroll_id}), deactivateLoader(action.type)]),
|
||||
@@ -287,13 +292,13 @@ export class DashboardSearchEffects {
|
||||
|
||||
searchExperiments = createEffect(() => this.actions.pipe(
|
||||
ofType(searchExperiments),
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectSearchScrollIds),
|
||||
this.store.select(selectShowOnlyUserWork),
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectHideExamples),
|
||||
this.store.select(selectShowHidden),
|
||||
),
|
||||
]),
|
||||
switchMap(([action, scrollIds, userFocus, user, hideExamples, showHidden]) => this.experimentsApi.tasksGetAllEx({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
_any_: {
|
||||
@@ -305,7 +310,8 @@ export class DashboardSearchEffects {
|
||||
...(userFocus && {user: [user.id]}),
|
||||
...(hideExamples && {allow_public: false}),
|
||||
only_fields: EXPERIMENT_SEARCH_ONLY_FIELDS,
|
||||
type: ['__$not', 'annotation_manual', '__$not', 'annotation', '__$not', 'dataset_import'],
|
||||
order_by: orderBy,
|
||||
type: [excludedKey, 'annotation_manual', excludedKey, 'annotation', excludedKey, 'dataset_import'],
|
||||
system_tags: ['-archived', '-pipeline', '-dataset'],
|
||||
search_hidden: showHidden,
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
@@ -319,12 +325,12 @@ export class DashboardSearchEffects {
|
||||
|
||||
searchReports = createEffect(() => this.actions.pipe(
|
||||
ofType(searchReports),
|
||||
withLatestFrom(
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectSearchScrollIds),
|
||||
this.store.select(selectShowOnlyUserWork),
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectHideExamples),
|
||||
),
|
||||
]),
|
||||
switchMap(([action, scrollIds, userFocus, user, hideExamples]) => this.reportsApi.reportsGetAllEx({
|
||||
_any_: {
|
||||
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
|
||||
@@ -337,6 +343,7 @@ export class DashboardSearchEffects {
|
||||
...(userFocus && {user: [user.id]}),
|
||||
system_tags: ['-archived'],
|
||||
only_fields: ['name', 'comment', 'company', 'tags', 'report', 'project.name', 'user.name', 'status', 'last_update', 'system_tags'] as (keyof Report)[],
|
||||
order_by: orderBy,
|
||||
}).pipe(
|
||||
mergeMap(res => [setReportsResults({
|
||||
reports: res.tasks,
|
||||
|
||||
@@ -18,6 +18,7 @@ import {Store} from '@ngrx/store';
|
||||
import {ErrorService} from '../shared/services/error.service';
|
||||
import {selectCurrentUser, selectShowOnlyUserWork} from '../core/reducers/users-reducer';
|
||||
import {selectHideExamples, selectShowHidden} from '@common/core/reducers/projects.reducer';
|
||||
import {excludedKey} from '@common/shared/utils/tableParamEncode';
|
||||
|
||||
@Injectable()
|
||||
export class CommonDashboardEffects {
|
||||
@@ -72,7 +73,7 @@ export class CommonDashboardEffects {
|
||||
page_size: 5,
|
||||
order_by: ['-last_update'],
|
||||
status: ['published', 'closed', 'failed', 'stopped', 'in_progress', 'completed'],
|
||||
type: ['__$not', 'annotation_manual', '__$not', 'annotation', '__$not', 'dataset_import'],
|
||||
type: [excludedKey, 'annotation_manual', excludedKey, 'annotation', excludedKey, 'dataset_import'],
|
||||
only_fields: ['type', 'status', 'created', 'name', 'id', 'last_update', 'started', 'project.name'],
|
||||
system_tags: ['-archived', '-pipeline'],
|
||||
user: showOnlyUserWork ? [user.id] : null,
|
||||
|
||||
@@ -5,7 +5,7 @@ import {User} from '~/business-logic/model/users/user';
|
||||
import {setRecentExperiments, setRecentProjects} from './common-dashboard.actions';
|
||||
|
||||
export interface IRecentTask {
|
||||
id?: Task['id'];
|
||||
id: Task['id'];
|
||||
name?: Task['name'];
|
||||
user?: User;
|
||||
type?: Task['type'];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {initSearch, resetSearch} from '../common-search/common-search.actions';
|
||||
import {filter, skip} from 'rxjs/operators';
|
||||
import {distinctUntilChanged, distinctUntilKeyChanged, filter} from 'rxjs/operators';
|
||||
import {Model} from '~/business-logic/model/models/model';
|
||||
import {clearSearchResults, getCurrentPageResults, searchClear, searchDeactivate, searchStart} from '../dashboard-search/dashboard-search.actions';
|
||||
import {clearSearchResults, getCurrentPageResults, searchClear, searchDeactivate, searchSetTerm, searchStart} from '../dashboard-search/dashboard-search.actions';
|
||||
import {IRecentTask} from './common-dashboard.reducer';
|
||||
import {ITask} from '~/business-logic/model/al-task';
|
||||
import {combineLatest, Observable, Subscription} from 'rxjs';
|
||||
@@ -23,10 +23,11 @@ import {Project} from '~/business-logic/model/projects/project';
|
||||
import {setSelectedProjectId} from '../core/actions/projects.actions';
|
||||
import {isExample} from '../shared/utils/shared-utils';
|
||||
import {activeLinksList, ActiveSearchLink, activeSearchLink} from '~/features/dashboard-search/dashboard-search.consts';
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {ChangeDetectorRef, Component, inject, OnDestroy, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import { selectShowOnlyUserWork } from '@common/core/reducers/users-reducer';
|
||||
import {IReport} from '@common/reports/reports.consts';
|
||||
import {isEqual} from "lodash-es";
|
||||
|
||||
@Component({
|
||||
selector: 'sm-dashboard-search-base',
|
||||
@@ -36,7 +37,7 @@ import {IReport} from '@common/reports/reports.consts';
|
||||
(experimentSelected)="taskSelected($event)"
|
||||
(modelSelected)="modelSelected($event)"
|
||||
(pipelineSelected)="pipelineSelected($event)"
|
||||
(activeLinkChanged)="activeLinkChanged($event)"
|
||||
(activeLinkChanged)="changeActiveLink($event)"
|
||||
(reportSelected)="reportSelected($event)"
|
||||
(openDatasetSelected)="openDatasetCardClicked($event)"
|
||||
(loadMoreClicked)="loadMore()"
|
||||
@@ -53,7 +54,6 @@ import {IReport} from '@common/reports/reports.consts';
|
||||
export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
|
||||
public activeLink = 'projects' as ActiveSearchLink;
|
||||
private searchSubs;
|
||||
private allResultsSubscription: Subscription;
|
||||
public searchQuery$: Observable<SearchState['searchQuery']>;
|
||||
public activeSearch$: Observable<boolean>;
|
||||
public modelsResults$: Observable<Array<Model>>;
|
||||
@@ -65,32 +65,54 @@ export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
|
||||
private scrollIds: Map<ActiveSearchLink, string>;
|
||||
public resultsCount$: Observable<Map<ActiveSearchLink, number>>;
|
||||
public reportsResults$: Observable<Array<IReport>>;
|
||||
|
||||
constructor(public store: Store, public router: Router){
|
||||
this.searchQuery$ = store.select(selectSearchQuery);
|
||||
this.activeSearch$ = store.select(selectActiveSearch);
|
||||
this.modelsResults$ = store.select(selectModelsResults);
|
||||
this.reportsResults$ = store.select(selectReportsResults);
|
||||
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);
|
||||
public store = inject(Store);
|
||||
public router = inject(Router);
|
||||
public route = inject(ActivatedRoute);
|
||||
private subs = new Subscription();
|
||||
private cdr: ChangeDetectorRef;
|
||||
constructor() {
|
||||
this.cdr = inject(ChangeDetectorRef);
|
||||
this.searchQuery$ = this.store.select(selectSearchQuery);
|
||||
this.activeSearch$ = this.store.select(selectActiveSearch);
|
||||
this.modelsResults$ = this.store.select(selectModelsResults);
|
||||
this.reportsResults$ = this.store.select(selectReportsResults);
|
||||
this.pipelinesResults$ = this.store.select(selectPipelinesResults);
|
||||
this.datasetsResults$ = this.store.select(selectDatasetsResults);
|
||||
this.projectsResults$ = this.store.select(selectProjectsResults);
|
||||
this.experimentsResults$ = this.store.select(selectExperimentsResults);
|
||||
this.searchTerm$ = this.store.select(selectSearchTerm);
|
||||
this.resultsCount$ = this.store.select(selectResultsCount);
|
||||
this.syncAppSearch();
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.allResultsSubscription = this.resultsCount$.pipe(
|
||||
this.subs.add(this.resultsCount$.pipe(
|
||||
filter(resultsCount => !!resultsCount),
|
||||
).subscribe((resultsCount) => this.setFirstActiveLink(resultsCount));
|
||||
distinctUntilChanged()
|
||||
).subscribe((resultsCount) => this.setFirstActiveLink(resultsCount)));
|
||||
|
||||
this.subs.add(this.route.queryParams.pipe(distinctUntilKeyChanged('tab'))
|
||||
.subscribe(params => {
|
||||
if (params.tab && activeLinksList.find( link => link.name === params.tab)) {
|
||||
this.activeLink = params.tab;
|
||||
this.activeLinkChanged(this.activeLink)
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
}));
|
||||
|
||||
this.subs.add(this.searchQuery$.pipe(
|
||||
distinctUntilChanged((previous, current) => isEqual(previous, current)))
|
||||
.subscribe(query => {
|
||||
this.store.dispatch(searchSetTerm(query));
|
||||
}));
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.store.dispatch(searchClear());
|
||||
this.searchTermChanged('');
|
||||
this.stopSyncSearch();
|
||||
this.allResultsSubscription.unsubscribe();
|
||||
this.subs.unsubscribe();
|
||||
this.searchSubs.unsubscribe();
|
||||
}
|
||||
|
||||
stopSyncSearch() {
|
||||
@@ -102,10 +124,9 @@ export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
|
||||
this.store.dispatch(initSearch({payload: 'Search for all'}));
|
||||
|
||||
this.searchSubs = combineLatest([
|
||||
this.searchQuery$,
|
||||
this.searchTerm$,
|
||||
this.store.select(selectShowOnlyUserWork),
|
||||
])
|
||||
.pipe(skip(1))
|
||||
.subscribe(([query]) => this.searchTermChanged(query?.query, query?.regExp));
|
||||
|
||||
this.searchSubs.add(this.store.select(selectSearchScrollIds).subscribe(scrollIds => this.scrollIds = scrollIds));
|
||||
@@ -119,7 +140,7 @@ export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
|
||||
|
||||
public searchTermChanged(term: string, regExp?: boolean) {
|
||||
if (term && term.length > 0) {
|
||||
this.store.dispatch(searchStart({query:term, regExp, force: term.length < 3, activeLink: this.activeLink}));
|
||||
this.store.dispatch(searchStart({query:term, regExp}));
|
||||
} else {
|
||||
this.activeLink = activeSearchLink.projects;
|
||||
this.store.dispatch(searchDeactivate());
|
||||
@@ -153,7 +174,6 @@ export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
|
||||
|
||||
|
||||
public activeLinkChanged(activeLink) {
|
||||
this.activeLink = activeLink;
|
||||
if (!this.scrollIds?.[activeLink]) {
|
||||
this.store.dispatch(getCurrentPageResults({activeLink}));
|
||||
}
|
||||
@@ -162,11 +182,13 @@ export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
|
||||
|
||||
setFirstActiveLink(resultsCount) {
|
||||
if (resultsCount[this.activeLink] > 0) {
|
||||
this.activeLinkChanged(this.activeLink);
|
||||
this.router.navigate([], {queryParams: {tab: this.activeLink}, queryParamsHandling: "merge", replaceUrl: true})
|
||||
this.store.dispatch(getCurrentPageResults({activeLink: this.activeLink}));
|
||||
} else {
|
||||
const firstTabIndex = activeLinksList.findIndex(activeLink => resultsCount[activeLink.name] > 0);
|
||||
if (firstTabIndex > -1) {
|
||||
this.activeLinkChanged(activeLinksList[firstTabIndex].name);
|
||||
this.router.navigate([], {queryParams: {tab: activeLinksList[firstTabIndex].name}, queryParamsHandling: "merge"})
|
||||
this.store.dispatch(getCurrentPageResults({activeLink: activeLinksList[firstTabIndex].name as ActiveSearchLink}));
|
||||
} else {
|
||||
this.store.dispatch(clearSearchResults());
|
||||
}
|
||||
@@ -176,4 +198,8 @@ export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
|
||||
loadMore() {
|
||||
this.store.dispatch(getCurrentPageResults({activeLink: this.activeLink}));
|
||||
}
|
||||
|
||||
changeActiveLink(tab: string) {
|
||||
this.router.navigate([], {queryParams: {tab}, queryParamsHandling: "merge"})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "src/app/webapp-common/shared/ui-components/styles/variables";
|
||||
@import "variables";
|
||||
|
||||
:host {
|
||||
flex: 0 0 220px;
|
||||
|
||||
@@ -1,14 +1,43 @@
|
||||
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';
|
||||
import {ChangeDetectorRef, Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {DagModelItem} from '@ngneat/dag';
|
||||
import {StepStatusEnum} from '@common/pipelines-controller/pipeline-controller-info/pipeline-controller-info.component';
|
||||
import {fileSizeConfigStorage} from '@common/shared/pipes/filesize.pipe';
|
||||
|
||||
export interface TreeVersion {
|
||||
name: string;
|
||||
version: string;
|
||||
job_id: string;
|
||||
status: StepStatusEnum,
|
||||
last_update: number,
|
||||
parents: string[],
|
||||
job_size: number
|
||||
}
|
||||
|
||||
export interface VersionItem extends DagModelItem {
|
||||
name: string;
|
||||
id: string;
|
||||
data: TreeVersion;
|
||||
}
|
||||
|
||||
@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;
|
||||
export class DatasetVersionStepComponent {
|
||||
protected _step: VersionItem;
|
||||
protected readonly fileSizeConfigStorage = fileSizeConfigStorage;
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) { }
|
||||
|
||||
@Input() set step(step: VersionItem) {
|
||||
this._step = {...step, data: {...step.data, status: step.data?.status || StepStatusEnum.pending}};
|
||||
}
|
||||
|
||||
get step() {
|
||||
return this._step;
|
||||
}
|
||||
@Input() selected: boolean;
|
||||
@Output() openConsole = new EventEmitter();
|
||||
|
||||
}
|
||||
|
||||
@@ -6,14 +6,15 @@
|
||||
[action]="'leave'"
|
||||
(smHesitate)="menu.closed.emit();"
|
||||
>
|
||||
<p class="command">{{command}}</p>
|
||||
<div class="w-100 d-flex flex-row-reverse">
|
||||
<p class="command">{{command}}</p>
|
||||
<div class="w-100 d-flex action">
|
||||
<i class="al-icon al-ico-success sm me-1" [class.visible]="copySuccess"></i>
|
||||
<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 me-1" [class.visible]="copySuccess"></i>
|
||||
>Copy command</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-menu>
|
||||
@@ -23,30 +24,32 @@
|
||||
[delay]="1000" [action]="'leave'"
|
||||
(smHesitate)="menuHesitate.hesitateStatus && menu.closed.emit()"
|
||||
>
|
||||
<i class="al-icon al-ico-download pointer line-item"
|
||||
data-id="downloadButton"
|
||||
#idElement
|
||||
[matMenuTriggerFor]="menu"
|
||||
(click)="openMenu(); menuHesitate.hesitateStatus = true"
|
||||
></i>
|
||||
<button
|
||||
class="btn"
|
||||
data-id="downloadButton"
|
||||
[matMenuTriggerFor]="menu"
|
||||
(click)="openMenu(); menuHesitate.hesitateStatus = true"
|
||||
><i class="al-icon al-ico-download pointer line-item"></i></button>
|
||||
</span>
|
||||
<sm-table
|
||||
[columns]="columns"
|
||||
[tableData]="tableData"
|
||||
[selectionMode]="null"
|
||||
[virtualScroll]="true"
|
||||
[scrollable]="true">
|
||||
|
||||
[scrollable]="true"
|
||||
[resizableColumns]="true"
|
||||
(columnResized)="resizeCol($event)"
|
||||
>
|
||||
<ng-template
|
||||
let-col
|
||||
let-i="rowIndex"
|
||||
let-row="rowData"
|
||||
let-line="rowData"
|
||||
pTemplate="body">
|
||||
<div [ngSwitch]="col.id" class="w-100">
|
||||
<ng-container *ngSwitchCase="'0'">
|
||||
<span class="ellipsis" [attr.fileType]="row[0].match('\\.([^ .]+)$')?.[1] || 'none'" smShowTooltipIfEllipsis [smTooltip]="row[0]">{{row[0]}}</span>
|
||||
<div [ngSwitch]="col.id">
|
||||
<ng-container *ngSwitchCase="'name'">
|
||||
<span class="ellipsis" [attr.fileType]="line.name?.match('\\.([^ .]+)$')?.[1] || 'none'" smShowTooltipIfEllipsis [smTooltip]="line.name">{{line.name}}</span>
|
||||
</ng-container>
|
||||
<div *ngSwitchDefault class="ellipsis" smShowTooltipIfEllipsis [smTooltip]="row[col.id]">{{row[col.id]}}</div>
|
||||
<div *ngSwitchDefault class="ellipsis" smShowTooltipIfEllipsis [smTooltip]="line[col.id]">{{line[col.id]}}</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</sm-table>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@import 'src/app/webapp-common/shared/ui-components/styles/variables';
|
||||
@import 'variables';
|
||||
@import "../../../webapp-common/assets/fonts/variables.scss";
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
@@ -25,7 +26,7 @@
|
||||
[filetype]::before {
|
||||
content: "\e9d3"; // fallback icon (file)
|
||||
color: #596c71;
|
||||
font-family: 'trains';
|
||||
font-family: #{$icomoon-font-family};
|
||||
font-size: 24px;
|
||||
margin-right: 12px;
|
||||
vertical-align: middle;
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
import {ChangeDetectionStrategy, Component, Input, ViewChild} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, Component, ElementRef, 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';
|
||||
|
||||
|
||||
const columnIds = ['name', 'size', 'hash'];
|
||||
|
||||
type Row = { [K in typeof columnIds[number]]: string }
|
||||
|
||||
interface RowData extends Row {
|
||||
id: string
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'sm-simple-dataset-version-content',
|
||||
templateUrl: './simple-dataset-version-content.component.html',
|
||||
@@ -11,12 +20,13 @@ import {fileSizeConfigStorage, FileSizePipe} from '@common/shared/pipes/filesize
|
||||
})
|
||||
export class SimpleDatasetVersionContentComponent {
|
||||
public columns: ISmCol[];
|
||||
public tableData: string[][];
|
||||
public tableData: RowData[];
|
||||
public command: string;
|
||||
private ngFile = new FileSizePipe();
|
||||
|
||||
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
|
||||
copySuccess: boolean;
|
||||
private colSizes = {} as { [colId: string]: number };
|
||||
|
||||
@Input() set id(id: string) {
|
||||
this.command = `clearml-data get --id ${id}`;
|
||||
@@ -24,21 +34,43 @@ export class SimpleDatasetVersionContentComponent {
|
||||
}
|
||||
|
||||
@Input() set data(csv: string) {
|
||||
const lines = csv?.split('\n') ?? [];
|
||||
const lines = csv?.trimEnd().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);
|
||||
const colWidth = (this.ref.nativeElement.getBoundingClientRect().width - 150) / 2 ?? 300;
|
||||
this.columns = header.split(/, ?/)
|
||||
.map((caption, index) => {
|
||||
const width = this.colSizes?.[columnIds[index]] ? `${this.colSizes[columnIds[index]]}px` : null;
|
||||
return {
|
||||
id: columnIds[index],
|
||||
header: caption,
|
||||
style: index === 1 ?
|
||||
{width: width ?? '150px'} :
|
||||
{
|
||||
// maxWidth: width ?? `${colWidth}px`,
|
||||
width: width ?? `${colWidth}px`,
|
||||
}
|
||||
};
|
||||
});
|
||||
const tableData = lines.map((line, index) => ({
|
||||
...line.split(/, ?/).reduce((acc, val, i) => {
|
||||
acc[columnIds[i]] = val;
|
||||
return acc;
|
||||
}, {} as Row),
|
||||
id: `${index}`
|
||||
} as RowData));
|
||||
if (Number(tableData[0]?.size) && this.columns[1]?.header?.includes('ize')) {
|
||||
this.tableData = tableData.map( line => ({
|
||||
...line,
|
||||
size: this.ngFile.transform(parseInt(line.size, 10) || 0, fileSizeConfigStorage) as string
|
||||
}));
|
||||
} else {
|
||||
this.tableData = tableData;
|
||||
}
|
||||
this.tableData = tableData;
|
||||
|
||||
}
|
||||
|
||||
constructor(private readonly ref: ElementRef){}
|
||||
|
||||
openMenu() {
|
||||
this.trigger.openMenu();
|
||||
}
|
||||
@@ -47,4 +79,9 @@ export class SimpleDatasetVersionContentComponent {
|
||||
this.copySuccess = true;
|
||||
window.setTimeout(() => this.copySuccess = false, 3000);
|
||||
}
|
||||
|
||||
resizeCol({columnId, widthPx}: {columnId: string, widthPx: number}) {
|
||||
this.colSizes[columnId] = widthPx;
|
||||
this.columns = this.columns.map(col => col.id === columnId ? {...col, style: {width: widthPx + 'px'}} : col);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="header"><span>DESCRIPTION</span><i data-id="editIcon" class="al-icon al-ico-edit sm" (click)="editDescription.emit(entity)"></i></div>
|
||||
<div class="header"><span>DESCRIPTION</span><i tabindex="1" data-id="editIcon" class="al-icon al-ico-edit sm" (click)="editDescription.emit(entity)" (keyup)="editDescription.emit(entity)"></i></div>
|
||||
<div class="param continue h-auto">
|
||||
<div class="multi-line-value">{{entity?.comment}}</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import {Component, EventEmitter, Output} 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';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {IExperimentInfo} from '~/features/experiments/shared/experiment-info.model';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-simple-dataset-version-details',
|
||||
@@ -10,10 +10,10 @@ import {Task} from '~/business-logic/model/tasks/task';
|
||||
styleUrls: ['./simple-dataset-version-details.component.scss', '../../pipelines-controller/pipeline-details/pipeline-info.component.scss']
|
||||
})
|
||||
export class SimpleDatasetVersionDetailsComponent extends PipelineInfoComponent {
|
||||
public fileSizeConfigStorage = fileSizeConfigStorage;
|
||||
public override fileSizeConfigStorage = fileSizeConfigStorage;
|
||||
|
||||
public convertStatusMap = DATASETS_STATUS_LABEL;
|
||||
public convertStatusMapBase = EXPERIMENTS_STATUS_LABELS;
|
||||
|
||||
@Output() editDescription = new EventEmitter<Task>();
|
||||
@Output() editDescription = new EventEmitter<IExperimentInfo>();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {ChangeDetectorRef, Component, NgZone} from '@angular/core';
|
||||
import {Component} from '@angular/core';
|
||||
import {PipelineControllerInfoComponent, PipelineItem, StatusOption} from '@common/pipelines-controller/pipeline-controller-info/pipeline-controller-info.component';
|
||||
import {DagManagerUnsortedService} from '@common/shared/services/dag-manager-unsorted.service';
|
||||
import {experimentDetailsUpdated, getSelectedPipelineStep, setSelectedPipelineStep} from '@common/experiments/actions/common-experiments-info.actions';
|
||||
import {last} from 'lodash-es';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {EditJsonComponent, EditJsonData} from '@common/shared/ui-components/overlay/edit-json/edit-json.component';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {CommonExperimentsInfoEffects} from '@common/experiments/effects/common-experiments-info.effects';
|
||||
import {tap} from 'rxjs/operators';
|
||||
import {TaskStatusEnum} from '~/business-logic/model/tasks/taskStatusEnum';
|
||||
import {IExperimentInfo} from '~/features/experiments/shared/experiment-info.model';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-simple-dataset-version-info',
|
||||
@@ -20,22 +20,17 @@ import {tap} from 'rxjs/operators';
|
||||
providers: [DagManagerUnsortedService]
|
||||
})
|
||||
export class SimpleDatasetVersionInfoComponent extends PipelineControllerInfoComponent {
|
||||
detailsPanelMode = StatusOption.content;
|
||||
defaultDetailsMode = StatusOption.content;
|
||||
public maximizeResults: boolean;
|
||||
|
||||
constructor(
|
||||
protected _dagManager: DagManagerUnsortedService<PipelineItem>,
|
||||
protected store: Store,
|
||||
protected cdr: ChangeDetectorRef,
|
||||
protected zone: NgZone,
|
||||
private dialog: MatDialog,
|
||||
private commonExperimentsInfoEffects: CommonExperimentsInfoEffects
|
||||
) {
|
||||
super(_dagManager, store, cdr, zone);
|
||||
super();
|
||||
this.detailsPanelMode = StatusOption.content;
|
||||
this.defaultDetailsMode = StatusOption.content;
|
||||
}
|
||||
|
||||
convertPipelineToDagModel(pipeline): PipelineItem[] {
|
||||
override convertPipelineToDagModel(pipeline): PipelineItem[] {
|
||||
const res = super.convertPipelineToDagModel(pipeline);
|
||||
if (res?.length > 0) {
|
||||
window.setTimeout(() => this.selectStep(last(res)), 1000);
|
||||
@@ -45,15 +40,15 @@ export class SimpleDatasetVersionInfoComponent extends PipelineControllerInfoCom
|
||||
return res;
|
||||
}
|
||||
|
||||
getEntityId(params) {
|
||||
override getEntityId(params) {
|
||||
return params?.versionId;
|
||||
}
|
||||
|
||||
protected getTreeObject(task) {
|
||||
protected override getTreeObject(task) {
|
||||
return task?.configuration?.['Dataset Struct']?.value;
|
||||
}
|
||||
|
||||
toggleResultSize() {
|
||||
override toggleResultSize() {
|
||||
this.maximizeResults = ! this.maximizeResults;
|
||||
if (this.detailsPanelMode === StatusOption.content) {
|
||||
this.detailsPanelMode = null;
|
||||
@@ -64,13 +59,13 @@ export class SimpleDatasetVersionInfoComponent extends PipelineControllerInfoCom
|
||||
}
|
||||
}
|
||||
|
||||
selectStep(step?: PipelineItem) {
|
||||
override selectStep(step?: PipelineItem) {
|
||||
if (step) {
|
||||
const id = step?.data?.job_id;
|
||||
if (id) {
|
||||
this.store.dispatch(getSelectedPipelineStep({id}));
|
||||
} else {
|
||||
this.store.dispatch(setSelectedPipelineStep({step: {id, type: step.data.job_type, status: step.data.status, name: step.name}}));
|
||||
this.store.dispatch(setSelectedPipelineStep({step: {id, type: step.data.job_type, status: step.data.status as unknown as TaskStatusEnum, name: step.name}}));
|
||||
this.showLog = false;
|
||||
}
|
||||
this.selectedEntity = step;
|
||||
@@ -82,13 +77,15 @@ export class SimpleDatasetVersionInfoComponent extends PipelineControllerInfoCom
|
||||
this.showLog = !this.showLog;
|
||||
}
|
||||
|
||||
protected getPanelMode() {
|
||||
protected override getPanelMode() {
|
||||
return this.detailsPanelMode;
|
||||
}
|
||||
|
||||
protected resetUninitializedRunningFields() {}
|
||||
protected override resetUninitializedRunningFields() {
|
||||
|
||||
editDescription(dataset: Task) {
|
||||
}
|
||||
|
||||
editDescription(dataset: IExperimentInfo) {
|
||||
const editJsonComponent = this.dialog.open(EditJsonComponent, {
|
||||
data: {
|
||||
textData: dataset.comment,
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
selectionMode="single"
|
||||
[initialColumns]="tableCols"
|
||||
[colsOrder]="(tableColsOrder$ | async)"
|
||||
[tableCols]="tableCols$| async"
|
||||
[tableCols]="tableCols$ | async"
|
||||
[experiments]="experiments$ | async"
|
||||
[users]="users$ | async"
|
||||
[hyperParamsOptions]="hyperParamsOptions$ | async"
|
||||
@@ -105,14 +105,15 @@
|
||||
#contextMenu
|
||||
[experiment]="contextExperiment"
|
||||
[selectedExperiment]="selectedExperiment$ | async"
|
||||
[selectedExperiments]="singleRowContext ? [contextExperiment] : selectedExperiments"
|
||||
[selectedExperiments]="singleRowContext ? (selectedExperiment$ | async) ? [(selectedExperiment$ | async)] : undefined : selectedExperiments"
|
||||
[selectedDisableAvailableIsMultiple]="!singleRowContext"
|
||||
[selectedDisableAvailable]="singleRowContext ? getSingleSelectedDisableAvailable(contextExperiment) : (selectedExperimentsDisableAvailable$ | async)"
|
||||
[selectedDisableAvailable]="singleRowContext ? getSingleSelectedDisableAvailable((selectedExperiment$ | async) || contextExperiment ) : (selectedExperimentsDisableAvailable$ | async)"
|
||||
[numSelected]="singleRowContext ? 1 : selectedExperiments.length"
|
||||
[tagsFilterByProject]="tagsFilterByProject$ | async"
|
||||
[projectTags]="tags$ | async"
|
||||
[companyTags]="companyTags$ | async"
|
||||
[activateFromMenuButton]="false"
|
||||
[useCurrentEntity]="singleRowContext"
|
||||
[minimizedView]="true"
|
||||
[tableMode]="!minimizedView"
|
||||
[backdrop]="menuBackdrop"
|
||||
|
||||
@@ -6,10 +6,6 @@ import {CountAvailableAndIsDisableSelectedFiltered} from '@common/shared/entity-
|
||||
import * as experimentsActions from '@common/experiments/actions/common-experiments-view.actions';
|
||||
import {INITIAL_CONTROLLER_TABLE_COLS} from '@common/pipelines-controller/controllers.consts';
|
||||
import {EXPERIMENTS_TABLE_COL_FIELDS} from '~/features/experiments/shared/experiments.const';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
import {take, withLatestFrom} from 'rxjs/operators';
|
||||
import {selectDefaultNestedModeForFeature} from '@common/core/reducers/projects.reducer';
|
||||
import {setBreadcrumbsOptions} from '@common/core/actions/projects.actions';
|
||||
@@ -20,26 +16,20 @@ import {setBreadcrumbsOptions} from '@common/core/actions/projects.actions';
|
||||
styleUrls: ['./simple-dataset-versions.component.scss', '../../pipelines-controller/controllers.component.scss']
|
||||
})
|
||||
export class SimpleDatasetVersionsComponent extends ControllersComponent implements OnInit {
|
||||
entityType = EntityTypeEnum.dataset;
|
||||
public shouldOpenDetails = true;
|
||||
isArchived: boolean;
|
||||
|
||||
protected getParamId(params) {
|
||||
protected override getParamId(params) {
|
||||
return params?.versionId;
|
||||
}
|
||||
|
||||
constructor(protected store: Store,
|
||||
protected route: ActivatedRoute,
|
||||
protected router: Router,
|
||||
protected dialog: MatDialog,
|
||||
protected refresh: RefreshService
|
||||
) {
|
||||
super(store, route, router, dialog, refresh);
|
||||
constructor() {
|
||||
super();
|
||||
this.entityType = EntityTypeEnum.dataset;
|
||||
this.tableCols = INITIAL_CONTROLLER_TABLE_COLS.map((col) =>
|
||||
col.id === EXPERIMENTS_TABLE_COL_FIELDS.NAME ? {...col, header: 'VERSION NAME'} : col);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
override ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.experiments$
|
||||
.pipe(take(1))
|
||||
@@ -57,7 +47,7 @@ export class SimpleDatasetVersionsComponent extends ControllersComponent impleme
|
||||
});
|
||||
}
|
||||
|
||||
createFooterItems(config: {
|
||||
override createFooterItems(config: {
|
||||
entitiesType: EntityTypeEnum;
|
||||
selected$: Observable<Array<any>>;
|
||||
showAllSelectedIsActive$: Observable<boolean>;
|
||||
@@ -71,10 +61,10 @@ export class SimpleDatasetVersionsComponent extends ControllersComponent impleme
|
||||
this.footerItems.splice(5, 1);
|
||||
}
|
||||
|
||||
downloadTableAsCSV() {
|
||||
override downloadTableAsCSV() {
|
||||
this.table.table.downloadTableAsCSV(`ClearML ${this.selectedProject.id === '*'? 'All': this.selectedProject?.basename?.substring(0,60)} Datasets`);
|
||||
}
|
||||
setupBreadcrumbsOptions() {
|
||||
override setupBreadcrumbsOptions() {
|
||||
this.sub.add(this.selectedProject$.pipe(
|
||||
withLatestFrom(this.store.select(selectDefaultNestedModeForFeature))
|
||||
).subscribe(([selectedProject, defaultNestedModeForFeature]) => {
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<sm-dialog-template *ngIf="!showButton; else content" iconClass="al-icon al-ico-datasets al-color blue-300" header="CREATE NEW DATASET">
|
||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||
</sm-dialog-template>
|
||||
|
||||
|
||||
|
||||
|
||||
<ng-template #content>
|
||||
<div class="content">
|
||||
<div class="code">
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import "variables";
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@@ -16,8 +18,15 @@
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
a.dark {
|
||||
color: white;
|
||||
a {
|
||||
color: $purple;
|
||||
&.dark {
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
sm-code-editor {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<i class="al-icon al-ico-add sm me-2"></i>NEW DATASET
|
||||
</button>
|
||||
|
||||
<div empty-state class="empty-datasets">
|
||||
<div empty-state class="empty-state">
|
||||
<div class="title-icon"><i class="al-icon al-ico-datasets xxl"></i></div>
|
||||
<div class="title">NO DATASETS TO SHOW</div>
|
||||
<div class="sub-title">Run your first dataset to see it displayed here
|
||||
|
||||
@@ -19,16 +19,13 @@ import {
|
||||
selectProjectTags
|
||||
} from '@common/core/reducers/projects.reducer';
|
||||
import {combineLatest, Observable, Subscription} from 'rxjs';
|
||||
import {debounceTime, withLatestFrom} from 'rxjs/operators';
|
||||
import {debounceTime, skip, withLatestFrom} from 'rxjs/operators';
|
||||
import {getAllProjectsPageProjects, resetProjects} from '@common/projects/common-projects.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-nested-simple-datasets-page',
|
||||
templateUrl: './nested-simple-datasets-page.component.html',
|
||||
styleUrls: [
|
||||
'../../../webapp-common/nested-project-view/nested-project-view-page/nested-project-view-page.component.scss',
|
||||
'../../../webapp-common/datasets/simple-datasets/empty.scss'
|
||||
],
|
||||
styleUrls: ['../../../webapp-common/nested-project-view/nested-project-view-page/nested-project-view-page.component.scss'],
|
||||
imports: [
|
||||
ProjectsSharedModule,
|
||||
SMSharedModule,
|
||||
@@ -45,7 +42,7 @@ export class NestedSimpleDatasetsPageComponent extends CommonProjectsPageCompone
|
||||
projectsTags$: Observable<string[]>;
|
||||
private mainPageFilterSub: Subscription;
|
||||
|
||||
projectCardClicked(data: { hasSubProjects: boolean; id: string; name: string }) {
|
||||
override projectCardClicked(data: { hasSubProjects: boolean; id: string; name: string }) {
|
||||
if (data.hasSubProjects) {
|
||||
this.router.navigate(['simple', data.id, 'projects'], {relativeTo: this.route.parent?.parent});
|
||||
} else {
|
||||
@@ -68,11 +65,11 @@ export class NestedSimpleDatasetsPageComponent extends CommonProjectsPageCompone
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
protected getExtraProjects(selectedProjectId, selectedProject) {
|
||||
protected override getExtraProjects(selectedProjectId, selectedProject) {
|
||||
return [];
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
override ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.projectsTags$ = this.store.select(selectProjectTags);
|
||||
this.store.dispatch(getProjectsTags({entity: 'dataset'}));
|
||||
@@ -80,28 +77,28 @@ export class NestedSimpleDatasetsPageComponent extends CommonProjectsPageCompone
|
||||
this.mainPageFilterSub = combineLatest([
|
||||
this.store.select(selectMainPageTagsFilter),
|
||||
this.store.select(selectMainPageTagsFilterMatchMode)
|
||||
]).pipe(debounceTime(0))
|
||||
]).pipe(debounceTime(0), skip(1))
|
||||
.subscribe(() => {
|
||||
this.store.dispatch(resetProjects());
|
||||
this.store.dispatch(getAllProjectsPageProjects());
|
||||
});
|
||||
}
|
||||
noProjectsReRoute() {
|
||||
override noProjectsReRoute() {
|
||||
return this.router.navigate(['..', 'datasets'], {relativeTo: this.route});
|
||||
}
|
||||
|
||||
shouldReRoute(selectedProject, config) {
|
||||
override shouldReRoute(selectedProject, config) {
|
||||
const relevantSubProjects = selectedProject?.sub_projects?.filter(proj => proj.name.includes('.datasets'));
|
||||
return config[3] === 'projects' && selectedProject.id !== '*' && (relevantSubProjects?.every(subProject => subProject.name.startsWith(selectedProject.name + '/.')));
|
||||
};
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
override ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
this.mainPageFilterSub.unsubscribe();
|
||||
this.store.dispatch(setTags({tags: []}));
|
||||
}
|
||||
|
||||
setupBreadcrumbsOptions() {
|
||||
override setupBreadcrumbsOptions() {
|
||||
this.subs.add(this.selectedProject$.pipe(
|
||||
withLatestFrom(this.store.select(selectDefaultNestedModeForFeature))
|
||||
).subscribe(([selectedProject, defaultNestedModeForFeature]) => {
|
||||
|
||||
@@ -38,9 +38,14 @@
|
||||
(delete)="delete.emit()"
|
||||
></sm-pipeline-card-menu>
|
||||
</div>
|
||||
<div *ngIf="project.last_update; else: noRun" class="last-run">Updated {{project.last_update | timeAgo}}</div>
|
||||
<span
|
||||
*ngIf="project.last_update; else: noRun"
|
||||
class="last-run"
|
||||
[smTooltip]="project.last_update | date: timeFormatString"
|
||||
matTooltipPosition="after"
|
||||
>Updated {{project.last_update | timeAgo}}</span>
|
||||
<ng-template #noRun>
|
||||
<div class="last-run"></div>
|
||||
<span class="last-run"></span>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="d-flex justify-content-around w-100">
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
@import "variables";
|
||||
|
||||
.empty-datasets {
|
||||
width: 100%;
|
||||
grid-column: 1/-1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: map-get($grid-max-widths, xl);
|
||||
margin: 0 auto;
|
||||
|
||||
.title-icon {
|
||||
color: $blue-600;
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: $blue-100;
|
||||
margin-bottom: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
text-align: center;
|
||||
margin-bottom: 64px;
|
||||
|
||||
.link {
|
||||
color: $neon-yellow;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #emptyState>
|
||||
<div class="empty-datasets">
|
||||
<div class="empty-state">
|
||||
<div class="title-icon"><i class="al-icon al-ico-datasets xxl"></i></div>
|
||||
<div class="title">NO DATASETS TO SHOW</div>
|
||||
<div class="sub-title">Run your first dataset to see it displayed here
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
@import "variables";
|
||||
@import "./empty";
|
||||
@import "empty";
|
||||
|
||||
:host {
|
||||
.sm-card-list-layout{
|
||||
&.in-empty-state{
|
||||
.sm-card-list-layout {
|
||||
&.in-empty-state {
|
||||
height: 100%;
|
||||
grid-template-rows: 64px 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
display: block;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
@@ -22,6 +23,5 @@
|
||||
background-color: $blue-800;
|
||||
margin-bottom: -12px;
|
||||
}
|
||||
@include empty-card-mixin();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,19 +20,23 @@ import {selectDefaultNestedModeForFeature} from '@common/core/reducers/projects.
|
||||
})
|
||||
export class SimpleDatasetsComponent extends PipelinesPageComponent implements OnInit {
|
||||
|
||||
public projectCardClicked(project: ProjectsGetAllResponseSingle) {
|
||||
public override projectCardClicked(project: ProjectsGetAllResponseSingle) {
|
||||
this.router.navigate(['simple', project.id, 'experiments'], {relativeTo: this.projectId? this.route.parent.parent: this.route});
|
||||
this.store.dispatch(setSelectedProjectId({projectId: project.id, example: this.isExample(project)}));
|
||||
}
|
||||
|
||||
protected getName() {
|
||||
protected override getName() {
|
||||
return EntityTypeEnum.simpleDataset;
|
||||
}
|
||||
|
||||
protected getDeletePopupEntitiesList() {
|
||||
protected override getDeletePopupEntitiesList() {
|
||||
return 'version';
|
||||
}
|
||||
|
||||
override noProjectsReRoute() {
|
||||
return this.router.navigate(['..', 'datasets'], {relativeTo: this.route});
|
||||
}
|
||||
|
||||
createDataset() {
|
||||
this.dialog.open(DatasetEmptyComponent, {
|
||||
maxWidth: '95vw',
|
||||
@@ -40,14 +44,14 @@ export class SimpleDatasetsComponent extends PipelinesPageComponent implements O
|
||||
});
|
||||
}
|
||||
|
||||
public createExamples() {
|
||||
public override createExamples() {
|
||||
this.store.dispatch(showExampleDatasets());
|
||||
}
|
||||
ngOnInit() {
|
||||
override ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.showExamples$ = this.store.select(selectShowDatasetExamples);
|
||||
}
|
||||
toggleNestedView(nested: boolean) {
|
||||
override toggleNestedView(nested: boolean) {
|
||||
this.store.dispatch(setDefaultNestedModeForFeature({feature: 'datasets', isNested: nested}));
|
||||
|
||||
if (nested) {
|
||||
@@ -57,7 +61,7 @@ export class SimpleDatasetsComponent extends PipelinesPageComponent implements O
|
||||
}
|
||||
}
|
||||
|
||||
setupBreadcrumbsOptions() {
|
||||
override setupBreadcrumbsOptions() {
|
||||
this.subs.add(this.selectedProject$.pipe(
|
||||
withLatestFrom(this.store.select(selectDefaultNestedModeForFeature))
|
||||
).subscribe(([selectedProject, defaultNestedModeForFeature]) => {
|
||||
|
||||
@@ -3,64 +3,61 @@ import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {TaskMetric} from '~/business-logic/model/events/taskMetric';
|
||||
import {EventsDebugImagesResponse} from '~/business-logic/model/events/eventsDebugImagesResponse';
|
||||
|
||||
export const DEBUG_IMAGES_PREFIX = 'DEBUG_IMAGES_';
|
||||
export const DEBUG_IMAGES_PREFIX = '[DEBUG IMAGES] ';
|
||||
|
||||
|
||||
export const resetDebugImages = createAction(DEBUG_IMAGES_PREFIX + 'RESET_DEBUG_IMAGES');
|
||||
export const resetDebugImages = createAction(DEBUG_IMAGES_PREFIX + 'reset debug images');
|
||||
|
||||
export const setDebugImages = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'SET_DEBUG_IMAGES',
|
||||
DEBUG_IMAGES_PREFIX + 'set debug images',
|
||||
props<{ res: EventsDebugImagesResponse; task: string }>()
|
||||
);
|
||||
export const getDebugImagesMetrics = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'GET_DEBUG_IMAGES_METRICS',
|
||||
DEBUG_IMAGES_PREFIX + 'get debug images metrics',
|
||||
props<{ tasks: string[] }>()
|
||||
);
|
||||
|
||||
export const refreshDebugImagesMetrics = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'REFRESH_DEBUG_IMAGES_METRICS',
|
||||
DEBUG_IMAGES_PREFIX + 'refresh_debug_images_metrics',
|
||||
props<{ tasks: string[]; autoRefresh?: boolean }>()
|
||||
);
|
||||
|
||||
export const fetchExperiments = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'FETCH_EXPERIMENTS',
|
||||
DEBUG_IMAGES_PREFIX + 'fetch experiments',
|
||||
props<{ tasks: string[] }>()
|
||||
);
|
||||
|
||||
export const setExperimentsNames = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'SET_EXPERIMENTS_NAMES',
|
||||
DEBUG_IMAGES_PREFIX + 'set experiments names',
|
||||
props<{ tasks: Partial<Task>[] }>()
|
||||
);
|
||||
|
||||
export const setMetrics = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'SET_DEBUG_IMAGES_METRICS',
|
||||
DEBUG_IMAGES_PREFIX + 'set debug images metrics',
|
||||
props<{ metrics: any[] }>()
|
||||
);
|
||||
|
||||
export const setSelectedMetric = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'SET_DEBUG_IMAGES_SELECTED_METRIC',
|
||||
DEBUG_IMAGES_PREFIX + 'set debug images selected metric',
|
||||
props<{ payload: TaskMetric }>()
|
||||
);
|
||||
|
||||
export const refreshMetric = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'REFRESH_IMAGES_SELECTED_METRIC',
|
||||
DEBUG_IMAGES_PREFIX + 'refresh images selected metric',
|
||||
props<{ payload: TaskMetric; autoRefresh?: boolean }>()
|
||||
);
|
||||
|
||||
export const getNextBatch= createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'GET_NEXT_DEBUG_IMAGES_BATCH',
|
||||
DEBUG_IMAGES_PREFIX + 'get next debug images batch',
|
||||
props<{ payload: TaskMetric }>()
|
||||
);
|
||||
|
||||
export const getPreviousBatch= createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'GET_PREVIOUS_DEBUG_IMAGES_BATCH',
|
||||
DEBUG_IMAGES_PREFIX + 'get previous debug images batch',
|
||||
props<{ payload: TaskMetric }>()
|
||||
);
|
||||
|
||||
export const setTimeIsNow = createAction(
|
||||
DEBUG_IMAGES_PREFIX + 'SET_TIME_IS_NOW',
|
||||
DEBUG_IMAGES_PREFIX + 'set time is now',
|
||||
props<{ task: string; timeIsNow: boolean }>()
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {catchError, mergeMap, map, switchMap, withLatestFrom, filter} from 'rxjs/operators';
|
||||
import {Actions, concatLatestFrom, createEffect, ofType} from '@ngrx/effects';
|
||||
import {catchError, mergeMap, map, switchMap, filter} from 'rxjs/operators';
|
||||
import * as debugActions from './debug-images-actions';
|
||||
import {activeLoader, deactivateLoader} from '../core/actions/layout.actions';
|
||||
import {ApiTasksService} from '~/business-logic/api-services/tasks.service';
|
||||
@@ -8,7 +8,7 @@ import {ApiEventsService} from '~/business-logic/api-services/events.service';
|
||||
import {requestFailed} from '../core/actions/http.actions';
|
||||
import {refreshExperiments} from '../experiments/actions/common-experiments-view.actions';
|
||||
import {Action, Store} from '@ngrx/store';
|
||||
import {selectDebugImages} from './debug-images-reducer';
|
||||
import {selectDebugImages, selectSelectedMetricForTask} from './debug-images-reducer';
|
||||
import {EventsDebugImagesResponse} from '~/business-logic/model/events/eventsDebugImagesResponse';
|
||||
import {EventsGetTaskMetricsResponse} from '~/business-logic/model/events/eventsGetTaskMetricsResponse';
|
||||
import {COMPARE_DEBUG_IMAGES_ONLY_FIELDS} from '../experiments-compare/experiments-compare.constants';
|
||||
@@ -17,8 +17,10 @@ import {setBeginningOfTime} from '@common/shared/debug-sample/debug-sample.actio
|
||||
|
||||
export const ALL_IMAGES = '-- All --';
|
||||
|
||||
export const removeAllImagesFromPayload = (payload) =>
|
||||
({...payload, metric: payload.metric === ALL_IMAGES ? null : payload.metric});
|
||||
export const removeAllImagesFromPayload = (payload, selectedMetric?: string ) => {
|
||||
const metric = selectedMetric ?? payload.metric
|
||||
return ({...payload, metric: metric === ALL_IMAGES ? null : metric});
|
||||
}
|
||||
|
||||
|
||||
// interface Image {
|
||||
@@ -40,7 +42,7 @@ export class DebugImagesEffects {
|
||||
|
||||
constructor(
|
||||
private actions$: Actions, private apiTasks: ApiTasksService,
|
||||
private eventsApi: ApiEventsService, private store: Store<any>
|
||||
private eventsApi: ApiEventsService, private store: Store
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -53,11 +55,11 @@ export class DebugImagesEffects {
|
||||
|
||||
fetchDebugImages$ = createEffect(() => this.actions$.pipe(
|
||||
ofType(debugActions.setSelectedMetric, debugActions.getNextBatch, debugActions.getPreviousBatch, debugActions.refreshMetric),
|
||||
withLatestFrom(this.store.select(selectDebugImages)),
|
||||
mergeMap(([action, debugImages]) =>
|
||||
this.eventsApi.eventsDebugImages({
|
||||
concatLatestFrom(() => [this.store.select(selectDebugImages), this.store.select(selectSelectedMetricForTask)]),
|
||||
mergeMap(([action, debugImages, selectedMetricForTask ]) =>
|
||||
this.eventsApi.eventsDebugImages({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
metrics: [removeAllImagesFromPayload(action.payload)],
|
||||
metrics: [removeAllImagesFromPayload(action.payload, selectedMetricForTask[action.payload.task])],
|
||||
iters: 3,
|
||||
scroll_id: (action.type !== debugActions.setSelectedMetric.type && debugImages?.[action.payload.task]) ?
|
||||
debugImages[action.payload.task].scroll_id :
|
||||
@@ -126,9 +128,11 @@ export class DebugImagesEffects {
|
||||
|
||||
fetchMetrics$ = createEffect(() => this.actions$.pipe(
|
||||
ofType(debugActions.getDebugImagesMetrics, debugActions.refreshDebugImagesMetrics),
|
||||
switchMap((action) => this.store.select(selectActiveWorkspaceReady).pipe(
|
||||
filter(ready => ready),
|
||||
map(() => action))),
|
||||
switchMap((action) => this.store.select(selectActiveWorkspaceReady)
|
||||
.pipe(
|
||||
filter(ready => ready),
|
||||
map(() => action))
|
||||
),
|
||||
switchMap((action) => this.eventsApi.eventsGetTaskMetrics({
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
tasks: action.tasks,
|
||||
@@ -136,7 +140,11 @@ export class DebugImagesEffects {
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
})
|
||||
.pipe(
|
||||
mergeMap((res: EventsGetTaskMetricsResponse) => [debugActions.setMetrics({metrics: res.metrics}), deactivateLoader(action.type)]),
|
||||
mergeMap((res: EventsGetTaskMetricsResponse) => [
|
||||
debugActions.setMetrics({metrics: res.metrics}),
|
||||
deactivateLoader(debugActions.getDebugImagesMetrics.type),
|
||||
deactivateLoader(debugActions.refreshDebugImagesMetrics.type),
|
||||
]),
|
||||
catchError(error => [requestFailed(error), deactivateLoader(action.type)])
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import {
|
||||
fetchExperiments, getDebugImagesMetrics, resetDebugImages, setDebugImages, setExperimentsNames, setMetrics, setTimeIsNow
|
||||
fetchExperiments,
|
||||
getDebugImagesMetrics,
|
||||
resetDebugImages,
|
||||
setDebugImages,
|
||||
setExperimentsNames,
|
||||
setMetrics, setSelectedMetric,
|
||||
setTimeIsNow
|
||||
} from './debug-images-actions';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {createFeatureSelector, createReducer, createSelector, on} from '@ngrx/store';
|
||||
@@ -7,14 +13,14 @@ import {EventsDebugImagesResponse} from '~/business-logic/model/events/eventsDeb
|
||||
|
||||
|
||||
export interface IDebugImagesState {
|
||||
debugImages: {[taskId: string]: EventsDebugImagesResponse};
|
||||
debugImages: { [taskId: string]: EventsDebugImagesResponse };
|
||||
settingsList: Array<IDebugImagesSettings>;
|
||||
tasks: Array<Partial<Task>>;
|
||||
optionalMetrics: Array<ITaskOptionalMetrics>;
|
||||
selectedMetricsForTask: { [taskId: string]: string };
|
||||
searchTerm: string;
|
||||
scrollId: any;
|
||||
noMore: boolean;
|
||||
selectedMetric: any;
|
||||
timeIsNow: any;
|
||||
|
||||
}
|
||||
@@ -37,7 +43,7 @@ export const initialState: IDebugImagesState = {
|
||||
optionalMetrics: [],
|
||||
scrollId: {},
|
||||
noMore: true,
|
||||
selectedMetric: null,
|
||||
selectedMetricsForTask: {},
|
||||
timeIsNow: {}
|
||||
}
|
||||
;
|
||||
@@ -47,6 +53,7 @@ export const selectDebugImages = createSelector(debugImages, (state) => state.de
|
||||
export const selectTaskNames = createSelector(debugImages, (state) => state.tasks);
|
||||
export const selectNoMore = createSelector(debugImages, (state) => state.noMore);
|
||||
export const selectOptionalMetrics = createSelector(debugImages, (state) => state.optionalMetrics);
|
||||
export const selectSelectedMetricForTask = createSelector(debugImages, (state) => state.selectedMetricsForTask);
|
||||
export const selectTimeIsNow = createSelector(debugImages, (state) => state.timeIsNow);
|
||||
|
||||
|
||||
@@ -61,8 +68,8 @@ export const debugSamplesReducer = createReducer(
|
||||
on(setDebugImages, (state, action) => ({...state, debugImages: {...state.debugImages, [action.task]: action.res}})),
|
||||
on(setExperimentsNames, (state, action) => ({...state, tasks: action.tasks})),
|
||||
on(setMetrics, (state, action) => ({...state, optionalMetrics: action.metrics})),
|
||||
on(setSelectedMetric, (state, action) => ({...state, selectedMetricsForTask: {...state.selectedMetricsForTask, [action.payload.task]: action.payload.metric}})),
|
||||
on(getDebugImagesMetrics, state => ({...state, optionalMetrics: initialState.optionalMetrics, debugImages: initialState.debugImages})),
|
||||
|
||||
on(setTimeIsNow, (state, action) => ({...state, timeIsNow: {...state.timeIsNow, [action.task]: action.timeIsNow}})),
|
||||
on(fetchExperiments, () => ({...initialState})),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
||||
26
src/app/webapp-common/debug-images/debug-images-types.ts
Normal file
26
src/app/webapp-common/debug-images/debug-images-types.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export interface DebugSampleEvent {
|
||||
timestamp: number;
|
||||
type?: string;
|
||||
task?: string;
|
||||
iter?: number;
|
||||
metric?: string;
|
||||
variant?: string;
|
||||
key?: string;
|
||||
url?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
'@timestamp'?: string;
|
||||
worker?: string;
|
||||
variantAndMetric?: string;
|
||||
}
|
||||
|
||||
export interface Iteration {
|
||||
events: DebugSampleEvent[];
|
||||
iter: number;
|
||||
}
|
||||
|
||||
export interface DebugSamples {
|
||||
metrics: string[];
|
||||
metric: string;
|
||||
scrollId: string;
|
||||
data: Iteration[];
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Component, Input, Output} from '@angular/core';
|
||||
import {EventEmitter} from '@angular/core';
|
||||
import {Iteration, Event} from '@common/debug-images/debug-images.component';
|
||||
import {ThemeEnum} from '@common/constants';
|
||||
import {DebugSampleEvent, Iteration} from '@common/debug-images/debug-images-types';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-debug-images-view',
|
||||
@@ -23,9 +23,9 @@ export class DebugImagesViewComponent {
|
||||
@Output() imageClicked = new EventEmitter();
|
||||
@Output() refreshClicked = new EventEmitter();
|
||||
@Output() createEmbedCode = new EventEmitter<{metrics?: string[]; variants?: string[]; domRect: DOMRect}>();
|
||||
@Output() urlError = new EventEmitter();
|
||||
@Output() urlError = new EventEmitter<{ frame: DebugSampleEvent; experimentId: string }>();
|
||||
|
||||
public imageUrlError(data: { frame: Event; experimentId: string }) {
|
||||
public imageUrlError(data: { frame: DebugSampleEvent; experimentId: string }) {
|
||||
this.urlError.emit(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="p-3 images-container">
|
||||
<div class="single-debug-images-container"
|
||||
*ngFor="let experimentId of experimentIds?.slice(0,LIMITED_VIEW_LIMIT); trackBy: trackExperiment;
|
||||
*ngFor="let experimentId of experimentIds | slice : 0 : LIMITED_VIEW_LIMIT; trackBy: trackExperiment;
|
||||
let first = first; let last = last"
|
||||
[class.separator]="experimentIds?.length > 1">
|
||||
<header *ngIf="experimentIds?.length > 1">
|
||||
@@ -9,15 +9,14 @@
|
||||
[experiment]="experiments | itemById: experimentId"
|
||||
[tags]="(experiments | itemById: experimentId)?.tags"
|
||||
(copyIdClicked)="copyIdToClipboard()"
|
||||
>
|
||||
</sm-experiment-compare-general-data>
|
||||
></sm-experiment-compare-general-data>
|
||||
</header>
|
||||
<div class="navigator-container" [class.active]="bindNavigationMode" [class.first]="first"
|
||||
[class.last]="last" [style.display]="isDarkTheme ? 'none' : 'flex'">
|
||||
<div class="connector-icon-container pointer" smTooltip="Sync browsing" [matTooltipShowDelay]="500"
|
||||
<div data-id="syncBrowsing" class="connector-icon-container pointer" smTooltip="Sync browsing" [matTooltipShowDelay]="500"
|
||||
[class.active]="bindNavigationMode" [class.hidden]="last" (click)="toggleConnectNavigation()">
|
||||
<i class="al-icon" [class.al-ico-connect]="bindNavigationMode"
|
||||
[class.al-ico-disconnect]="!bindNavigationMode"></i></div>
|
||||
<i class="al-icon" [class.al-ico-connect]="bindNavigationMode" [class.al-ico-disconnect]="!bindNavigationMode"></i>
|
||||
</div>
|
||||
<div class="metric-bar" [class.minimized]="minimized"
|
||||
*ngIf="!thereAreNoMetrics(experimentId) && !disableStatusRefreshFilter" data-id="metricBar">
|
||||
<label data-id="metricText">Metric:</label>
|
||||
@@ -34,7 +33,7 @@
|
||||
</mat-form-field>
|
||||
<label data-id="IterationText">Iterations:</label>
|
||||
|
||||
<div [ngClass]="{'disabled': (beginningOfTime$| async)[experimentId]}"
|
||||
<div [ngClass]="{'disabled': (beginningOfTime$| async)?.[experimentId]}"
|
||||
(click)="nextBatch({task: experimentId, metric: metricSelect.value})"
|
||||
class="al-icon al-ico-next-batch al-color blue-300"
|
||||
smTooltip="Older images" data-id="OlderImages"></div>
|
||||
@@ -43,23 +42,23 @@
|
||||
<div class="al-icon al-ico-between al-color light-blue-grey"></div>
|
||||
<b>{{debugImages?.[experimentId]?.data?.[0].iter}}</b>
|
||||
|
||||
<div [ngClass]="{'disabled': (timeIsNow$| async)[experimentId]}"
|
||||
<div [ngClass]="{'disabled': (timeIsNow$| async)?.[experimentId]}"
|
||||
(click)="(!timeIsNow[experimentId]) && previousBatch({task: experimentId, metric: metricSelect.value})"
|
||||
class="al-icon al-ico-prev-batch al-color blue-300"
|
||||
smTooltip="Newer images" data-id="NewerImages"></div>
|
||||
|
||||
<div [ngClass]="{'disabled': (timeIsNow$| async)[experimentId] && !allowAutorefresh }"
|
||||
<div [ngClass]="{'disabled': (timeIsNow$| async)?.[experimentId] && !allowAutorefresh }"
|
||||
(click)="(!(timeIsNow[experimentId] && !allowAutorefresh)) && backToNow({task: experimentId, metric: metricSelect.value})"
|
||||
class="al-icon al-ico-back-to-top al-color blue-300"
|
||||
smTooltip="Newest samples" data-id="NewestSamples"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-images no-output" [class.dark]="isDarkTheme" *ngIf="shouldShowNoImagesForExperiment(experimentId)">
|
||||
<svg class="mb-3 w-100" xmlns="http://www.w3.org/2000/svg" width="400" height="200" viewBox="0 0 300 150">
|
||||
<svg class="mb-3" xmlns="http://www.w3.org/2000/svg" width="200" height="100" viewBox="0 0 300 150">
|
||||
<path opacity="0.1"
|
||||
d="M72.67,79.36a5.39,5.39,0,0,1-1.45,4.32,1.17,1.17,0,0,1-.6.34,1.13,1.13,0,0,1-1.31-.88,1.11,1.11,0,0,1,.28-1,3.19,3.19,0,0,0,.89-2.36c-.22-1.09-1.66-1.73-1.68-1.74A1.12,1.12,0,0,1,69.65,76C69.76,76,72.22,77.07,72.67,79.36ZM46.19,78.1a1.38,1.38,0,0,0-1.06,1.61L47.3,90.38a1.38,1.38,0,0,0,1.61,1.06l4-.82L50.19,77.29Zm30.87.39c-.91-4.54-5.23-7.07-5.41-7.18a1.13,1.13,0,1,0-1.16,1.94s3.63,2.13,4.34,5.68-1.8,7-1.82,7a1.12,1.12,0,0,0,.25,1.56,1.11,1.11,0,0,0,.86.2,1.12,1.12,0,0,0,.68-.43C75,87.09,78,83,77.06,78.49Zm-3.57-12a1.12,1.12,0,0,0-1,2,12.48,12.48,0,0,1,2.91,2.24,13.87,13.87,0,0,1,3.88,7.28,14.19,14.19,0,0,1-.78,8.27,13,13,0,0,1-1.83,3.22,1.11,1.11,0,0,0,1.06,1.82h0a1,1,0,0,0,.63-.36,16.47,16.47,0,0,0,3.13-13.38A16.18,16.18,0,0,0,73.49,66.53Zm-9.28,1a1.35,1.35,0,0,0-1.6-1.06h0a1.45,1.45,0,0,0-.71.4L52.2,76.93l2.7,13.24,12.82,5.52a1.36,1.36,0,0,0,1.79-.7,1.33,1.33,0,0,0,.09-.81ZM261.4,76.66l-3.77,21.62-8.28-8.4-.47,2.7a4.13,4.13,0,0,1-4.76,3.35l-25.67-4.48a4.14,4.14,0,0,1-3.35-4.76l2.36-13.51c0-.12,0-.24.08-.35a6.17,6.17,0,1,1,10-3.71,5.62,5.62,0,0,1-.5,1.55l4.49.79a6.37,6.37,0,0,1,.06-1.63,6.15,6.15,0,1,1,11.63,3.65l4.63.81a4,4,0,0,1,3.4,4.53c0,.08,0,.15-.05.23l-.48,2.7ZM225.28,68.1a3.7,3.7,0,0,0-3.52-3.87h-.19a3.79,3.79,0,1,0,3.71,3.87Zm16.29,3A3.7,3.7,0,0,0,238,67.24h-.19a3.79,3.79,0,1,0,3.72,3.86Zm53.2-20.19L281,129.18a17.25,17.25,0,0,1-20,14l-78.27-13.81a17,17,0,0,1-11.13-7.08,17.86,17.86,0,0,1-1-1.69H130.44a17.86,17.86,0,0,1-1,1.69,17,17,0,0,1-11.13,7.08L40.09,143.16a17.25,17.25,0,0,1-20-14L6.26,50.86a17.26,17.26,0,0,1,14-20h0L94.63,17.77A17.24,17.24,0,0,1,110.76,6.58h79.47a17.24,17.24,0,0,1,16.13,11.19l74.38,13.11a17.25,17.25,0,0,1,14,20ZM120.22,119.58h-9.47a17.21,17.21,0,0,1-17.19-17.19V25.1L21.81,37.75a9.55,9.55,0,0,0-7.74,11.06l13.76,78.06a9.57,9.57,0,0,0,11.06,7.71L117,120.81A9.68,9.68,0,0,0,120.22,119.58Zm69.82-7a9.54,9.54,0,0,0,9.52-9.52V24.13a9.53,9.53,0,0,0-9.5-9.55h-79a9.51,9.51,0,0,0-9.51,9.51v79a9.53,9.53,0,0,0,9.51,9.52Zm95.52-70.94a9.49,9.49,0,0,0-6.17-3.93L207.56,25.1v77.33a17.23,17.23,0,0,1-17.21,17.15h-9.49a9.66,9.66,0,0,0,3.28,1.23l78.15,13.77a9.58,9.58,0,0,0,11.07-7.75l13.78-78.06a9.44,9.44,0,0,0-1.58-7.09Zm-109,.43c1.09.64,1,2.34,1,2.34l-.14,37.87a3.33,3.33,0,0,1-3.36,3.3h0l-48.13-.16a3.37,3.37,0,0,1-3.37-3.35l.2-37.39a3.3,3.3,0,0,1,2.11-3l.18-.08h49.68s1.76-.15,1.83.53ZM160.49,53.34a3.8,3.8,0,0,0,7.59,0,3.7,3.7,0,0,0-3.67-3.76h-.12A3.79,3.79,0,0,0,160.49,53.34ZM171.6,74.93l-8.55-9.46-.19-.19a2.49,2.49,0,0,0-3.52.16v0l-4.2,4.63-9.19-10.63a3.05,3.05,0,0,0-1.59-1,2.75,2.75,0,0,0-2.66.95h0l-13.13,15v4.13l43,.14Z"/>
|
||||
</svg>
|
||||
<h3>NO DEBUG SAMPLES</h3>
|
||||
<h4>NO DEBUG SAMPLES</h4>
|
||||
</div>
|
||||
<sm-debug-images-view
|
||||
*ngIf="debugImages?.[experimentId]?.data"
|
||||
@@ -70,10 +69,9 @@
|
||||
[isDarkTheme]="isDarkTheme"
|
||||
[isDatasetVersionPreview]="disableStatusRefreshFilter"
|
||||
(imageClicked)="imageClicked($event, experimentId)"
|
||||
(urlError)="urlError()"
|
||||
(urlError)="urlError($event)"
|
||||
(createEmbedCode)="createEmbedCode($event, experimentId)"
|
||||
>
|
||||
</sm-debug-images-view>
|
||||
></sm-debug-images-view>
|
||||
</div>
|
||||
<div *ngIf="experimentIds?.length>LIMITED_VIEW_LIMIT"
|
||||
class="limit-message-container">
|
||||
|
||||
@@ -110,9 +110,9 @@ sm-debug-images-view {
|
||||
left: 50%;
|
||||
transform: translateY(-50%) translateX(-50%);
|
||||
display: block;
|
||||
color: $cloudy-blue-two;
|
||||
font-weight: 500;
|
||||
color: $blue-100;
|
||||
line-height: 1.2;
|
||||
h4 {color: $blue-100;}
|
||||
|
||||
&.dark {
|
||||
color: $blue-250;
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
SimpleChanges
|
||||
} from '@angular/core';
|
||||
import {ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges} from '@angular/core';
|
||||
import {combineLatest, Observable, Subscription} from 'rxjs';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {AdminService} from '~/shared/services/admin.service';
|
||||
import {selectS3BucketCredentials} from '../core/reducers/common-auth-reducer';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import * as debugActions from './debug-images-actions';
|
||||
@@ -22,51 +10,30 @@ import {
|
||||
selectDebugImages,
|
||||
selectNoMore,
|
||||
selectOptionalMetrics,
|
||||
selectSelectedMetricForTask,
|
||||
selectTaskNames,
|
||||
selectTimeIsNow
|
||||
} from './debug-images-reducer';
|
||||
import {selectRouterParams} from '../core/reducers/router-reducer';
|
||||
import {distinctUntilChanged, filter, map, withLatestFrom} from 'rxjs/operators';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {ActivatedRoute, Params} from '@angular/router';
|
||||
import {TaskStatusEnum} from '~/business-logic/model/tasks/taskStatusEnum';
|
||||
import {ImageViewerComponent} from '../shared/debug-sample/image-viewer/image-viewer.component';
|
||||
import {selectSelectedExperiment} from '~/features/experiments/reducers';
|
||||
import {TaskMetric} from '~/business-logic/model/events/taskMetric';
|
||||
import {isEqual} from 'lodash-es';
|
||||
import {ALL_IMAGES} from './debug-images-effects';
|
||||
import {getSignedUrl} from '../core/actions/common-auth.actions';
|
||||
import {getSignedUrl, removeSignedUrl} from '../core/actions/common-auth.actions';
|
||||
import {addMessage} from '../core/actions/layout.actions';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
import {selectBeginningOfTime} from '@common/shared/debug-sample/debug-sample.reducer';
|
||||
import {LIMITED_VIEW_LIMIT} from '@common/experiments-compare/experiments-compare.constants';
|
||||
import {ReportCodeEmbedService} from '~/shared/services/report-code-embed.service';
|
||||
|
||||
export interface Event {
|
||||
timestamp: number;
|
||||
type?: string;
|
||||
task?: string;
|
||||
iter?: number;
|
||||
metric?: string;
|
||||
variant?: string;
|
||||
key?: string;
|
||||
url?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
'@timestamp'?: string;
|
||||
worker?: string;
|
||||
}
|
||||
|
||||
export interface Iteration {
|
||||
events: Event[];
|
||||
iter: number;
|
||||
}
|
||||
|
||||
interface DebugSamples {
|
||||
metrics: string[];
|
||||
metric: string;
|
||||
scrollId: string;
|
||||
data: Iteration[];
|
||||
}
|
||||
import {EventsDebugImagesResponse} from '~/business-logic/model/events/eventsDebugImagesResponse';
|
||||
import {DebugSampleEvent, DebugSamples} from '@common/debug-images/debug-images-types';
|
||||
import {concatLatestFrom} from '@ngrx/effects';
|
||||
import {isGoogleCloudUrl} from '@common/settings/admin/base-admin-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-debug-images',
|
||||
@@ -87,11 +54,10 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
private refreshingSubscription: Subscription;
|
||||
private optionalMetricsSubscription: Subscription;
|
||||
|
||||
public debugImages$: Observable<any>;
|
||||
private routerParams$: Observable<any>;
|
||||
private routerParams$: Observable<Params>;
|
||||
private tasks$: Observable<Partial<Task>[]>;
|
||||
public timeIsNow$: Observable<any>;
|
||||
public beginningOfTime$: Observable<any>;
|
||||
public timeIsNow$: Observable<boolean>;
|
||||
public beginningOfTime$: Observable<boolean>;
|
||||
|
||||
public mergeIterations: boolean;
|
||||
public debugImages: { [experimentId: string]: DebugSamples } = null;
|
||||
@@ -103,9 +69,9 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
public optionalMetrics$: Observable<ITaskOptionalMetrics[]>;
|
||||
public optionalMetrics: { [experimentId: string]: string };
|
||||
public selectedMetrics: { [taskId: string]: string } = {};
|
||||
public beginningOfTime: any;
|
||||
public beginningOfTime: boolean;
|
||||
private beginningOfTimeSubscription: Subscription;
|
||||
public timeIsNow: any;
|
||||
public timeIsNow: boolean;
|
||||
private timeIsNowSubscription: Subscription;
|
||||
minimized: boolean;
|
||||
readonly allImages = ALL_IMAGES;
|
||||
@@ -118,7 +84,6 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
|
||||
constructor(
|
||||
private store: Store,
|
||||
private adminService: AdminService,
|
||||
private dialog: MatDialog,
|
||||
private changeDetection: ChangeDetectorRef,
|
||||
private activeRoute: ActivatedRoute,
|
||||
@@ -135,10 +100,11 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
|
||||
this.debugImagesSubscription = combineLatest([
|
||||
store.select(selectS3BucketCredentials),
|
||||
store.select(selectDebugImages)
|
||||
store.select(selectDebugImages),
|
||||
store.select(selectSelectedMetricForTask)
|
||||
])
|
||||
.pipe(
|
||||
map(([, debugImages]) => !debugImages ? {} : Object.entries(debugImages).reduce(((acc, val: any) => {
|
||||
map(([, debugImages, metricForTask]) => !debugImages ? {} : Object.entries(debugImages).reduce(((acc, val: [string, EventsDebugImagesResponse]) => {
|
||||
const id = val[0];
|
||||
const iterations = val[1].metrics.find(m => m.task === id).iterations;
|
||||
if (iterations?.length === 0) {
|
||||
@@ -152,12 +118,12 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
return {
|
||||
...event,
|
||||
url: event.url,
|
||||
variantAndMetric: this.selectedMetric === ALL_IMAGES ? `${event.metric}/${event.variant}` : ''
|
||||
variantAndMetric: (this.selectedMetric === ALL_IMAGES || metricForTask[id] === ALL_IMAGES ) ? `${event.metric}/${event.variant}` : ''
|
||||
};
|
||||
})
|
||||
}))
|
||||
};
|
||||
acc[id].metrics = val[1].metrics.map(metric => metric.metric || metric.iterations[0].events[0].metric);
|
||||
acc[id].metrics = val[1].metrics.map((metric: any) => metric.metric || metric.iterations[0].events[0].metric);
|
||||
acc[id].metric = acc[id].metrics[0];
|
||||
acc[id].scrollId = val[1].scroll_id;
|
||||
return acc;
|
||||
@@ -205,6 +171,7 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
if (multipleExperiments) {
|
||||
this.routerParamsSubscription = this.routerParams$
|
||||
.subscribe(params => {
|
||||
this.selectedMetric = null;
|
||||
this.experimentIds = (params.ids ? params.ids.split(',') : params.experimentId.split(','));
|
||||
this.store.dispatch(getDebugImagesMetrics({tasks: this.experimentIds.slice(0, LIMITED_VIEW_LIMIT)}));
|
||||
this.store.dispatch(fetchExperiments({tasks: this.experimentIds.slice(0, LIMITED_VIEW_LIMIT)}));
|
||||
@@ -234,10 +201,13 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
this.selectedExperimentSubscription = this.store.select(selectSelectedExperiment)
|
||||
.pipe(
|
||||
filter(experiment => !!experiment && !this.disableStatusRefreshFilter),
|
||||
distinctUntilChanged((previous, current) => previous?.id === current?.id)
|
||||
).subscribe(experiment => {
|
||||
distinctUntilChanged((previous, current) => previous?.id === current?.id),
|
||||
withLatestFrom(this.store.select(selectSelectedMetricForTask))
|
||||
).subscribe(([experiment, metricForTask]) => {
|
||||
this.selectedMetric = null;
|
||||
this.experimentNames = {[experiment.id]: experiment.name};
|
||||
this.experimentIds = [experiment.id];
|
||||
this.selectedMetrics[experiment.id] = metricForTask[experiment.id] ;
|
||||
this.store.dispatch(getDebugImagesMetrics({tasks: this.experimentIds}));
|
||||
});
|
||||
}
|
||||
@@ -245,19 +215,16 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
|
||||
this.refreshingSubscription = this.refresh.tick
|
||||
.pipe(
|
||||
filter(auto =>
|
||||
auto !== null && (!this.disableStatusRefreshFilter || this.selected.status === TaskStatusEnum.InProgress)
|
||||
),
|
||||
withLatestFrom(
|
||||
this.store.select(selectTimeIsNow),
|
||||
)
|
||||
filter(auto => auto !== null && !this.disableStatusRefreshFilter),
|
||||
concatLatestFrom(() => this.store.select(selectTimeIsNow))
|
||||
)
|
||||
.subscribe(([auto, timeIsNow]) => {
|
||||
if (multipleExperiments) {
|
||||
this.store.dispatch(debugActions.refreshDebugImagesMetrics({tasks: this.experimentIds, autoRefresh: auto}));
|
||||
}
|
||||
this.store.dispatch(getDebugImagesMetrics({tasks: this.experimentIds}));
|
||||
this.experimentIds.forEach(experimentId => {
|
||||
if (experimentId && timeIsNow?.[experimentId] && this.debugImages[experimentId] && this.elRef.nativeElement.scrollTop < 40) {
|
||||
if (experimentId && !(timeIsNow?.[experimentId] === false) && this.elRef.nativeElement.scrollTop < 40) {
|
||||
this.store.dispatch(debugActions.refreshMetric({
|
||||
payload: {
|
||||
task: experimentId,
|
||||
@@ -269,19 +236,22 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
});
|
||||
});
|
||||
|
||||
this.optionalMetricsSubscription = this.optionalMetrics$.subscribe(optionalMetrics => {
|
||||
this.optionalMetricsSubscription = this.optionalMetrics$.pipe(concatLatestFrom(()=> [this.store.select(selectSelectedMetricForTask)]))
|
||||
.subscribe(([optionalMetrics, selectedMetricForTask ]) => {
|
||||
const optionalMetricsDic = {};
|
||||
optionalMetrics.forEach(experimentMetrics => optionalMetricsDic[experimentMetrics.task] = experimentMetrics.metrics);
|
||||
if ((!isEqual(this.optionalMetrics, optionalMetricsDic)) && optionalMetrics.length > 0) {
|
||||
this.optionalMetrics = optionalMetricsDic;
|
||||
optionalMetrics.forEach(optionalMetric => {
|
||||
optionalMetric.metrics[0] && this.store.dispatch(debugActions.setSelectedMetric({
|
||||
payload: {
|
||||
task: optionalMetric.task,
|
||||
metric: optionalMetric.metrics[0]
|
||||
}
|
||||
}));
|
||||
});
|
||||
if (!this.selectedMetric || !optionalMetrics?.[0].metrics.includes(this.selectedMetric)) {
|
||||
optionalMetrics.forEach(optionalMetric => {
|
||||
optionalMetric.metrics[0] && this.store.dispatch(debugActions.setSelectedMetric({
|
||||
payload: {
|
||||
task: optionalMetric.task,
|
||||
metric: selectedMetricForTask[optionalMetric.task] ?? optionalMetric.metrics[0]
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
this.changeDetection.detectChanges();
|
||||
}
|
||||
});
|
||||
@@ -300,7 +270,12 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
this.store.dispatch(resetDebugImages());
|
||||
}
|
||||
|
||||
public urlError(/*{frame}*/) {
|
||||
public urlError({frame}: { frame: DebugSampleEvent; experimentId: string }) {
|
||||
const url = frame.url;
|
||||
if (isGoogleCloudUrl(url)) {
|
||||
this.store.dispatch(removeSignedUrl({url}));
|
||||
this.store.dispatch(getSignedUrl({url}));
|
||||
}
|
||||
// this.adminService.checkImgUrl(frame.oldSrc || frame.src);
|
||||
}
|
||||
|
||||
@@ -331,7 +306,7 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
return experimentID;
|
||||
}
|
||||
|
||||
selectMetric(change: any, task) {
|
||||
selectMetric(change: {value: string}, taskId) {
|
||||
this.selectedMetric = change.value;
|
||||
if (this.bindNavigationMode) {
|
||||
this.experimentIds.forEach(experimentId => {
|
||||
@@ -339,8 +314,8 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
this.store.dispatch(debugActions.setSelectedMetric({payload: {task: experimentId, metric: change.value}}));
|
||||
});
|
||||
} else {
|
||||
this.selectedMetrics[task] = change.value;
|
||||
this.store.dispatch(debugActions.setSelectedMetric({payload: {task, metric: change.value}}));
|
||||
this.selectedMetrics[taskId] = change.value;
|
||||
this.store.dispatch(debugActions.setSelectedMetric({payload: {task: taskId, metric: change.value}}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,16 +369,16 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
|
||||
}
|
||||
|
||||
thereAreNoMetrics(experiment) {
|
||||
return !(this.optionalMetrics && this.optionalMetrics[experiment] && this.optionalMetrics[experiment].length > 0);
|
||||
thereAreNoMetrics(experiment: string) {
|
||||
return !(this.optionalMetrics?.[experiment]?.length > 0);
|
||||
}
|
||||
|
||||
thereAreNoDebugImages(experiment) {
|
||||
return !(this.debugImages && this.debugImages[experiment] && this.debugImages[experiment].data?.length > 0);
|
||||
thereAreNoDebugImages(experiment: string) {
|
||||
return !(this.debugImages?.[experiment]?.data?.length > 0);
|
||||
}
|
||||
|
||||
shouldShowNoImagesForExperiment(experiment: string) {
|
||||
return (this.thereAreNoMetrics(experiment) && this.optionalMetrics && this.optionalMetrics[experiment]) || (this.thereAreNoDebugImages(experiment) && this.debugImages && this.debugImages[experiment]);
|
||||
return this.thereAreNoMetrics(experiment) && this.thereAreNoDebugImages(experiment);
|
||||
}
|
||||
|
||||
copyIdToClipboard() {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {createAction, props} from '@ngrx/store';
|
||||
import {Task} from '~/business-logic/model/tasks/task';
|
||||
import {Params} from '@angular/router';
|
||||
import {ISmCol} from '../../shared/ui-components/data/table/table.consts';
|
||||
import {SortMeta} from 'primeng/api';
|
||||
import {TableFilter} from '../../shared/utils/tableParamEncode';
|
||||
import {EntityTypeEnum} from '~/shared/constants/non-common-consts';
|
||||
import {ITableExperiment} from '@common/experiments/shared/common-experiment-model.model';
|
||||
|
||||
export const EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ = 'EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_';
|
||||
|
||||
@@ -14,6 +14,8 @@ export const SET_SELECT_EXPERIMENTS_FOR_COMPARE = EXPERIMENTS_COMPARE_SELECT_EXP
|
||||
export const RESET_SELECT_EXPERIMENT_FOR_COMPARE = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + 'RESET_SELECT_EXPERIMENT_FOR_COMPARE';
|
||||
export const TOGGLE_SHOW_SACLARS_OPTIONS = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + 'TOGGLE_SHOW_SACLARS_OPTIONS';
|
||||
export const SET_HIDE_IDENTICAL_ROWS = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + 'SET_HIDE_IDENTICAL_ROWS';
|
||||
export const SET_SHOW_ROW_EXTREMES = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + 'SET_SHOW_ROW_EXTREMES';
|
||||
export const SET_SHOW_GLOBAL_LEGEND = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + 'SET_SHOW_GLOBAL_LEGEND';
|
||||
export const SET_REFRESHING = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + 'SET_REFRESHING';
|
||||
export const SET_EXPERIMENTS_UPDATE_TIME = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + 'SET_EXPERIMENTS_UPDATE_TIME';
|
||||
export const REFRESH_IF_NEEDED = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + 'REFRESH_IF_NEEDED';
|
||||
@@ -22,10 +24,12 @@ export const SET_NAVIGATION_PREFERENCES = EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_
|
||||
|
||||
|
||||
export const setHideIdenticalFields = createAction(SET_HIDE_IDENTICAL_ROWS, props<{payload: boolean}>());
|
||||
export const setShowRowExtremes = createAction(SET_SHOW_ROW_EXTREMES, props<{payload: boolean}>());
|
||||
export const setShowGlobalLegend = createAction(SET_SHOW_GLOBAL_LEGEND);
|
||||
export const setExperimentsUpdateTime = createAction(SET_EXPERIMENTS_UPDATE_TIME, props<{ payload: {[key: string]: Date}}>());
|
||||
export const refreshIfNeeded = createAction(REFRESH_IF_NEEDED, props<{ payload: boolean; autoRefresh?: boolean; entityType: string }>());
|
||||
export const toggleShowScalarOptions = createAction(TOGGLE_SHOW_SACLARS_OPTIONS);
|
||||
export const setSearchExperimentsForCompareResults = createAction(SET_SELECT_EXPERIMENTS_FOR_COMPARE, props<{ payload: Array<Task> }>());
|
||||
export const setSearchExperimentsForCompareResults = createAction(SET_SELECT_EXPERIMENTS_FOR_COMPARE, props<{ payload: ITableExperiment[] }>());
|
||||
export const setShowSearchExperimentsForCompare = createAction(SET_SHOW_SEARCH_EXPERIMENTS_FOR_COMPARE, props<{ payload: boolean }>());
|
||||
export const resetSelectCompareHeader = createAction(RESET_SELECT_EXPERIMENT_FOR_COMPARE, props<{fullReset?: boolean}>());
|
||||
export const getSelectedExperimentsForCompareAddDialog = createAction(GET_SELECTED_EXPERIMENTS_FOR_COMPARE, props<{tasksIds?: string[]}>());
|
||||
@@ -52,8 +56,12 @@ export const compareAddDialogSetTableSort = createAction(
|
||||
props<{ orders: SortMeta[]; projectId: string; colIds: string[] }>()
|
||||
);
|
||||
export const refetchExperimentRequested = createAction(REFETCH_EXPERIMENT_REQUESTED, props<{ autoRefresh: boolean; entity: EntityTypeEnum }>());
|
||||
export const setNavigationPreferences = createAction(SET_NAVIGATION_PREFERENCES, props<{ navigationPreferences: Params }>());
|
||||
export const setAddTableViewArchived = createAction(
|
||||
EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + '[show archived in add table]',
|
||||
props<{show: boolean}>()
|
||||
);
|
||||
|
||||
export const setExportTable = createAction(
|
||||
EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + '[set export table]',
|
||||
props<{export: boolean}>()
|
||||
);
|
||||
|
||||
@@ -3,18 +3,22 @@ import {ScalarKeyEnum} from '~/business-logic/model/events/scalarKeyEnum';
|
||||
import {EventsGetMultiTaskPlotsResponse} from '~/business-logic/model/events/eventsGetMultiTaskPlotsResponse';
|
||||
import {ExperimentCompareSettings} from '@common/experiments-compare/reducers/experiments-compare-charts.reducer';
|
||||
import {EntityTypeEnum} from '~/shared/constants/non-common-consts';
|
||||
import {EventsGetTaskSingleValueMetricsResponse} from '~/business-logic/model/events/eventsGetTaskSingleValueMetricsResponse';
|
||||
import {EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_} from '@common/experiments-compare/actions/compare-header.actions';
|
||||
import {ChartHoverModeEnum} from "@common/experiments/shared/common-experiments.const";
|
||||
|
||||
|
||||
export const EXPERIMENTS_COMPARE_METRICS_CHARTS_ = 'EXPERIMENTS_COMPARE_METRICS_CHARTS_';
|
||||
|
||||
// COMMANDS:
|
||||
export const SET_EXPERIMENT_PLOTS = EXPERIMENTS_COMPARE_METRICS_CHARTS_ + 'SET_EXPERIMENT_PLOTS';
|
||||
|
||||
// EVENTS:
|
||||
|
||||
|
||||
export const getMultiScalarCharts = createAction(
|
||||
EXPERIMENTS_COMPARE_METRICS_CHARTS_ + 'GET_MULTI_SCALAR_CHARTS',
|
||||
props<{ taskIds: string[]; entity: EntityTypeEnum; autoRefresh?: boolean; xAxisType: ScalarKeyEnum }>()
|
||||
);
|
||||
|
||||
export const getMultiSinleScalars = createAction(
|
||||
EXPERIMENTS_COMPARE_METRICS_CHARTS_ + 'GET_MULTI_SINGLE_SCALAR_CHARTS',
|
||||
props<{ taskIds: string[]; entity: EntityTypeEnum; autoRefresh?: boolean; cached?: boolean }>()
|
||||
);
|
||||
|
||||
@@ -23,6 +27,11 @@ export const getMultiPlotCharts = createAction(
|
||||
props<{ taskIds: Array<string>; entity: EntityTypeEnum; autoRefresh?: boolean }>()
|
||||
);
|
||||
|
||||
export const setExperimentMultiScalarSingleValue = createAction(
|
||||
EXPERIMENTS_COMPARE_METRICS_CHARTS_ + 'SET_MULTI_SINGLE_SCALAR_CHARTS',
|
||||
props<EventsGetTaskSingleValueMetricsResponse>()
|
||||
);
|
||||
|
||||
export const setSelectedExperiments = createAction(
|
||||
EXPERIMENTS_COMPARE_METRICS_CHARTS_ + 'SET_SELECTED_EXPERIMENTS',
|
||||
props<{selectedExperiments: string[]}>()
|
||||
@@ -54,3 +63,17 @@ export const setExperimentMetricsSearchTerm = createAction(
|
||||
);
|
||||
|
||||
export const resetExperimentMetrics = createAction(EXPERIMENTS_COMPARE_METRICS_CHARTS_ + 'RESET_EXPERIMENT_METRICS');
|
||||
|
||||
export const getGlobalLegendData = createAction(
|
||||
EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + '[get global legend data]',
|
||||
props<{ids: string[], entity: EntityTypeEnum}>()
|
||||
);
|
||||
export const setGlobalLegendData = createAction(
|
||||
EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + '[set global legend data]',
|
||||
props<{data: {name: string, tags: string[], systemTags: string[], id: string, project: {id: string}}[]}>()
|
||||
);
|
||||
|
||||
export const setScalarsHoverMode = createAction(
|
||||
EXPERIMENTS_COMPARE_SELECT_EXPERIMENT_ + 'SET SCALARS HOVER MODE',
|
||||
props<{ hoverMode: ChartHoverModeEnum }>()
|
||||
);
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
import {createAction, props} from '@ngrx/store';
|
||||
import {createActionGroup, props, emptyProps} from '@ngrx/store';
|
||||
import {ExperimentParams} from '../shared/experiments-compare-details.model';
|
||||
import {EntityTypeEnum} from '~/shared/constants/non-common-consts';
|
||||
|
||||
export const resetState = createAction('[experiment compare params] RESET_STATE');
|
||||
export const setExperiments = createAction('[experiment compare params] SET_EXPERIMENTS', props<{experiments: ExperimentParams[]}>());
|
||||
export const experimentListUpdated = createAction('[experiment compare params] EXPERIMENT_LIST_UPDATED', props<{ids: string[]; entity: EntityTypeEnum}>());
|
||||
export const paramsActions = createActionGroup({
|
||||
source: 'experiment compare params',
|
||||
events: {
|
||||
'reset state': emptyProps(),
|
||||
'set experiments': props<{experiments: ExperimentParams[]}>(),
|
||||
'experiment list updated': props<{ids: string[]; entity: EntityTypeEnum}>(),
|
||||
'set view': props<{primary: string; secondary: string}>()
|
||||
}
|
||||
})
|
||||
// export const resetState = createAction('[experiment compare params] RESET_STATE');
|
||||
// export const setExperiments = createAction('[experiment compare params] SET_EXPERIMENTS', props<{experiments: ExperimentParams[]}>());
|
||||
// export const experimentListUpdated = createAction('[experiment compare params] EXPERIMENT_LIST_UPDATED', props<{ids: string[]; entity: EntityTypeEnum}>());
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import {CanActivateFn, createUrlTreeFromSnapshot} from '@angular/router';
|
||||
import {inject} from '@angular/core';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectViewMode} from '@common/experiments-compare/reducers';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
export const compareNavigationGuard: CanActivateFn = route => {
|
||||
const store = inject(Store);
|
||||
const currentPage = route?.url?.[0]?.path;
|
||||
|
||||
return store.select(selectViewMode(currentPage))
|
||||
.pipe(
|
||||
map(mode => createUrlTreeFromSnapshot(route, [mode]))
|
||||
);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user