mirror of
https://github.com/clearml/clearml-web
synced 2025-06-26 18:27:02 +00:00
V1.9.2 (#48)
* Release v1.9 * v1.9.2 fixes and improvements Co-authored-by: Shay Halsband <shy.halsband@gmail.com>
This commit is contained in:
335
package-lock.json
generated
335
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "clearml-webapp",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "clearml-webapp",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.2",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^14.2.12",
|
||||
"@angular/cdk": "^14.2.7",
|
||||
@@ -57,6 +57,7 @@
|
||||
"process": "^0.11.10",
|
||||
"rxjs": "^7.5.7",
|
||||
"string-to-color": "^2.2.2",
|
||||
"tinycolor2": "^1.5.2",
|
||||
"tslib": "^2.4.1",
|
||||
"url": "^0.11.0",
|
||||
"uuid": "^9.0.0",
|
||||
@@ -6806,16 +6807,6 @@
|
||||
"resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz",
|
||||
"integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y="
|
||||
},
|
||||
"node_modules/colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
@@ -9471,36 +9462,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/findup-sync": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
|
||||
"integrity": "sha512-z8Nrwhi6wzxNMIbxlrTzuUW6KWuKkogZ/7OdDVq+0+kxn77KUH1nipx8iU6suqkHqc4y6n7a9A8IpmxY/pTjWg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "~5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/findup-sync/node_modules/glob": {
|
||||
"version": "5.0.15",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
|
||||
"integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "2 || 3",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/flat-cache": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
|
||||
@@ -9540,15 +9501,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/font-awesome": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
||||
"integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.3"
|
||||
}
|
||||
},
|
||||
"node_modules/for-each": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||
@@ -10929,12 +10881,6 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/jquery": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz",
|
||||
"integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/js-sdsl": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
|
||||
@@ -12353,24 +12299,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/optimist": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
||||
"integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimist": "~0.0.1",
|
||||
"wordwrap": "~0.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/optimist/node_modules/minimist": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
|
||||
"integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||
@@ -12805,17 +12733,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/popper.js": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
|
||||
"deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.16",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
|
||||
@@ -15094,6 +15011,11 @@
|
||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tinycolor2": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.5.2.tgz",
|
||||
"integrity": "sha512-h80m9GPFGbcLzZByXlNSEhp1gf8Dy+VX/2JCGUZsWLo7lV1mnE/XlxGYgRBoMLJh1lIDXP0EMC4RPTjlRaV+Bg=="
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
|
||||
@@ -15174,38 +15096,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||
},
|
||||
"node_modules/tslint": {
|
||||
"version": "3.15.1",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-3.15.1.tgz",
|
||||
"integrity": "sha512-wkqXlDiU1qG31dMuxnCSNeNMdKmSaEMYgJ2RERgFkt1WvVEF/wYwUYR396DDDcJDDBYpq16a6XJodQh70IRtBQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"colors": "^1.1.2",
|
||||
"diff": "^2.2.1",
|
||||
"findup-sync": "~0.3.0",
|
||||
"glob": "^7.0.3",
|
||||
"optimist": "~0.6.0",
|
||||
"resolve": "^1.1.7",
|
||||
"underscore.string": "^3.3.4"
|
||||
},
|
||||
"bin": {
|
||||
"tslint": "bin/tslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=1.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/tslint/node_modules/diff": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz",
|
||||
"integrity": "sha512-9wfm3RLzMp/PyTFWuw9liEzdlxsdGixCW0ZTU1XDmtlAkvpVXTPGF8KnfSs0hm3BPbg19OrUPPsRkHXoREpP1g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/tsutils": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
|
||||
@@ -15298,20 +15188,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/underscore.string": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz",
|
||||
"integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"sprintf-js": "^1.1.1",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/unicode-canonical-property-names-ecmascript": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
|
||||
@@ -15881,16 +15757,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wordwrap": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
||||
"integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
@@ -16367,8 +16233,7 @@
|
||||
"version": "14.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-14.4.0.tgz",
|
||||
"integrity": "sha512-AhAUFvSg0urtb6Lsowvuxwu6DMXUy0BPwrnfNOBGjRt9vG7F9kgXXAsm5DnIS0GNy/mLZ9mSfa86fv++1e0KUA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"@angular-eslint/bundled-angular-compiler": {
|
||||
"version": "14.4.0",
|
||||
@@ -19243,15 +19108,13 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz",
|
||||
"integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"@csstools/selector-specificity": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz",
|
||||
"integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
@@ -19524,8 +19387,7 @@
|
||||
"version": "14.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.10.tgz",
|
||||
"integrity": "sha512-sLHapZLVub6mEz5b19tf1VfIV1w3tYfg7FNPLeni79aldxu1FbP1v2WmiFAnMzrswqyK0bhTtxrl+Z/CLKqyoQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
@@ -20168,15 +20030,13 @@
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
|
||||
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"acorn-jsx": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"adjust-sourcemap-loader": {
|
||||
"version": "4.0.0",
|
||||
@@ -20637,8 +20497,7 @@
|
||||
"bootstrap": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz",
|
||||
"integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==",
|
||||
"requires": {}
|
||||
"integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ=="
|
||||
},
|
||||
"bowser": {
|
||||
"version": "2.11.0",
|
||||
@@ -20951,13 +20810,6 @@
|
||||
"resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz",
|
||||
"integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y="
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
@@ -21297,8 +21149,7 @@
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz",
|
||||
"integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"css-select": {
|
||||
"version": "4.2.1",
|
||||
@@ -22579,8 +22430,7 @@
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz",
|
||||
"integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
@@ -22951,32 +22801,6 @@
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"findup-sync": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
|
||||
"integrity": "sha512-z8Nrwhi6wzxNMIbxlrTzuUW6KWuKkogZ/7OdDVq+0+kxn77KUH1nipx8iU6suqkHqc4y6n7a9A8IpmxY/pTjWg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"glob": "~5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "5.0.15",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
|
||||
"integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "2 || 3",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flat-cache": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
|
||||
@@ -22999,12 +22823,6 @@
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"dev": true
|
||||
},
|
||||
"font-awesome": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
||||
"integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==",
|
||||
"peer": true
|
||||
},
|
||||
"for-each": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||
@@ -23464,8 +23282,7 @@
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
|
||||
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
@@ -24009,12 +23826,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"jquery": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz",
|
||||
"integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==",
|
||||
"peer": true
|
||||
},
|
||||
"js-sdsl": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
|
||||
@@ -25074,26 +24885,6 @@
|
||||
"is-wsl": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
||||
"integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"minimist": "~0.0.1",
|
||||
"wordwrap": "~0.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
|
||||
"integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionator": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||
@@ -25434,12 +25225,6 @@
|
||||
"find-up": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"popper.js": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
|
||||
"peer": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.16",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
|
||||
@@ -25573,15 +25358,13 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz",
|
||||
"integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"postcss-gap-properties": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz",
|
||||
"integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"postcss-image-set-function": {
|
||||
"version": "4.0.7",
|
||||
@@ -25607,8 +25390,7 @@
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz",
|
||||
"integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"postcss-lab-function": {
|
||||
"version": "4.2.1",
|
||||
@@ -25635,22 +25417,19 @@
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz",
|
||||
"integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"postcss-media-minmax": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz",
|
||||
"integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"postcss-modules-extract-imports": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
|
||||
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"postcss-modules-local-by-default": {
|
||||
"version": "4.0.0",
|
||||
@@ -25710,8 +25489,7 @@
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz",
|
||||
"integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"postcss-place": {
|
||||
"version": "7.0.5",
|
||||
@@ -25792,8 +25570,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz",
|
||||
"integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"postcss-selector-not": {
|
||||
"version": "6.0.1",
|
||||
@@ -26346,8 +26123,7 @@
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@@ -26966,8 +26742,7 @@
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@@ -27023,6 +26798,11 @@
|
||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||
"dev": true
|
||||
},
|
||||
"tinycolor2": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.5.2.tgz",
|
||||
"integrity": "sha512-h80m9GPFGbcLzZByXlNSEhp1gf8Dy+VX/2JCGUZsWLo7lV1mnE/XlxGYgRBoMLJh1lIDXP0EMC4RPTjlRaV+Bg=="
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
|
||||
@@ -27087,31 +26867,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||
},
|
||||
"tslint": {
|
||||
"version": "3.15.1",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-3.15.1.tgz",
|
||||
"integrity": "sha512-wkqXlDiU1qG31dMuxnCSNeNMdKmSaEMYgJ2RERgFkt1WvVEF/wYwUYR396DDDcJDDBYpq16a6XJodQh70IRtBQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"colors": "^1.1.2",
|
||||
"diff": "^2.2.1",
|
||||
"findup-sync": "~0.3.0",
|
||||
"glob": "^7.0.3",
|
||||
"optimist": "~0.6.0",
|
||||
"resolve": "^1.1.7",
|
||||
"underscore.string": "^3.3.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"diff": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz",
|
||||
"integrity": "sha512-9wfm3RLzMp/PyTFWuw9liEzdlxsdGixCW0ZTU1XDmtlAkvpVXTPGF8KnfSs0hm3BPbg19OrUPPsRkHXoREpP1g==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
|
||||
@@ -27178,17 +26933,6 @@
|
||||
"which-boxed-primitive": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"underscore.string": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz",
|
||||
"integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"sprintf-js": "^1.1.1",
|
||||
"util-deprecate": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"unicode-canonical-property-names-ecmascript": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
|
||||
@@ -27394,8 +27138,7 @@
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@@ -27605,13 +27348,6 @@
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"dev": true
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
||||
"integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
@@ -27659,8 +27395,7 @@
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"xhr2": {
|
||||
"version": "0.2.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clearml-webapp",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.2",
|
||||
"license": "",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
@@ -68,6 +68,7 @@
|
||||
"process": "^0.11.10",
|
||||
"rxjs": "^7.5.7",
|
||||
"string-to-color": "^2.2.2",
|
||||
"tinycolor2": "^1.5.2",
|
||||
"tslib": "^2.4.1",
|
||||
"url": "^0.11.0",
|
||||
"uuid": "^9.0.0",
|
||||
|
||||
@@ -12,13 +12,20 @@ import {messagesReducer} from '@common/core/reducers/messages-reducer';
|
||||
import {projectsReducer, RootProjects} from '@common/core/reducers/projects.reducer';
|
||||
import {routerReducer} from '@common/core/reducers/router-reducer';
|
||||
import {SmSyncStateSelectorService} from '@common/core/services/sync-state-selector.service';
|
||||
import {EXPERIMENTS_COMPARE_METRICS_CHARTS_} from '@common/experiments-compare/actions/experiments-compare-charts.actions';
|
||||
import {
|
||||
EXPERIMENTS_COMPARE_METRICS_CHARTS_
|
||||
} from '@common/experiments-compare/actions/experiments-compare-charts.actions';
|
||||
import {compareSyncedKeys} from '@common/experiments-compare/experiments-compare.module';
|
||||
import {EXPERIMENTS_OUTPUT_PREFIX} from '@common/experiments/actions/common-experiment-output.actions';
|
||||
import {EXPERIMENTS_INFO_PREFIX} from '@common/experiments/actions/common-experiments-info.actions';
|
||||
import {EXPERIMENTS_PREFIX} from '@common/experiments/actions/common-experiments-view.actions';
|
||||
import {EXPERIMENTS_STORE_KEY} from '@common/experiments/shared/common-experiments.const';
|
||||
import {MODELS_PREFIX_INFO, MODELS_PREFIX_MENU, MODELS_PREFIX_VIEW, MODELS_STORE_KEY} from '@common/models/models.consts';
|
||||
import {
|
||||
MODELS_PREFIX_INFO,
|
||||
MODELS_PREFIX_MENU,
|
||||
MODELS_PREFIX_VIEW,
|
||||
MODELS_STORE_KEY
|
||||
} from '@common/models/models.consts';
|
||||
import {modelSyncedKeys} from '@common/models/models.module';
|
||||
import {PROJECTS_PREFIX} from '@common/projects/common-projects.consts';
|
||||
import {CHOOSE_COLOR_PREFIX} from '@common/shared/ui-components/directives/choose-color/choose-color.actions';
|
||||
@@ -42,6 +49,7 @@ import {usersReducer} from './reducers/users.reducer';
|
||||
import {viewReducer} from './reducers/view.reducer';
|
||||
import {UsageStatsService} from './services/usage-stats.service';
|
||||
import {extCoreModules} from '~/build-specifics';
|
||||
import {ReportCodeEmbedService} from '../shared/services/report-code-embed.service';
|
||||
|
||||
export const reducers = {
|
||||
auth: authReducer,
|
||||
@@ -83,16 +91,16 @@ export const localStorageReducer = (reducer: ActionReducer<any>): ActionReducer<
|
||||
if (action.type === '@ngrx/store/init') {
|
||||
const savedState = JSON.parse(localStorage.getItem(key));
|
||||
nextState = merge(nextState, savedState);
|
||||
}
|
||||
}
|
||||
if (state === nextState) {
|
||||
return nextState;
|
||||
}
|
||||
}
|
||||
if (actionsPrefix && !actionsPrefix.some(ap => action.type.startsWith(ap))) {
|
||||
return nextState;
|
||||
}
|
||||
}
|
||||
localStorage.setItem(key, JSON.stringify(pick(syncedKeys, nextState)));
|
||||
return nextState;
|
||||
};
|
||||
};
|
||||
|
||||
const userPrefMetaFactory = (userPreferences: UserPreferences): MetaReducer<any>[] => [
|
||||
(reducer: ActionReducer<any>) =>
|
||||
@@ -141,6 +149,7 @@ const userPrefMetaFactory = (userPreferences: UserPreferences): MetaReducer<any>
|
||||
SmSyncStateSelectorService,
|
||||
UsageStatsService,
|
||||
AdminService,
|
||||
ReportCodeEmbedService,
|
||||
{
|
||||
provide: USER_PROVIDED_META_REDUCERS,
|
||||
deps: [UserPreferences],
|
||||
|
||||
@@ -45,6 +45,7 @@ export class ProjectsEffects {
|
||||
return [
|
||||
actions.setSelectedProject({project: ALL_PROJECTS_OBJECT}),
|
||||
actions.getProjectUsers(action),
|
||||
actions.getCompanyTags(),
|
||||
deactivateLoader(action.type)];
|
||||
} else {
|
||||
this.fetchingExampleExperiment = action.example && action.projectId;
|
||||
|
||||
2
src/app/shared/services/report-code-embed.service.ts
Normal file
2
src/app/shared/services/report-code-embed.service.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export {ReportCodeEmbedBaseService as ReportCodeEmbedService} from '../../webapp-common/shared/services/report-code-embed-base.service';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
@font-face {
|
||||
font-family: '#{$icomoon-font-family}';
|
||||
src: url('./#{$icomoon-font-family}.ttf?f4pbms') format('truetype');
|
||||
src: url('./#{$icomoon-font-family}.ttf?s4ibjb') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
@@ -24,19 +24,19 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.al-ico-md-copy {
|
||||
.al-ico-markdown {
|
||||
&:before {
|
||||
content: $al-ico-md-copy;
|
||||
content: $al-ico-markdown;
|
||||
}
|
||||
}
|
||||
.al-ico-hor-expand {
|
||||
&:before {
|
||||
content: $al-ico-hor-expand;
|
||||
content: $al-ico-hor-expand;
|
||||
}
|
||||
}
|
||||
.al-ico-hor-minimize {
|
||||
&:before {
|
||||
content: $al-ico-hor-minimize;
|
||||
content: $al-ico-hor-minimize;
|
||||
}
|
||||
}
|
||||
.al-ico-pdf {
|
||||
|
||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
$icomoon-font-family: "trains" !default;
|
||||
$icomoon-font-path: "fonts" !default;
|
||||
|
||||
$al-ico-md-copy: "\e9ec";
|
||||
$al-ico-markdown: "\e9ec";
|
||||
$al-ico-hor-expand: "\e9ea";
|
||||
$al-ico-hor-minimize: "\e9eb";
|
||||
$al-ico-pdf: "\e9e9";
|
||||
|
||||
BIN
src/app/webapp-common/assets/icons/training_loss_graph.png
Normal file
BIN
src/app/webapp-common/assets/icons/training_loss_graph.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
439
src/app/webapp-common/assets/markdown-cheatsheet.html
Normal file
439
src/app/webapp-common/assets/markdown-cheatsheet.html
Normal file
@@ -0,0 +1,439 @@
|
||||
|
||||
<style>
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:root {
|
||||
--blue-25: #f9fafb;
|
||||
--blue-50: #f2f4fc;
|
||||
--blue-100: #dce0ee;
|
||||
--blue-200: #c3cdf0;
|
||||
--blue-250: #A4ADCD;
|
||||
--blue-280: #a7b2d8;
|
||||
--blue-300: #8492c2;
|
||||
--blue-400: #5a658e;
|
||||
--blue-450: #657099;
|
||||
--blue-480: #707ba3;
|
||||
--blue-500: #384161;
|
||||
--blue-550: #47527A;
|
||||
--blue-600: #2c3246;
|
||||
--blue-650: #24293c;
|
||||
--blue-700: #202432;
|
||||
--blue-800: #1a1e2c;
|
||||
--blue-900: #141722;
|
||||
--blue-950: #0d0e15;
|
||||
--purple: #4d66ff;
|
||||
--dark-border: #303443;
|
||||
}
|
||||
|
||||
/* LAYOUT */
|
||||
.modal-content {
|
||||
font-family: "Heebo", sans-serif;
|
||||
color: var(--blue-500);
|
||||
font-size: 14px;
|
||||
max-width: 640px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.modal-content .intro {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.modal-content .link {
|
||||
color: var(--purple);
|
||||
text-decoration: none;
|
||||
}
|
||||
.modal-content .link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.modal-content h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: 0 0 6px 0;
|
||||
}
|
||||
.modal-content .graph {
|
||||
max-width: 100%;
|
||||
border: 1px solid var(--dark-border);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.modal-content figcaption {
|
||||
color: var(--blue-400);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.modal-content section::after {
|
||||
content: "✦ ✦ ✦";
|
||||
text-align: center;
|
||||
display: block;
|
||||
padding: 24px 0;
|
||||
font-size: 11px;
|
||||
color: var(--blue-300);
|
||||
}
|
||||
|
||||
.modal-content p > small {
|
||||
display: block;
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
/* HEADING */
|
||||
.modal-content h1 {font-size: 28px; font-weight: 500;}
|
||||
.modal-content h2 {font-size: 22px; font-weight: 500; color: #70a798;}
|
||||
.modal-content h3 {font-size: 16px; font-weight: 500; color: #7dafdb;}
|
||||
|
||||
/* BLOCKQUOTE */
|
||||
.modal-content blockquote {
|
||||
padding: 4px 16px;
|
||||
border-left: 3px solid var(--purple);
|
||||
}
|
||||
.modal-content blockquote p {
|
||||
margin: 0;
|
||||
color: var(--blue-500);
|
||||
}
|
||||
|
||||
/* LIST */
|
||||
.modal-content ul,
|
||||
.modal-content ol {
|
||||
margin-bottom: 14px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
/* TABLES */
|
||||
.modal-content .table {
|
||||
background-color: var(--blue-900);
|
||||
border: 1px solid var(--dark-border);
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.modal-content .table thead {
|
||||
color: var(--blue-300);
|
||||
background-color: var(--blue-800);
|
||||
}
|
||||
.modal-content .table th {
|
||||
font-weight: 500;
|
||||
}
|
||||
.modal-content .table th,
|
||||
.modal-content .table td {
|
||||
border: 1px solid var(--dark-border);
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.modal-content .table td {
|
||||
color: var(--blue-100);
|
||||
}
|
||||
|
||||
|
||||
/* COLLAPSIBLE SECTIONS */
|
||||
.modal-content details {
|
||||
margin-bottom: 2px;
|
||||
padding: 6px 28px;
|
||||
color: var(--blue-250);
|
||||
background-color: var(--blue-900);
|
||||
border: 1px solid var(--dark-border);
|
||||
border-radius: 4px;
|
||||
transition: margin 0.35s;
|
||||
}
|
||||
.modal-content details summary {
|
||||
cursor: pointer;
|
||||
color: #90bce3;
|
||||
margin-left: -24px;
|
||||
}
|
||||
.modal-content summary::marker {
|
||||
content:"";
|
||||
}
|
||||
.modal-content summary::before {
|
||||
content: "\e9af";
|
||||
color: var(--blue-300);
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-family: "trains";
|
||||
font-size: 18px;
|
||||
margin: -3px 6px 0 0;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 0.35s;
|
||||
}
|
||||
.modal-content summary:hover {
|
||||
color: var(--blue-100);
|
||||
}
|
||||
.modal-content details[open] summary {
|
||||
color: #e1edf8;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.modal-content details[open] summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
|
||||
/* CODE */
|
||||
.modal-content code {
|
||||
margin: 0;
|
||||
padding: 1px 4px;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
border-radius: 4px;
|
||||
color: var(--blue-100);
|
||||
background-color: var(--blue-600);
|
||||
border: 1px solid var(--blue-500);
|
||||
}
|
||||
.modal-content .md-code {
|
||||
display: block;
|
||||
position: relative;
|
||||
margin-bottom: 12px;
|
||||
padding: 16px;
|
||||
font-size: 13px;
|
||||
background-color: var(--blue-900);
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.modal-content .md-code.word-break {
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
|
||||
/* HORIZONTAL RULES */
|
||||
.modal-content hr {
|
||||
margin: 24px 0;
|
||||
border: none;
|
||||
border-bottom: 1px solid #0000001a;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<div class="modal-content">
|
||||
|
||||
<p class="intro">The following is a quick reference for the Markdown syntax that can be used in ClearML Reports.</p>
|
||||
|
||||
<!-- EMBEDDED VISUALIZATIONS -->
|
||||
<section>
|
||||
<h2>Embedded Visualizations</h2>
|
||||
<p>You can embed most ClearML plots and images appearing in experiments (or experiment comparison). Each such asset will show a "<b>Copy embed code</b>" button on hover, which will copy specific HTML embed code that, pasted in a report, will embed that asset.</p>
|
||||
<code class="md-code hljs html word-break"><span class="hljs-tag"><<span class="hljs-name">iframe</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/widgets?type=scalar&tasks=e-f6b6632fdd3648d1948fb311baaa3d03&metrics=train&company=aec6c91852c548158a76778aa40c1772"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"c66c0fda-5196-4d5f-ab0d-50ead9cb8738"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"100%"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"400"</span>></span><span class="hljs-tag"></<span class="hljs-name">iframe</span>></span></code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<figure>
|
||||
<img class="graph" src="app/webapp-common/assets/icons/training_loss_graph.png"
|
||||
alt="Training loss">
|
||||
<figcaption><small>Training loss from the Pytorch MNIST example</small></figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<!-- HEADING -->
|
||||
<section>
|
||||
<h2>Heading</h2>
|
||||
<code class="md-code"># H1
|
||||
## H2
|
||||
### H3
|
||||
#### H4
|
||||
##### H5
|
||||
###### H6
|
||||
</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<h1>H1</h1>
|
||||
<h2>H2</h2>
|
||||
<h3>H3</h3>
|
||||
<h4>H4</h4>
|
||||
<h5>H5</h5>
|
||||
<h6>H6</h6>
|
||||
</section>
|
||||
|
||||
<!-- EMPHASIS -->
|
||||
<section>
|
||||
<h2>Emphasis</h2>
|
||||
<code class="md-code">**This is bold text** and __so is this__
|
||||
*This is italic text* and _so is this_
|
||||
~~Strikethrough~~</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<p><strong>This is bold text</strong> and <strong>so is this</strong></p>
|
||||
<p><em>This is italic text</em> and <em>so is this</em></p>
|
||||
<p><del>Strikethrough</del></p>
|
||||
</section>
|
||||
|
||||
<!-- BLOCKQUOTES -->
|
||||
<section>
|
||||
<h2>Blockquotes</h2>
|
||||
<code class="md-code">> Blockquotes can be nested...
|
||||
>> ...by using additional greater-than signs right next to each other...
|
||||
> > > ...or with spaces between arrows.</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<blockquote>
|
||||
<p>Blockquotes can be nested...</p>
|
||||
<blockquote>
|
||||
<p>...by using additional greater-than signs right next to each other...</p>
|
||||
<blockquote>
|
||||
<p>...or with spaces between arrows.</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</section>
|
||||
|
||||
<!-- LIST -->
|
||||
<section>
|
||||
<h2>List</h2>
|
||||
|
||||
<h3>Unordered</h3>
|
||||
<code class="md-code">+ Create a list by starting a line with `+`, `-`, or `*`
|
||||
+ Sub-lists are made by indenting 2 spaces:
|
||||
- Marker character change forces new list start:
|
||||
* Ac tristique libero volutpat at
|
||||
+ Facilisis in pretium nisl aliquet
|
||||
- Nulla volutpat aliquam velit
|
||||
+ [x] Checkmarks are supported as well.
|
||||
</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<ul>
|
||||
<li>Create a list by starting a line with <code>+</code>, <code>-</code>, or <code>*</code></li><li>Sub-lists are made by indenting 2 spaces:<ul>
|
||||
<li>Marker character change forces new list start:<ul>
|
||||
<li>Ac tristique libero volutpat at</li><li>Facilisis in pretium nisl aliquet</li><li>Nulla volutpat aliquam velit</li></ul>
|
||||
</li></ul>
|
||||
</li><li><i class="fa-solid fa-check-square"></i> Checkmarks are supported as well.</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
<h3>Ordered</h3>
|
||||
<code class="md-code">1. Start with a numbered element...
|
||||
2. And just keep going.
|
||||
1. You can use sequential numbers...
|
||||
1. ...or keep all the numbers as `1.`
|
||||
</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<ol>
|
||||
<li>Start with a numbered element...</li>
|
||||
<li>And just keep going.</li>
|
||||
<li>You can use sequential numbers...</li>
|
||||
<li>...or keep all the numbers as <code>1.</code></li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<!-- TABLES -->
|
||||
<section>
|
||||
<h2>Tables</h2>
|
||||
<code class="md-code">| | Align Right | Align Left | Align Center |
|
||||
| —————-—— | —————————-—:|:—————————- |:—————————-——:|
|
||||
| 1 | 1 | 1 | 1 |
|
||||
| 11 | 11 | 11 | 11 |
|
||||
</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
|
||||
<table class="table" style="width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th style="text-align: right;">Align Right</th>
|
||||
<th style="text-align: left;">Align Left</th>
|
||||
<th style="text-align: center;">Align Center</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td style="text-align: right;">1</td>
|
||||
<td>1</td>
|
||||
<td style="text-align: center;">1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
<td style="text-align: right;">11</td>
|
||||
<td>11</td>
|
||||
<td style="text-align: center;">11</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- COLLAPSIBLE SECTIONS -->
|
||||
<section>
|
||||
<h2>Collapsible sections</h2>
|
||||
<code class="md-code hljs html"><span class="hljs-tag"><<span class="hljs-name">details</span>></span>
|
||||
<span class="hljs-tag"><<span class="hljs-name">summary</span>></span>Extra detailed information<span class="hljs-tag"></<span class="hljs-name">summary</span>></span>
|
||||
All the gory details!
|
||||
<span class="hljs-tag"></<span class="hljs-name">details</span>></span></code>
|
||||
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<details>
|
||||
<summary>Extra detailed information</summary>
|
||||
All the gory details!
|
||||
</details>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- CODE -->
|
||||
<section>
|
||||
<h2>Code</h2>
|
||||
|
||||
<h3>Inline code</h3>
|
||||
<code class="md-code">Putting `code` in the middle of the sentence</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<p>Putting <code>code</code> in the middle of the sentence</p>
|
||||
<hr/>
|
||||
<h3>Block code with indentation</h3>
|
||||
<pre>
|
||||
// Comment
|
||||
line 1
|
||||
line 2
|
||||
line 3</pre>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<code class="md-code">// Comment
|
||||
line 1
|
||||
line 2
|
||||
line 3</code>
|
||||
<hr/>
|
||||
<h3>Block code using "fences"</h3>
|
||||
<pre>```
|
||||
Meaningful code
|
||||
```</pre>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<code class="md-code">Meaningful code</code>
|
||||
<hr/>
|
||||
<h3>Syntax highlighting</h3>
|
||||
|
||||
<code class="md-code">```py
|
||||
from clearml import task
|
||||
t = task.Init(project_name='Groundbreaking research', task_name='Baseline')
|
||||
```</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<code class="md-code"><span class="hljs-keyword">from</span> clearml <span class="hljs-keyword">import</span> task
|
||||
t = task.Init(project_name=<span class="hljs-string">'Groundbreaking research'</span>, task_name=<span class="hljs-string">'Baseline'</span>)
|
||||
</code>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- LINKS -->
|
||||
<section>
|
||||
<h2>Links</h2>
|
||||
<code class="md-code hljs md">[<span class="hljs-string">link text</span>](<span class="hljs-link">http://ww.clear.ml</span>)
|
||||
[<span class="hljs-string">link with tooltip</span>](<span class="hljs-link">http://www.clear.ml/docs/latest/docs/ "ClearML Documentation"</span>)</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<p><a href="http://www.clear.ml" target="_blank">link text</a></p>
|
||||
<p><a href="http://www.clear.ml/docs/latest/docs/" target="_blank" title="ClearML Documentation">link with tooltip</a></p>
|
||||
</section>
|
||||
|
||||
<!-- HORIZONTAL RULES -->
|
||||
<section>
|
||||
<h2>Horizontal Rules</h2>
|
||||
<code class="md-code">___
|
||||
---
|
||||
***</code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<hr>
|
||||
<hr>
|
||||
<hr>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- IMAGES -->
|
||||
<section>
|
||||
<h2>Images</h2>
|
||||
<code class="md-code"></code>
|
||||
<p><small>The rendered output looks like this:</small></p>
|
||||
<p class="text-center">
|
||||
<img style="max-width: 50%;" src="app/webapp-common/assets/icons/c-logomark.svg" alt="Logo with text" title="ClearML Logo with title">
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
@@ -9,6 +9,7 @@ export const getPlot = createAction('[App] getPlot', props<{
|
||||
metrics: string[];
|
||||
variants: string[];
|
||||
company: string;
|
||||
otherSearchParams?: URLSearchParams;
|
||||
}>());
|
||||
|
||||
export const getScalar = createAction('[App] getScalar', props<{
|
||||
@@ -17,6 +18,7 @@ export const getScalar = createAction('[App] getScalar', props<{
|
||||
metrics: string[];
|
||||
variants: string[];
|
||||
company: string;
|
||||
otherSearchParams?: URLSearchParams;
|
||||
}>());
|
||||
export const getSample = createAction('[App] getSample', props<{
|
||||
tasks: string[];
|
||||
@@ -24,6 +26,7 @@ export const getSample = createAction('[App] getSample', props<{
|
||||
metrics: string[];
|
||||
variants: string[];
|
||||
company: string;
|
||||
otherSearchParams?: URLSearchParams;
|
||||
}>());
|
||||
|
||||
export const setPlotData = createAction('[App] setPlot', props<{ data: ReportsApiMultiplotsResponse }>());
|
||||
@@ -32,3 +35,4 @@ export const setSampleData = createAction('[App] setSample', props<{ data: Debug
|
||||
|
||||
export const reportsPlotlyReady = createAction('[App] plotly ready');
|
||||
export const setSignIsNeeded = createAction('[App] set sign is needed');
|
||||
export const setNoPermissions = createAction('[App] set no permissions');
|
||||
|
||||
@@ -1,38 +1,39 @@
|
||||
<ng-container *ngIf="activated; else placeHolder" [ngSwitch]="type">
|
||||
<ng-container *ngIf="(signIsNeeded | async) === false; else signIsNeededTemplate">
|
||||
<ng-template #plotTemplate>
|
||||
<sm-single-graph
|
||||
[class.less-padding]="false"
|
||||
[class.two-in-a-row]="false"
|
||||
[graphsNumber]="1"
|
||||
[height]="singleGraphHeight"
|
||||
[chart]="plotData"
|
||||
[id]="'lala'"
|
||||
[isDarkTheme]="true"
|
||||
[showLoaderOnDraw]="false"
|
||||
[identifier]="'lala'"
|
||||
[width]="400"
|
||||
[isCompare]="true"
|
||||
[noMargins]="true"
|
||||
[legendConfiguration]="{noTextWrap: true}"
|
||||
[hideMaximize]="hideMaximize"
|
||||
(maximizeClicked)="maximize()">
|
||||
</sm-single-graph>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="(signIsNeeded$ | async) === false; else signIsNeededTemplate">
|
||||
<ng-container *ngIf="(noPermissions$ | async) === false; else noPermissionsTemplate">
|
||||
<ng-template #plotTemplate>
|
||||
<sm-single-graph
|
||||
[hideDownloadButtons]="externalTool"
|
||||
[class.less-padding]="false"
|
||||
[class.two-in-a-row]="false"
|
||||
[graphsNumber]="1"
|
||||
[height]="singleGraphHeight"
|
||||
[chart]="plotData"
|
||||
[id]="'lala'"
|
||||
[isDarkTheme]="isDarkTheme"
|
||||
[showLoaderOnDraw]="false"
|
||||
[identifier]="'lala'"
|
||||
[width]="400"
|
||||
[isCompare]="true"
|
||||
[noMargins]="true"
|
||||
[legendConfiguration]="{noTextWrap: true}"
|
||||
[hideMaximize]="hideMaximize"
|
||||
(maximizeClicked)="maximize()">
|
||||
</sm-single-graph>
|
||||
</ng-template>
|
||||
|
||||
<ng-container *ngIf="type === 'sample'; else plotTemplate">
|
||||
<sm-debug-image-snippet
|
||||
*ngIf="frame?.url"
|
||||
class="d-flex-center"
|
||||
[class.no-pointer]="hideMaximize !== 'show'"
|
||||
[frame]="frame"
|
||||
[noHoverEffects]="true"
|
||||
(imageClicked)="hideMaximize === 'show' && sampleClicked($event)">
|
||||
</sm-debug-image-snippet>
|
||||
<ng-container *ngIf="type === 'sample'; else plotTemplate">
|
||||
<sm-debug-image-snippet
|
||||
*ngIf="frame?.url"
|
||||
class="d-flex-center"
|
||||
[frame]="frame"
|
||||
[noHoverEffects]="true"
|
||||
(imageClicked)="hideMaximize === 'show' && sampleClicked($event)">
|
||||
</sm-debug-image-snippet>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #placeHolder>
|
||||
<div class="placeholder">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
|
||||
@@ -44,7 +45,14 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
||||
<ng-template #signIsNeededTemplate>
|
||||
<div class="s3message">Missing S3 credentials. Please verify credentials in <a target="_blank" class="link" href="/settings/webapp-configuration">WEB APP CLOUD ACCESS</a> in clearml app.</div>
|
||||
<div class="s3message">Missing S3 credentials. Please verify credentials in <a target="_blank" class="link"
|
||||
href="/settings/webapp-configuration">WEB
|
||||
APP CLOUD ACCESS</a> in clearml app.
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #noPermissionsTemplate>
|
||||
<div class="placeholder">Missing permissions to view this item.
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, OnInit, ViewChild} from '@angular/core';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {Observable} from 'rxjs';
|
||||
import {filter, map, switchMap, take} from 'rxjs/operators';
|
||||
import {Environment} from '../environments/base';
|
||||
import {getPlot, getSample, getScalar, reportsPlotlyReady} from './app.actions';
|
||||
import {ReportsApiMultiplotsResponse, selectPlotData, selectReportsPlotlyReady, selectSampleData, selectSignIsNeeded, State} from './app.reducer';
|
||||
import {
|
||||
ReportsApiMultiplotsResponse,
|
||||
selectNoPermissions,
|
||||
selectPlotData,
|
||||
selectReportsPlotlyReady,
|
||||
selectSampleData,
|
||||
selectSignIsNeeded,
|
||||
State
|
||||
} from './app.reducer';
|
||||
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';
|
||||
@@ -18,6 +26,7 @@ import {cloneDeep} from 'lodash/fp';
|
||||
import {MetricsPlotEvent} from '~/business-logic/model/events/metricsPlotEvent';
|
||||
import {SingleGraphComponent} from '@common/shared/single-graph/single-graph.component';
|
||||
import {setCurrentDebugImage} from '@common/shared/debug-sample/debug-sample.actions';
|
||||
import {isFileserverUrl} from '~/shared/utils/url';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -34,13 +43,21 @@ export class AppComponent implements OnInit {
|
||||
private environment: Environment;
|
||||
public activated: boolean = false;
|
||||
private searchParams: URLSearchParams;
|
||||
private otherSearchParams: URLSearchParams;
|
||||
public type: string;
|
||||
public singleGraphHeight;
|
||||
public hideMaximize: 'show' | 'hide' | 'disabled' = 'show';
|
||||
public signIsNeeded: Observable<boolean>;
|
||||
|
||||
public signIsNeeded$: Observable<boolean>;
|
||||
public noPermissions$: Observable<boolean>;
|
||||
public isDarkTheme: boolean;
|
||||
public externalTool: boolean = false;
|
||||
@ViewChild(SingleGraphComponent) 'singleGraph': SingleGraphComponent;
|
||||
|
||||
@HostListener('window:resize')
|
||||
onResize() {
|
||||
this.singleGraph?.redrawPlot();
|
||||
}
|
||||
|
||||
constructor(
|
||||
private store: Store<State>,
|
||||
private configService: ConfigurationService,
|
||||
@@ -49,26 +66,39 @@ export class AppComponent implements OnInit {
|
||||
this.configService.globalEnvironmentObservable.subscribe(env => {
|
||||
this.environment = env;
|
||||
});
|
||||
this.signIsNeeded = store.select(selectSignIsNeeded);
|
||||
this.signIsNeeded$ = store.select(selectSignIsNeeded);
|
||||
this.noPermissions$ = store.select(selectNoPermissions);
|
||||
this.searchParams = new URLSearchParams(window.location.search);
|
||||
this.type = this.searchParams.get('type');
|
||||
this.singleGraphHeight = window.innerHeight;
|
||||
this.otherSearchParams = this.getOtherSearchParams();
|
||||
this.isDarkTheme = !this.searchParams.get('light');
|
||||
|
||||
try {
|
||||
const lala = JSON.parse(localStorage.getItem('_saved_state_'));
|
||||
this.store.dispatch(setS3Credentials(lala.auth.s3BucketCredentials));
|
||||
const data = JSON.parse(localStorage.getItem('_saved_state_'));
|
||||
data.auth && this.store.dispatch(setS3Credentials(data.auth.s3BucketCredentials));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private getOtherSearchParams() {
|
||||
const paramsToRemove = ['light', 'type','tasks', 'metrics', 'variants', 'iterations', 'company'];
|
||||
const otherSearchParams = new URLSearchParams(window.location.search);
|
||||
paramsToRemove.forEach( key => {
|
||||
otherSearchParams.delete(key);
|
||||
});
|
||||
return otherSearchParams;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
try {
|
||||
if (!(window.top as any).holdIframe) {
|
||||
this.activate();
|
||||
}
|
||||
} catch (e) {
|
||||
this.externalTool = true;
|
||||
this.hideMaximize = 'hide';
|
||||
console.log('no-access-to-parent-window');
|
||||
this.activate();
|
||||
@@ -164,14 +194,18 @@ export class AppComponent implements OnInit {
|
||||
this.store.select(selectSampleData)
|
||||
.pipe(filter(sample => !!sample))
|
||||
.subscribe(sample => {
|
||||
this.store.dispatch(getSignedUrl({url: sample.url}));
|
||||
this.store.select(selectSignedUrl(sample.url))
|
||||
const url = new URL(sample.url);
|
||||
if (isFileserverUrl(sample.url) && this.searchParams.get('company')) {
|
||||
url.searchParams.append('tenant', this.searchParams.get('company'));
|
||||
}
|
||||
this.store.dispatch(getSignedUrl({url: url.toString()}));
|
||||
this.store.select(selectSignedUrl(url.toString()))
|
||||
.pipe(
|
||||
filter(signed => !!signed?.signed),
|
||||
map(({signed: signedUrl}) => signedUrl),
|
||||
take(1)
|
||||
).subscribe(() => {
|
||||
this.frame = sample;
|
||||
).subscribe((signedUrl) => {
|
||||
this.frame = {...sample, url: signedUrl};
|
||||
this.activated = true;
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
@@ -186,18 +220,18 @@ export class AppComponent implements OnInit {
|
||||
metrics: this.searchParams.getAll('metrics'),
|
||||
variants: this.searchParams.getAll('variants'),
|
||||
iterations: this.searchParams.getAll('iterations').map(iteration => parseInt(iteration, 10)),
|
||||
company: this.searchParams.get('company') || '',
|
||||
company: this.searchParams.get('company') || '',
|
||||
};
|
||||
|
||||
switch (this.type) {
|
||||
case 'plot':
|
||||
this.store.dispatch(getPlot(queryParams));
|
||||
this.store.dispatch(getPlot({...queryParams, otherSearchParams: this.otherSearchParams}));
|
||||
break;
|
||||
case 'scalar':
|
||||
this.store.dispatch(getScalar(queryParams));
|
||||
this.store.dispatch(getScalar({...queryParams, otherSearchParams: this.otherSearchParams}));
|
||||
break;
|
||||
case 'sample':
|
||||
this.store.dispatch(getSample(queryParams));
|
||||
this.store.dispatch(getSample({...queryParams, otherSearchParams: this.otherSearchParams}));
|
||||
}
|
||||
this.activated = true;
|
||||
};
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {getPlot, getSample, getScalar, setPlotData, setSampleData, setSignIsNeeded} from './app.actions';
|
||||
import {
|
||||
getPlot,
|
||||
getSample,
|
||||
getScalar,
|
||||
setNoPermissions,
|
||||
setPlotData,
|
||||
setSampleData,
|
||||
setSignIsNeeded
|
||||
} from './app.actions';
|
||||
import {EMPTY, mergeMap, of, switchMap} from 'rxjs';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {catchError, filter} from 'rxjs/operators';
|
||||
import {ReportsApiMultiplotsResponse, State} from './app.reducer';
|
||||
import {ApiReportsService} from '~/business-logic/api-services/reports.service';
|
||||
import {BaseAdminService} from '@common/settings/admin/base-admin.service';
|
||||
import {ReportsGetTaskDataResponse} from '~/business-logic/model/reports/reportsGetTaskDataResponse';
|
||||
import {setCurrentDebugImage} from '@common/shared/debug-sample/debug-sample.actions';
|
||||
import {getSignedUrl, setSignedUrl} from '@common/core/actions/common-auth.actions';
|
||||
import {SignResponse} from '@common/settings/admin/base-admin-utils';
|
||||
import {HttpClient, HttpHeaders} from '@angular/common/http';
|
||||
import {HTTP} from '~/app.constants';
|
||||
import {DebugSample} from '@common/shared/debug-sample/debug-sample.reducer';
|
||||
import {requestFailed} from '@common/core/actions/http.actions';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@@ -37,7 +45,7 @@ export class AppEffects {
|
||||
|
||||
getPlot = createEffect(() => this.actions$.pipe(
|
||||
ofType(getPlot),
|
||||
switchMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data`,
|
||||
switchMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data?${action.otherSearchParams.toString()}`,
|
||||
{
|
||||
id: action.tasks,
|
||||
plots: {
|
||||
@@ -47,12 +55,13 @@ export class AppEffects {
|
||||
},
|
||||
{headers: this.getHeaders(action.company)}
|
||||
)),
|
||||
mergeMap((res) => [setPlotData({data: res.data.plots as unknown as ReportsApiMultiplotsResponse})])
|
||||
mergeMap((res) => [setPlotData({data: res.data.plots as unknown as ReportsApiMultiplotsResponse})]),
|
||||
catchError(error => [requestFailed(error), ...(error.status === 403 ? [setNoPermissions()] : [])])
|
||||
));
|
||||
|
||||
getScalar = createEffect(() => this.actions$.pipe(
|
||||
ofType(getScalar),
|
||||
mergeMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data`,
|
||||
mergeMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data?${action.otherSearchParams.toString()}`,
|
||||
{
|
||||
id: action.tasks,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
@@ -64,14 +73,14 @@ export class AppEffects {
|
||||
).pipe(
|
||||
mergeMap(res => [
|
||||
setPlotData({data: res.data.scalar_metrics_iter_histogram as ReportsApiMultiplotsResponse})]
|
||||
)
|
||||
), catchError(error => [requestFailed(error), ...(error.status === 403 ? [setNoPermissions()] : [])])
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
getSample = createEffect(() => this.actions$.pipe(
|
||||
ofType(getSample),
|
||||
switchMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data`,
|
||||
switchMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data?${action.otherSearchParams.toString()}`,
|
||||
{
|
||||
id: action.tasks,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
@@ -84,7 +93,8 @@ export class AppEffects {
|
||||
).pipe(
|
||||
mergeMap(res => [
|
||||
setSampleData({data: res.data.debug_images?.[0]?.iterations?.[0]?.events[0] as DebugSample})
|
||||
])
|
||||
]),
|
||||
catchError(error => [requestFailed(error), ...(error.status === 403 ? [setNoPermissions()] : [])])
|
||||
)
|
||||
))
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {createReducer, createSelector, on} from '@ngrx/store';
|
||||
import {reportsPlotlyReady, setPlotData, setSampleData, setSignIsNeeded} from './app.actions';
|
||||
import {reportsPlotlyReady, setNoPermissions, setPlotData, setSampleData, setSignIsNeeded} from './app.actions';
|
||||
import {DebugSample} from '@common/shared/debug-sample/debug-sample.reducer';
|
||||
import {MetricsPlotEvent} from '~/business-logic/model/events/metricsPlotEvent';
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface State {
|
||||
scaleFactor: number;
|
||||
plotlyReady: boolean;
|
||||
signIsNeeded: boolean;
|
||||
noPermissions: boolean;
|
||||
}
|
||||
|
||||
export const initialState: State = {
|
||||
@@ -32,7 +33,8 @@ export const initialState: State = {
|
||||
sampleData: null,
|
||||
scaleFactor: 100,
|
||||
plotlyReady: false,
|
||||
signIsNeeded: false
|
||||
signIsNeeded: false,
|
||||
noPermissions: false
|
||||
};
|
||||
|
||||
export const appReducer = createReducer(
|
||||
@@ -40,10 +42,8 @@ export const appReducer = createReducer(
|
||||
on(reportsPlotlyReady, (state) => ({...state, plotlyReady: true})),
|
||||
on(setPlotData, (state, action) => ({...state, plotData: action.data as ReportsApiMultiplotsResponse})),
|
||||
on(setSampleData, (state, action) => ({...state, sampleData: action.data})),
|
||||
on(setSignIsNeeded, (state) => {
|
||||
debugger
|
||||
return ({...state, signIsNeeded: true})
|
||||
}),
|
||||
on(setSignIsNeeded, (state) => ({...state, signIsNeeded: true})),
|
||||
on(setNoPermissions, (state) => ({...state, noPermissions: true})),
|
||||
);
|
||||
|
||||
export const selectFeature = state => state.appReducer as State;
|
||||
@@ -53,3 +53,4 @@ export const selectReportsPlotlyReady = createSelector(selectFeature, state => s
|
||||
export const selectPlotData = createSelector(selectFeature, state => state.plotData);
|
||||
export const selectSampleData = createSelector(selectFeature, state => state.sampleData);
|
||||
export const selectSignIsNeeded = createSelector(selectFeature, state => state.signIsNeeded);
|
||||
export const selectNoPermissions = createSelector(selectFeature, state => state.noPermissions);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//@import "../webapp-common/shared/ui-components/styles/material-theme.scss";
|
||||
|
||||
$custom-typography: mat.define-typography-config(
|
||||
$font-family: $font-family-base
|
||||
$font-family: $font-family-base
|
||||
);
|
||||
|
||||
@include mat.all-component-typographies($custom-typography);
|
||||
@@ -587,10 +587,10 @@ as-split {
|
||||
}
|
||||
|
||||
$type-colors: (
|
||||
string: #ff8400,
|
||||
number: $neon-yellow,
|
||||
boolean: #b938a4,
|
||||
date: #05668D,
|
||||
string: #ff8400,
|
||||
number: $neon-yellow,
|
||||
boolean: #b938a4,
|
||||
date: #05668D,
|
||||
);
|
||||
|
||||
|
||||
@@ -780,3 +780,44 @@ button.btn.button-outline-dark {
|
||||
fill: rgb(77, 102, 255) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media print {
|
||||
* {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
html, body, .content, .info-content {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
sm-side-nav,
|
||||
.actions button, .actions .add-button,
|
||||
.content.report .mat-drawer, .report .hover-button {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.md-layout #print-element {
|
||||
padding: 0 !important
|
||||
}
|
||||
.content.report .mat-drawer-content {
|
||||
margin-left: unset !important
|
||||
}
|
||||
.content.report .mat-drawer-content,
|
||||
.content.report .mat-drawer-container {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.md-editor-container,
|
||||
.preview-panel {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.header-container,
|
||||
.header,
|
||||
.content {
|
||||
max-width: unset !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ import {MessageSeverityEnum} from '@common/constants';
|
||||
|
||||
export const setAutoRefresh = createAction(
|
||||
VIEW_PREFIX + '[set auto refresh]',
|
||||
props<{autoRefresh: boolean}>()
|
||||
props<{ autoRefresh: boolean }>()
|
||||
);
|
||||
|
||||
export const setCompareAutoRefresh = createAction(
|
||||
VIEW_PREFIX + '[set compare auto refresh]',
|
||||
props<{autoRefresh: boolean}>()
|
||||
props<{ autoRefresh: boolean }>()
|
||||
);
|
||||
|
||||
export const setServerError = createAction(
|
||||
@@ -25,16 +25,17 @@ export const setServerError = createAction(
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
export const setNotificationDialog = createAction(
|
||||
VIEW_PREFIX + '[set notification dialog]',
|
||||
props<{notification: {message: string; title: string}}>()
|
||||
props<{ notification: { message: string; title: string } }>()
|
||||
);
|
||||
|
||||
export const resetLoader = createAction(VIEW_PREFIX + '[reset loader]');
|
||||
|
||||
export const setBackdrop = createAction(
|
||||
VIEW_PREFIX + '[set backdrop]',
|
||||
props<{active: boolean}>()
|
||||
props<{ active: boolean }>()
|
||||
);
|
||||
|
||||
export const activeLoader = createAction(
|
||||
@@ -42,6 +43,11 @@ export const activeLoader = createAction(
|
||||
(endpoint: string) => ({endpoint, noPreferences: true})
|
||||
);
|
||||
|
||||
export const showEmbedReportMenu = createAction(
|
||||
VIEW_PREFIX + '[show embed report menu]',
|
||||
props<{ show: boolean; position: { x: number; y: number } }>()
|
||||
);
|
||||
|
||||
export const deactivateLoader = createAction(
|
||||
VIEW_PREFIX + '[deactivate loader]',
|
||||
(endpoint: string) => ({endpoint, noPreferences: true})
|
||||
@@ -50,12 +56,12 @@ export const deactivateLoader = createAction(
|
||||
|
||||
export const visibilityChanged = createAction(
|
||||
VIEW_PREFIX + '[visibility changed]',
|
||||
props<{visible: boolean}>()
|
||||
props<{ visible: boolean }>()
|
||||
);
|
||||
|
||||
export const saveAceCaretPosition = createAction(
|
||||
VIEW_PREFIX + '[save ace caret position]',
|
||||
props<{id: string; position: Ace.Point}>()
|
||||
props<{ id: string; position: Ace.Point }>()
|
||||
);
|
||||
|
||||
export const resetAceCaretsPositions = createAction(VIEW_PREFIX + '[reset ace carets positions]');
|
||||
@@ -63,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: any[]; name: string }[], suppressNextMessages?: boolean) =>
|
||||
({severity, msg, userActions, suppressNextMessages})
|
||||
);
|
||||
|
||||
@@ -71,29 +77,26 @@ export const removeMessage = createAction(VIEW_PREFIX + '[remove message]');
|
||||
|
||||
export const setServerUpdatesAvailable = createAction(
|
||||
VIEW_PREFIX + '[set server updates available]',
|
||||
props<{availableUpdates}>()
|
||||
props<{ availableUpdates }>()
|
||||
);
|
||||
|
||||
export const setScaleFactor = createAction(
|
||||
VIEW_PREFIX + '[set scale]',
|
||||
props<{scale: number}>()
|
||||
props<{ scale: number }>()
|
||||
);
|
||||
|
||||
export const firstLogin = createAction(
|
||||
VIEW_PREFIX + '[set first Login]',
|
||||
props<{first: boolean}>()
|
||||
props<{ first: boolean }>()
|
||||
);
|
||||
|
||||
export const neverShowPopupAgain = createAction(VIEW_PREFIX + 'NEVER_SHOW_POPUP_AGAIN', props<{ popupId: string; reset?: boolean }>());
|
||||
export const setRedactedArguments = createAction(VIEW_PREFIX + 'SET_REDACTED_ARGUMENTS', props<{ redactedArguments: { key: string} [] }>());
|
||||
export const setHideRedactedArguments = createAction(VIEW_PREFIX + 'SET_SHOW_REDACTED_ARGUMENTS', props<{hide: boolean }>());
|
||||
export const setRedactedArguments = createAction(VIEW_PREFIX + 'SET_REDACTED_ARGUMENTS', props<{ redactedArguments: { key: string } [] }>());
|
||||
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}>()
|
||||
props<{ appsYouTubeIntroVideoId }>()
|
||||
);
|
||||
|
||||
export const toggleUserFocus = createAction(
|
||||
VIEW_PREFIX + '[toggle user focus in header',
|
||||
props<{show: boolean}>()
|
||||
);
|
||||
|
||||
|
||||
@@ -53,12 +53,6 @@ export class ProjectsEffects {
|
||||
map(action => activeLoader(action.type))
|
||||
));
|
||||
|
||||
setProject = createEffect(() => this.actions$.pipe(
|
||||
ofType(actions.setSelectedProject),
|
||||
switchMap(action => [actions.getCompanyTags()]
|
||||
.concat(!!action.project?.id ? [actions.getTags(action.project.id)] : []))
|
||||
));
|
||||
|
||||
getProjects$ = createEffect(() => this.actions$.pipe(
|
||||
ofType(actions.getAllSystemProjects),
|
||||
withLatestFrom(
|
||||
|
||||
@@ -36,7 +36,7 @@ export const createUserPrefReducer = (
|
||||
}
|
||||
|
||||
// filter unchanged state.
|
||||
if (action.noPreferences || state === nextState) {
|
||||
if (action.noPreferences || state === nextState || !(nextState?.[key]?.preferencesReady)) {
|
||||
return nextState;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,12 @@ import {
|
||||
setCurrentUserName
|
||||
} from '../actions/users.actions';
|
||||
import {GetCurrentUserResponseUserObject} from '~/business-logic/model/users/getCurrentUserResponseUserObject';
|
||||
import {GetCurrentUserResponseUserObjectCompany} from '~/business-logic/model/users/getCurrentUserResponseUserObjectCompany';
|
||||
import {OrganizationGetUserCompaniesResponseCompanies} from '~/business-logic/model/organization/organizationGetUserCompaniesResponseCompanies';
|
||||
import {
|
||||
GetCurrentUserResponseUserObjectCompany
|
||||
} from '~/business-logic/model/users/getCurrentUserResponseUserObjectCompany';
|
||||
import {
|
||||
OrganizationGetUserCompaniesResponseCompanies
|
||||
} from '~/business-logic/model/organization/organizationGetUserCompaniesResponseCompanies';
|
||||
|
||||
export interface UsersState {
|
||||
currentUser: GetCurrentUserResponseUserObject;
|
||||
@@ -34,13 +38,13 @@ export const initUsers: UsersState = {
|
||||
|
||||
export const users = state => state.users as UsersState;
|
||||
|
||||
export const selectCurrentUser = createSelector(users, state => state.currentUser);
|
||||
export const selectCurrentUser = createSelector(users, state => state.currentUser);
|
||||
export const selectActiveWorkspace = createSelector(users, state => state.activeWorkspace);
|
||||
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 selectServerVersions = createSelector(users, state => state.serverVersions);
|
||||
export const selectGettingStarted = createSelector(users, state => state.gettingStarted);
|
||||
|
||||
export const usersReducerFunctions = [
|
||||
@@ -54,6 +58,8 @@ export const usersReducerFunctions = [
|
||||
...state,
|
||||
currentUser: null
|
||||
})),
|
||||
on(setFilterByUser, (state, action) => ({...state, showOnlyUserWork: action.showOnlyUserWork})),
|
||||
on(setFilterByUser, (state, action) => {
|
||||
return ({...state, showOnlyUserWork: action.showOnlyUserWork});
|
||||
}),
|
||||
on(setApiVersion, (state, action) => ({...state, serverVersions: action.serverVersions}))
|
||||
] as ReducerTypes<UsersState, any>[];
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface ViewState {
|
||||
showUserFocus: boolean;
|
||||
redactedArguments: { key: string }[];
|
||||
hideRedactedArguments: boolean;
|
||||
showEmbedReportMenu: { show: boolean; position: { x: number; y: number } };
|
||||
}
|
||||
|
||||
export const initViewState: ViewState = {
|
||||
@@ -48,6 +49,7 @@ export const initViewState: ViewState = {
|
||||
{key: 'AWS_SECRET_ACCESS_KEY'},
|
||||
{key: 'AZURE_STORAGE_KEY'}],
|
||||
hideRedactedArguments: false,
|
||||
showEmbedReportMenu: {show: null, position: null}
|
||||
};
|
||||
|
||||
export const views = state => state.views as ViewState;
|
||||
@@ -72,7 +74,7 @@ export const selectAceCaretPosition = createSelector(views, state => state.aceCa
|
||||
export const selectNeverShowPopups = createSelector(views, (state): string[] => state.neverShowPopupAgain);
|
||||
export const selectRedactedArguments = createSelector(views, (state): { key: string }[] => state.redactedArguments);
|
||||
export const selectHideRedactedArguments = createSelector(views, (state): { key: string }[] => state.hideRedactedArguments ? state.redactedArguments : null);
|
||||
export const selectShowUserFocus = createSelector(views, state => state.showUserFocus);
|
||||
export const selectShowEmbedReportMenu = createSelector(views, state => state.showEmbedReportMenu);
|
||||
|
||||
|
||||
export const viewReducers = [
|
||||
@@ -114,11 +116,14 @@ export const viewReducers = [
|
||||
on(layoutActions.setBackdrop, (state, action) => ({...state, backdropActive: action.active})),
|
||||
on(layoutActions.setAutoRefresh, (state, action) => ({...state, autoRefresh: action.autoRefresh})),
|
||||
on(layoutActions.setCompareAutoRefresh, (state, action) => ({...state, compareAutoRefresh: action.autoRefresh})),
|
||||
on(layoutActions.showEmbedReportMenu, (state, action) => ({
|
||||
...state,
|
||||
showEmbedReportMenu: {show: action.show, position: action.position}
|
||||
})),
|
||||
on(layoutActions.neverShowPopupAgain, (state, action) => ({
|
||||
...state,
|
||||
neverShowPopupAgain: action.reset ? state.neverShowPopupAgain.filter(popups => popups !== action.popupId) : Array.from(new Set([...state.neverShowPopupAgain, action.popupId]))
|
||||
})),
|
||||
on(layoutActions.toggleUserFocus, (state, action) => ({...state, showUserFocus: action.show}))
|
||||
] as ReducerTypes<ViewState, any>[];
|
||||
|
||||
export const viewReducer = createReducer(
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
></sm-simple-dataset-card>
|
||||
</ng-container>
|
||||
|
||||
<div class="load-more" *ngIf="(noMoreProjects$ | async) === false">
|
||||
<div class="load-more" *ngIf="(noMoreProjects$ | async) === false && (projectsList$ | async)?.length > 0">
|
||||
<button (click)="loadMore()" class="btn btn-cml-primary load-more-btn">LOAD MORE</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
[theme]="isDarkTheme? themeEnum.Dark: themeEnum.Light"
|
||||
(imageError)="imageUrlError({frame, experimentId})"
|
||||
(imageClicked)="imageClicked.emit({frame})"
|
||||
(createEmbedCode)="createEmbedCode.emit({metrics: [frame.metric], variants: [frame.variant]});">
|
||||
(createEmbedCode)="createEmbedCode.emit({metrics: [frame.metric], variants: [frame.variant], domRect:$event});">
|
||||
</sm-debug-image-snippet>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -22,7 +22,7 @@ export class DebugImagesViewComponent {
|
||||
@Input() isDatasetVersionPreview = false;
|
||||
@Output() imageClicked = new EventEmitter();
|
||||
@Output() refreshClicked = new EventEmitter();
|
||||
@Output() createEmbedCode = new EventEmitter<{metrics?: string[]; variants?: string[]}>();
|
||||
@Output() createEmbedCode = new EventEmitter<{metrics?: string[]; variants?: string[]; domRect: DOMRect}>();
|
||||
@Output() urlError = new EventEmitter();
|
||||
|
||||
public imageUrlError(data: { frame: Event; experimentId: string }) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="p-3 images-container">
|
||||
<div class="single-debug-images-container"
|
||||
*ngFor="let experimentId of experimentIds.slice(0,LIMITED_VIEW_LIMIT); trackBy: trackExperiment; let first = first; let last =
|
||||
last"
|
||||
*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">
|
||||
<sm-experiment-compare-general-data
|
||||
|
||||
@@ -40,8 +40,8 @@ import {getSignedUrl} 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 {ReportCodeEmbedService} from '@common/shared/services/report-code-embed.service';
|
||||
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;
|
||||
@@ -412,7 +412,7 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
|
||||
this.bindNavigationMode = !this.bindNavigationMode;
|
||||
}
|
||||
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[] }, experimentId: string) {
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[]; domRect: DOMRect }, experimentId: string) {
|
||||
this.reportEmbed.createCode({
|
||||
type: 'sample',
|
||||
tasks: [experimentId],
|
||||
|
||||
@@ -31,8 +31,8 @@ import {ExperimentGraphsComponent} from '@common/shared/experiment-graphs/experi
|
||||
import {selectScalarsHoverMode} from '@common/experiments/reducers';
|
||||
import {setScalarsHoverMode} from '@common/experiments/actions/common-experiment-output.actions';
|
||||
import {ChartHoverModeEnum} from '@common/experiments/shared/common-experiments.const';
|
||||
import {ReportCodeEmbedService} from '@common/shared/services/report-code-embed.service';
|
||||
import {Router} from '@angular/router';
|
||||
import { ReportCodeEmbedService } from '~/shared/services/report-code-embed.service';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -213,7 +213,7 @@ export class ExperimentCompareScalarChartsComponent implements OnInit, OnDestroy
|
||||
this.store.dispatch(setScalarsHoverMode({hoverMode}));
|
||||
}
|
||||
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[] }) {
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[]; domRect: DOMRect }) {
|
||||
this.reportEmbed.createCode({
|
||||
type: 'scalar',
|
||||
tasks: this.taskIds,
|
||||
|
||||
@@ -24,7 +24,7 @@ import {ExtFrame} from '@common/shared/single-graph/plotly-graph-base';
|
||||
import {RefreshService} from '@common/core/services/refresh.service';
|
||||
import {addMessage} from '@common/core/actions/layout.actions';
|
||||
import {ExperimentGraphsComponent} from '@common/shared/experiment-graphs/experiment-graphs.component';
|
||||
import {ReportCodeEmbedService} from '@common/shared/services/report-code-embed.service';
|
||||
import { ReportCodeEmbedService } from '~/shared/services/report-code-embed.service';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-experiment-compare-plots',
|
||||
@@ -148,7 +148,7 @@ export class ExperimentComparePlotsComponent implements OnInit, OnDestroy {
|
||||
this.store.dispatch(resetExperimentMetrics());
|
||||
}
|
||||
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[] }) {
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[]; domRect: DOMRect }) {
|
||||
this.reportEmbed.createCode({
|
||||
type: 'plot',
|
||||
tasks: this.taskIds,
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import {select, Store} from '@ngrx/store';
|
||||
import {flatten, get, isEmpty} from 'lodash/fp';
|
||||
import {selectExperimentsUpdateTime} from '../reducers';
|
||||
import {selectRouterParams} from '../../core/reducers/router-reducer';
|
||||
import {selectRouterConfig, selectRouterParams} from '../../core/reducers/router-reducer';
|
||||
import {selectAppVisible} from '../../core/reducers/view.reducer';
|
||||
import {MINIMUM_ONLY_FIELDS} from '../../experiments/experiment.consts';
|
||||
import * as exSelectors from '../../experiments/reducers';
|
||||
|
||||
@@ -27,7 +27,7 @@ import {selectSelectedProject} from '@common/core/reducers/projects.reducer';
|
||||
export class ExperimentInfoArtifactsComponent implements OnDestroy {
|
||||
public backdropActive$: Observable<boolean>;
|
||||
public modelInfo$: Observable<IExperimentModelInfo>;
|
||||
public ExperimentInfo$: Observable<IExperimentInfo>;
|
||||
public experimentInfo$: Observable<IExperimentInfo>;
|
||||
public activeSection: any;
|
||||
public selectedId$: Observable<string>;
|
||||
private experimentKey$: Observable<string>;
|
||||
@@ -43,9 +43,10 @@ export class ExperimentInfoArtifactsComponent implements OnDestroy {
|
||||
this.backdropActive$ = this.store.select(selectBackdropActive);
|
||||
this.editable$ = this.store.select(selectIsExperimentEditable);
|
||||
this.modelInfo$ = this.store.select(selectExperimentModelInfoData);
|
||||
this.ExperimentInfo$ = this.store.select(selectExperimentInfoData);
|
||||
this.experimentInfo$ = this.store.select(selectExperimentInfoData);
|
||||
this.routerConfig$ = this.store.select(selectRouterConfig);
|
||||
this.selectedId$ = this.store.select(selectRouterParams).pipe(map(params => params?.artifactId || params?.modelId));
|
||||
this.selectedId$ = this.store.select(selectRouterParams)
|
||||
.pipe(map(params => decodeURIComponent(params?.artifactId || params?.modelId)));
|
||||
this.experimentKey$ = this.store.select(selectRouterParams).pipe(map(params => params?.experimentId));
|
||||
|
||||
this.sub.add(this.store.select(selectRouterConfig)
|
||||
@@ -74,7 +75,7 @@ export class ExperimentInfoArtifactsComponent implements OnDestroy {
|
||||
this.selectedId$,
|
||||
this.modelInfo$,
|
||||
this.experimentKey$,
|
||||
this.ExperimentInfo$,
|
||||
this.experimentInfo$,
|
||||
this.store.select(selectCurrentArtifactExperimentId)
|
||||
])
|
||||
.pipe(
|
||||
@@ -110,11 +111,11 @@ export class ExperimentInfoArtifactsComponent implements OnDestroy {
|
||||
private resetSelection(modelInfo): void {
|
||||
let target: string;
|
||||
if (modelInfo.input?.length > 0) {
|
||||
target = `../artifacts/input-model/${modelInfo.input[0]?.id}`;
|
||||
target = `../artifacts/input-model/${encodeURIComponent(modelInfo.input[0]?.id)}`;
|
||||
} else if (modelInfo.output?.length > 0) {
|
||||
target = `../artifacts/output-model/${modelInfo.output[0]?.id}/`;
|
||||
target = `../artifacts/output-model/${encodeURIComponent(modelInfo.output[0]?.id)}/`;
|
||||
} else if (modelInfo.artifacts.length > 0) {
|
||||
target = `../artifacts/other/${modelInfo.artifacts[0]?.key}/${modelInfo.artifacts[0]?.mode}`;
|
||||
target = `../artifacts/other/${encodeURIComponent(modelInfo.artifacts[0]?.key)}/${encodeURIComponent(modelInfo.artifacts[0]?.mode)}`;
|
||||
} else {
|
||||
// no items
|
||||
target = '../artifacts/input-model/input-model';
|
||||
|
||||
@@ -27,9 +27,7 @@ export class ExperimentInfoArtifactItemComponent implements OnInit, OnDestroy {
|
||||
this.modelInfo$ = this.store.select(selectExperimentModelInfoData);
|
||||
this.artifactKey$ = this.store.select(selectRouterParams)
|
||||
.pipe(
|
||||
map(params => {
|
||||
return {key: get('artifactId', params), mode: get('mode', params)};
|
||||
}),
|
||||
map(params => ({key: decodeURIComponent(params?.artifactId), mode: get('mode', params)})),
|
||||
filter(artifactId => !!artifactId),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
@@ -109,10 +109,10 @@ export class ExperimentOutputLogComponent implements OnInit, AfterViewInit, OnDe
|
||||
}));
|
||||
|
||||
this.subs.add(this.refresh.tick
|
||||
.pipe(filter(() => !this.loading && !!this.currExperiment?.id && (!this.logRef || this.logRef.atEnd)))
|
||||
.subscribe((autoRefresh) => this.store.dispatch(getExperimentLog({
|
||||
.pipe(filter(autoRefresh => autoRefresh !== null && !this.loading && !!this.currExperiment?.id && (!this.logRef || this.logRef.atEnd)))
|
||||
.subscribe(autoRefresh => this.store.dispatch(getExperimentLog({
|
||||
id: this.currExperiment.id,
|
||||
direction: autoRefresh ? 'prev' : 'next',
|
||||
direction: autoRefresh ? 'next' : 'prev',
|
||||
from: last(this.logRef?.orgLogs)?.timestamp,
|
||||
autoRefresh: true
|
||||
})))
|
||||
|
||||
@@ -35,7 +35,7 @@ import {ExtFrame} from '@common/shared/single-graph/plotly-graph-base';
|
||||
import {MetricsPlotEvent} from '~/business-logic/model/events/metricsPlotEvent';
|
||||
import {addMessage} from '@common/core/actions/layout.actions';
|
||||
import {ExperimentGraphsComponent} from '@common/shared/experiment-graphs/experiment-graphs.component';
|
||||
import {ReportCodeEmbedService} from '@common/shared/services/report-code-embed.service';
|
||||
import { ReportCodeEmbedService } from '~/shared/services/report-code-embed.service';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-experiment-output-plots',
|
||||
@@ -49,9 +49,6 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan
|
||||
|
||||
public plotsList: Array<SelectableListItem>;
|
||||
public selectedGraph: string = null;
|
||||
private plotsSubscription: Subscription;
|
||||
private settingsSubscription: Subscription;
|
||||
private routerParamsSubscription: Subscription;
|
||||
private experimentId: string;
|
||||
private routerParams$: Observable<any>;
|
||||
public listOfHidden: Observable<Array<any>>;
|
||||
@@ -61,9 +58,10 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan
|
||||
public graphs: { [key: string]: ExtFrame[] };
|
||||
public refreshDisabled: boolean;
|
||||
public selectIsExperimentPendingRunning: Observable<boolean>;
|
||||
private selectedExperimentSubscription: Subscription;
|
||||
public splitSize$: Observable<number>;
|
||||
public dark: boolean;
|
||||
private subs = new Subscription();
|
||||
private experimentCompany: string | undefined;
|
||||
|
||||
|
||||
constructor(
|
||||
@@ -73,8 +71,9 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan
|
||||
private changeDetection: ChangeDetectorRef,
|
||||
private reportEmbed: ReportCodeEmbedService
|
||||
) {
|
||||
this.searchTerm$ = this.store.pipe(select(selectExperimentMetricsSearchTerm));
|
||||
this.splitSize$ = this.store.pipe(select(selectSplitSize));
|
||||
this.searchTerm$ = this.store.select(selectExperimentMetricsSearchTerm);
|
||||
this.splitSize$ = this.store.select(selectSplitSize);
|
||||
this.subs.add(this.store.select(selectSelectedExperiment).subscribe(exp => this.experimentCompany = exp?.company?.id ?? null));
|
||||
|
||||
this.experimentSettings$ = this.store.pipe(
|
||||
select(selectSelectedExperimentSettings),
|
||||
@@ -105,7 +104,7 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan
|
||||
ngOnInit() {
|
||||
this.minimized = this.activeRoute.snapshot.routeConfig.data?.minimized;
|
||||
this.listOfHidden = this.store.select(selectSelectedSettingsHiddenPlot);
|
||||
this.plotsSubscription = this.store.select(selectExperimentInfoPlots)
|
||||
this.subs.add(this.store.select(selectExperimentInfoPlots)
|
||||
.pipe(
|
||||
distinctUntilChanged(),
|
||||
filter(metrics => !!metrics),
|
||||
@@ -119,15 +118,15 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan
|
||||
this.graphs = graphs;
|
||||
parsingError && this.store.dispatch(addMessage('warn', `Couldn't read all plots. Please make sure all plots are properly formatted (NaN & Inf aren't supported).`, [], true));
|
||||
this.changeDetection.detectChanges();
|
||||
});
|
||||
}));
|
||||
|
||||
this.settingsSubscription = this.experimentSettings$
|
||||
this.subs.add(this.experimentSettings$
|
||||
.subscribe((selectedPlot) => {
|
||||
this.selectedGraph = selectedPlot;
|
||||
this.graphsComponent?.scrollToGraph(selectedPlot);
|
||||
});
|
||||
}));
|
||||
|
||||
this.routerParamsSubscription = this.routerParams$
|
||||
this.subs.add(this.routerParams$
|
||||
.subscribe(params => {
|
||||
if (!this.experimentId || this.experimentId !== params.experimentId) {
|
||||
this.graphs = undefined;
|
||||
@@ -136,9 +135,9 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan
|
||||
this.store.dispatch(setExperimentMetricsSearchTerm({searchTerm: ''}));
|
||||
}
|
||||
this.experimentId = params.experimentId;
|
||||
});
|
||||
}));
|
||||
|
||||
this.selectedExperimentSubscription = this.store.select(selectSelectedExperiment)
|
||||
this.subs.add(this.store.select(selectSelectedExperiment)
|
||||
.pipe(
|
||||
filter(experiment => !!experiment && !this.isDatasetVersionPreview),
|
||||
distinctUntilChanged()
|
||||
@@ -146,16 +145,13 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan
|
||||
.subscribe(experiment => {
|
||||
this.experimentId = experiment.id;
|
||||
this.refresh();
|
||||
});
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.resetMetrics();
|
||||
this.plotsSubscription.unsubscribe();
|
||||
this.settingsSubscription.unsubscribe();
|
||||
this.routerParamsSubscription.unsubscribe();
|
||||
this.selectedExperimentSubscription.unsubscribe();
|
||||
this.subs.unsubscribe();
|
||||
this.resetMetrics();
|
||||
}
|
||||
|
||||
@@ -188,7 +184,7 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan
|
||||
this.store.dispatch(resetExperimentMetrics());
|
||||
}
|
||||
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[] }) {
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[]; domRect: DOMRect }) {
|
||||
this.reportEmbed.createCode({
|
||||
type: 'plot',
|
||||
tasks: [this.experimentId],
|
||||
|
||||
@@ -33,7 +33,7 @@ import {ExtFrame} from '@common/shared/single-graph/plotly-graph-base';
|
||||
import {ExperimentGraphsComponent} from '@common/shared/experiment-graphs/experiment-graphs.component';
|
||||
import {isEqual} from 'lodash/fp';
|
||||
import { EventsGetTaskSingleValueMetricsResponseValues } from '~/business-logic/model/events/eventsGetTaskSingleValueMetricsResponseValues';
|
||||
import {ReportCodeEmbedService} from '@common/shared/services/report-code-embed.service';
|
||||
import { ReportCodeEmbedService } from '~/shared/services/report-code-embed.service';
|
||||
|
||||
export const prepareScalarList = (metricsScalar: GroupedList): GroupedList =>
|
||||
Object.keys(metricsScalar || []).reduce((acc, curr) => {
|
||||
@@ -155,6 +155,8 @@ export class ExperimentOutputScalarsComponent implements OnInit, OnDestroy {
|
||||
this.subs.add(this.store.select(selectShowSettings)
|
||||
.subscribe((show) => this.showSettingsBar = show)
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -265,7 +267,7 @@ export class ExperimentOutputScalarsComponent implements OnInit, OnDestroy {
|
||||
}, {});
|
||||
}
|
||||
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[] }) {
|
||||
createEmbedCode(event: { metrics?: string[]; variants?: string[]; domRect: DOMRect}) {
|
||||
this.reportEmbed.createCode({
|
||||
type: 'scalar',
|
||||
tasks: [this.experimentId],
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*ngFor="let model of inputModels"
|
||||
queryParamsHandling="preserve"
|
||||
class="artifact-container pointer"
|
||||
[routerLink]="'input-model/' + model.id"
|
||||
[routerLink]="'input-model/' + (model.id | safeUrlParameter)"
|
||||
[class.selected]="model.id === selectedArtifactKey && activeSection === ACTIVE_SECTIONS['input-model']"
|
||||
>
|
||||
<i class="icon sm al-ico-import"></i>
|
||||
@@ -32,7 +32,7 @@
|
||||
*ngFor="let model of outputModels"
|
||||
queryParamsHandling="preserve"
|
||||
class="artifact-container pointer"
|
||||
[routerLink]="'output-model/' + model.id"
|
||||
[routerLink]="'output-model/' + (model.id | safeUrlParameter)"
|
||||
[class.selected]="model.id === selectedArtifactKey && activeSection === ACTIVE_SECTIONS['output-model']"
|
||||
>
|
||||
<i class="icon sm al-ico-export"></i>
|
||||
@@ -47,7 +47,7 @@
|
||||
<div *ngFor="let artifact of dataAuditArtifacts; trackBy: trackByFn"
|
||||
class="artifact-container pointer"
|
||||
queryParamsHandling="preserve"
|
||||
[routerLink]="'artifact/' + artifact.key+ '/' + artifact.mode"
|
||||
[routerLink]="'artifact/' + (artifact.key | safeUrlParameter) + '/' + (artifact.mode | safeUrlParameter)"
|
||||
[class.selected]="selectedArtifactKey === artifact.key && activeSection === ACTIVE_SECTIONS['artifact']"
|
||||
>
|
||||
<i class="sm al-icon al-ico-data-audit al-color" [class]="(selectedArtifactKey === artifact.key ? 'white' : 'blue-400')"></i>{{artifact.key}}
|
||||
@@ -60,7 +60,7 @@
|
||||
<div *ngFor="let artifact of otherArtifacts; trackBy: trackByFn"
|
||||
class="artifact-container pointer"
|
||||
queryParamsHandling="preserve"
|
||||
[routerLink]="'other/' + artifact.key+'/' +artifact.mode"
|
||||
[routerLink]="'other/' + (artifact.key | safeUrlParameter) + '/' + (artifact.mode | safeUrlParameter)"
|
||||
[class.selected]="selectedArtifactKey === artifact.key && activeSection === ACTIVE_SECTIONS['other']"
|
||||
>
|
||||
<i class="sm al-icon al-ico-data-audit al-color" [class]="(selectedArtifactKey === artifact.key ? 'white' : 'blue-400')"></i>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</mat-expansion-panel>
|
||||
<mat-expansion-panel expanded="true" *ngIf="((hyperParams | keyvalue)|filterOut:'key':'properties')?.length>0">
|
||||
<mat-expansion-panel-header collapsedHeight="40px" expandedHeight="40px" class="active">
|
||||
HYPER PARAMETERS
|
||||
HYPERPARAMETERS
|
||||
</mat-expansion-panel-header>
|
||||
<div
|
||||
*ngFor="let hyperParameter of (hyperParams | keyvalue)|filterOut:'key':'properties' | sort:'key'; trackBy: trackByFn"
|
||||
|
||||
@@ -13,7 +13,6 @@ export class ExperimentHyperParamsNavbarComponent {
|
||||
properties: 'User Properties',
|
||||
design: 'General'
|
||||
};
|
||||
encodeURI = encodeURI;
|
||||
@Input() hyperParams: { [key: string]: any};
|
||||
@Input() configuration: { [key: string]: any};
|
||||
@Input() selectedObject;
|
||||
|
||||
@@ -6,7 +6,7 @@ import {selectCompanyTags, selectProjectTags, selectTagsFilterByProject} from '@
|
||||
import {addTag, removeTag} from '../../actions/common-experiments-menu.actions';
|
||||
import {TagsMenuComponent} from '@common/shared/ui-components/tags/tags-menu/tags-menu.component';
|
||||
import {MenuComponent} from '@common/shared/ui-components/panel/menu/menu.component';
|
||||
import {activateEdit, deactivateEdit} from '../../actions/common-experiments-info.actions';
|
||||
import {activateEdit, deactivateEdit, setExperiment} from '../../actions/common-experiments-info.actions';
|
||||
import {EXPERIMENTS_STATUS_LABELS, ExperimentTagsEnum} from '~/features/experiments/shared/experiments.const';
|
||||
import {EXPERIMENT_COMMENT} from '../experiment-general-info/experiment-general-info.component';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
@@ -51,8 +51,8 @@ export class ExperimentInfoHeaderComponent implements OnDestroy {
|
||||
@Input() isSharedAndNotOwner: boolean;
|
||||
@Output() experimentNameChanged = new EventEmitter<string>();
|
||||
@Output() minimizeClicked = new EventEmitter();
|
||||
@Output() closeInfoClicked = new EventEmitter();
|
||||
@Output() maximizedClicked = new EventEmitter();
|
||||
@Output() closeInfoClicked = new EventEmitter();
|
||||
@Output() maximizedClicked = new EventEmitter();
|
||||
@ViewChild('tagMenu') tagMenu: MenuComponent;
|
||||
@ViewChild('tagsMenuContent') tagMenuContent: TagsMenuComponent;
|
||||
|
||||
@@ -65,6 +65,7 @@ export class ExperimentInfoHeaderComponent implements OnDestroy {
|
||||
ngOnDestroy(): void {
|
||||
this.tagMenu = null;
|
||||
this.tagMenuContent = null;
|
||||
this.store.dispatch(setExperiment(null));
|
||||
}
|
||||
|
||||
private _experiment: any;
|
||||
|
||||
@@ -33,7 +33,7 @@ export class CommonExperimentOutputEffects {
|
||||
|
||||
activeLoader = createEffect(() => this.actions$.pipe(
|
||||
ofType(outputActions.getExperimentLog, outputActions.experimentScalarRequested, outputActions.experimentPlotsRequested),
|
||||
filter(action => !action?.['from']),
|
||||
filter(action => !action?.['autoRefresh']),
|
||||
map(action => activeLoader(action.type))
|
||||
));
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ export class CommonExperimentsMenuEffects {
|
||||
if (res.queue_watched === false && !neverShowAgainPopups.includes('orphanedQueue')) {
|
||||
this.dialog.open(WelcomeMessageComponent, {
|
||||
data: {
|
||||
queue: res.queue,
|
||||
queue: action.queue,
|
||||
step: 2
|
||||
}
|
||||
}).afterClosed().subscribe(doNotShowAgain => {
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
[projectTags]="projectTags$ | async"
|
||||
[companyTags]="companyTags$ | async"
|
||||
[activateFromMenuButton]="false"
|
||||
[useCurrentEntity]="singleRowContext"
|
||||
[minimizedView]="true"
|
||||
[tableMode]="!minimizedView"
|
||||
[backdrop]="menuBackdrop"
|
||||
|
||||
@@ -84,9 +84,9 @@ export class CloneDialogComponent implements OnInit, OnDestroy {
|
||||
const projectList = projects?.map(project => ({value: project.id, label: project.name}));
|
||||
if (!isEqual(projectList, this.projects)) {
|
||||
this.projects = projectList;
|
||||
const defaultProject = this.projects.find(project => project.value === this.defaultProjectId) as Project;
|
||||
const defaultProjectIndex = this.projects.findIndex(project => project.value === this.defaultProjectId);
|
||||
setTimeout(() => {
|
||||
this.formData.project = (defaultProject && defaultProject.company?.id) ? defaultProject : projects[0] ? this.projects[0] : null;
|
||||
this.formData.project = (defaultProjectIndex > -1 && projects[defaultProjectIndex].company?.id) ? this.projects[defaultProjectIndex] : projects[0] ? this.projects[0] : null;
|
||||
this.filterText = this.formData.project?.label;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
|
||||
@Input() companyTags: string[];
|
||||
@Input() numSelected = 0;
|
||||
@Input() activateFromMenuButton = true;
|
||||
@Input() useCurrentEntity = false;
|
||||
|
||||
@Input() set experiment(experiment: ISelectedExperiment) {
|
||||
this._experiment = experiment;
|
||||
@@ -202,14 +203,14 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
|
||||
}
|
||||
|
||||
public resetPopup() {
|
||||
const selectedExperiments = this.selectedExperiments ? selectionDisabledReset(this.selectedExperiments).selectedFiltered : [this._experiment];
|
||||
const selectedExperiments = (!(this.activateFromMenuButton || this.useCurrentEntity) && this.selectedExperiments) ? selectionDisabledReset(this.selectedExperiments).selectedFiltered : [this._experiment];
|
||||
const devWarning: boolean = selectedExperiments.some(exp => isDevelopment(exp));
|
||||
const confirmDialogRef = this.dialog.open(CommonDeleteDialogComponent, {
|
||||
data: {
|
||||
entity: this._experiment,
|
||||
numSelected: this.numSelected,
|
||||
numSelected: selectedExperiments?.length ?? this.numSelected,
|
||||
entityType: EntityTypeEnum.experiment,
|
||||
useCurrentEntity: this.activateFromMenuButton,
|
||||
useCurrentEntity: this.activateFromMenuButton || this.useCurrentEntity,
|
||||
resetMode: true,
|
||||
devWarning
|
||||
},
|
||||
@@ -359,7 +360,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
|
||||
entity: this._experiment,
|
||||
numSelected: this.numSelected,
|
||||
entityType: entityType || EntityTypeEnum.experiment,
|
||||
useCurrentEntity: this.activateFromMenuButton,
|
||||
useCurrentEntity: this.activateFromMenuButton || this.useCurrentEntity,
|
||||
includeChildren
|
||||
},
|
||||
width: '600px',
|
||||
|
||||
@@ -47,25 +47,16 @@ export class SelectQueueComponent implements OnInit, OnDestroy {
|
||||
reference?: string;
|
||||
}
|
||||
) {
|
||||
this.store.dispatch(new GetQueuesForEnqueue());
|
||||
this.userAllowedToCreateQueue$ = userAllowedToCreateQueue$(store);
|
||||
|
||||
if (data && data.taskIds?.length > 0) {
|
||||
this.store.dispatch(new GetTaskForEnqueue(data.taskIds));
|
||||
this.reference = data.taskIds.length < 2 ? data.reference : `${data.taskIds.length} experiments `;
|
||||
}
|
||||
this.queuesSub = this.queues$.subscribe(queues => {
|
||||
if (queues) {
|
||||
this.queues = queues;
|
||||
this.queuesNames = queues.map(q => q.name);
|
||||
this.defaultQueue = this.blTaskService.getDefaultQueue(this.queues) || queues[0];
|
||||
this.queueControl.reset(this.defaultQueue, {emitEvent: false});
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.dispatch(new GetQueuesForEnqueue());
|
||||
this.filteredOptions$ = combineLatest([
|
||||
this.queueControl.valueChanges.pipe(startWith('')),
|
||||
this.queues$
|
||||
@@ -82,6 +73,16 @@ export class SelectQueueComponent implements OnInit, OnDestroy {
|
||||
return queues.filter(q => q.name.toLowerCase().includes(name));
|
||||
}),
|
||||
);
|
||||
|
||||
this.queuesSub = this.queues$.subscribe(queues => {
|
||||
if (queues) {
|
||||
this.queues = queues;
|
||||
this.queuesNames = queues.map(q => q.name);
|
||||
this.defaultQueue = this.blTaskService.getDefaultQueue(this.queues) || queues[0];
|
||||
this.queueControl.reset(this.defaultQueue, {emitEvent: false});
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closeDialog(confirmed) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<div class="header-container" [class.login]="isLogin">
|
||||
<sm-breadcrumbs
|
||||
class="spacer"
|
||||
[class.flex-grow-1]="!(userFocus || (showUserFocus$ | async))"
|
||||
[class.user-focus]="userFocus || (showUserFocus$ | async)"
|
||||
[class.flex-grow-1]="!(userFocus)"
|
||||
[class.user-focus]="userFocus "
|
||||
[class.share-view]="isShareMode"
|
||||
[activeWorkspace]="activeWorkspace">
|
||||
</sm-breadcrumbs>
|
||||
<div class="ml-3 d-flex align-items-center mr-auto" *ngIf="userFocus || (showUserFocus$ | async)">
|
||||
<div class="ml-3 d-flex align-items-center mr-auto" *ngIf="userFocus">
|
||||
<sm-show-only-user-work></sm-show-only-user-work>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<mat-menu #profileMenu="matMenu" class="user-menu">
|
||||
<button mat-menu-item [routerLink]="'settings/' + userNotificationPath">
|
||||
<span class="al-icon icon sm-md" [class]="userNotificationPath ? 'al-ico-settings-alert' : 'al-ico-settings'">
|
||||
<span class="path1"></span><span class="path2"></span>
|
||||
<span class="path1" data-id="Settings Button"></span><span class="path2"></span>
|
||||
</span>
|
||||
Settings
|
||||
</button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectActiveWorkspace, selectCurrentUser} from '../../core/reducers/users-reducer';
|
||||
import {selectActiveWorkspace, selectCurrentUser, selectShowOnlyUserWork} from '../../core/reducers/users-reducer';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {logout} from '../../core/actions/users.actions';
|
||||
import {addMessage, openAppsAwarenessDialog} from '../../core/actions/layout.actions';
|
||||
@@ -16,7 +16,6 @@ import {ActivatedRoute, NavigationEnd, Router} from '@angular/router';
|
||||
import {LoginService} from '~/shared/services/login.service';
|
||||
import {selectUserSettingsNotificationPath} from '~/core/reducers/view.reducer';
|
||||
import {selectInvitesPending} from '~/core/reducers/users.reducer';
|
||||
import {selectShowUserFocus} from '@common/core/reducers/view.reducer';
|
||||
import {MESSAGES_SEVERITY} from '@common/constants';
|
||||
|
||||
@Component({
|
||||
@@ -37,7 +36,6 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
public url: Observable<string>;
|
||||
public invitesPending$: Observable<any[]>;
|
||||
public userNotificationPath: string;
|
||||
public showUserFocus$: Observable<boolean>;
|
||||
private sub = new Subscription();
|
||||
|
||||
constructor(
|
||||
@@ -53,7 +51,6 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
this.user = this.store.select(selectCurrentUser);
|
||||
this.sub.add(this.store.select(selectUserSettingsNotificationPath).subscribe(path => this.userNotificationPath = path));
|
||||
this.invitesPending$ = this.store.select(selectInvitesPending);
|
||||
this.showUserFocus$ = this.store.select(selectShowUserFocus);
|
||||
this.sub.add(this.store.select(selectActiveWorkspace)
|
||||
.pipe(
|
||||
filter(workspace => !!workspace),
|
||||
|
||||
@@ -36,7 +36,7 @@ export class S3AccessResolverComponent {
|
||||
this.isAzure = data.isAzure;
|
||||
if (data.credentialsError) {
|
||||
this.header = this.header = `${data.credentialsError}, please check credentials for bucket <b>${this.bucket}</b>.`;
|
||||
} else {
|
||||
} else {
|
||||
this.header = `Please provide credentials for bucket <b>${this.bucket}</b>.`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,10 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
if (this.showGitHub) {
|
||||
fetch('https://api.github.com/repos/allegroai/clearml', {method: 'GET'})
|
||||
.then(response => response.json()
|
||||
.then(json => this.stars = json['stargazers_count'])
|
||||
.then(json => {
|
||||
this.stars = json['stargazers_count'];
|
||||
this.cdr.detectChanges();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div *ngIf="showButton" mat-button [matMenuTriggerFor]="modelMenu" class="p-0 cell menu-button al-icon al-ico-bars-menu pointer"></div>
|
||||
<div *ngIf="!showButton"
|
||||
<div *ngIf="activateFromMenuButton" mat-button [matMenuTriggerFor]="modelMenu" class="p-0 cell menu-button al-icon al-ico-bars-menu pointer"></div>
|
||||
<div *ngIf="!activateFromMenuButton"
|
||||
style="visibility: hidden; position: fixed"
|
||||
[style.left.px]="position.x"
|
||||
[style.top.px]="position.y"
|
||||
|
||||
@@ -69,7 +69,8 @@ export class ModelMenuComponent extends BaseContextMenuComponent {
|
||||
@Input() projectTags: string[];
|
||||
@Input() companyTags: string[];
|
||||
@Input() tagsFilterByProject: boolean;
|
||||
@Input() showButton = true;
|
||||
@Input() activateFromMenuButton = true;
|
||||
@Input() useCurrentEntity = false;
|
||||
@Output() tagSelected = new EventEmitter<string>();
|
||||
|
||||
constructor(
|
||||
@@ -167,7 +168,7 @@ export class ModelMenuComponent extends BaseContextMenuComponent {
|
||||
entity: this._model,
|
||||
numSelected: this.numSelected,
|
||||
entityType: EntityTypeEnum.model,
|
||||
useCurrentEntity: this.showButton
|
||||
useCurrentEntity: this.activateFromMenuButton || this.useCurrentEntity
|
||||
},
|
||||
width: '600px',
|
||||
disableClose: true
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
[tagsFilterByProject]="tagsFilterByProject$ | async"
|
||||
[projectTags]="projectTags$ | async"
|
||||
[companyTags]="companyTags$ | async"
|
||||
[showButton]="false"
|
||||
[activateFromMenuButton]="false"
|
||||
[useCurrentEntity]="singleRowContext"
|
||||
[backdrop]="menuBackdrop"
|
||||
(tagSelected)="addTag($event)"
|
||||
(menuOpened)="setContextMenuStatus(true)"
|
||||
|
||||
@@ -118,6 +118,7 @@
|
||||
[projectTags]="projectTags$ | async"
|
||||
[companyTags]="companyTags$ | async"
|
||||
[activateFromMenuButton]="false"
|
||||
[useCurrentEntity]="singleRowContext"
|
||||
[minimizedView]="true"
|
||||
[tableMode]="!minimizedView"
|
||||
[backdrop]="menuBackdrop"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
class="al-icon lg"
|
||||
[class]="'al-ico-type-' + (entity?.type ? (entity.type.toString()).replace('_', '-') : 'training')"
|
||||
></i>
|
||||
<div class="name">{{step.name}}</div>
|
||||
<div class="name" [smTooltip]="step.name" smShowTooltipIfEllipsis>{{step.name}}</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="status" [class]="step.data.status">{{step.data.status}}</div>
|
||||
<sm-id-badge class="ml-1" [id]="step.data.job_id" (copied)="copyToClipboard()"></sm-id-badge>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
></sm-pipeline-card>
|
||||
</ng-container>
|
||||
|
||||
<div class="load-more" *ngIf="(noMoreProjects$ | async) === false">
|
||||
<div class="load-more" *ngIf="(noMoreProjects$ | async) === false && (projectsList$ | async)?.length > 0">
|
||||
<button (click)="loadMore()" class="btn btn-cml-primary load-more-btn">LOAD MORE</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ def step(size: int):
|
||||
name='ingest',
|
||||
project='data processing',
|
||||
version='0.1',
|
||||
pipeline_execution_queue="default"
|
||||
pipeline_execution_queue='default'
|
||||
)
|
||||
def pipeline_logic(do_stuff: bool):
|
||||
if do_stuff:
|
||||
|
||||
@@ -62,27 +62,31 @@ export const commonProjectsReducers = [
|
||||
on(setCurrentScrollId, (state, action) => ({...state, scrollId: action.scrollId})),
|
||||
on(setNoMoreProjects, (state, action) => ({...state, noMoreProjects: action.payload})),
|
||||
on(updateProjectSuccess, (state, action) => ({...state, projects: state.projects?.map(ex => ex.id === action.id ? {...ex, ...action.changes} : ex)})),
|
||||
on(resetProjects, state => ({...state, scrollId: null, noMoreProjects: false, projects: commonProjectsInitState.projects})),
|
||||
on(resetProjects, state => ({...state,
|
||||
scrollId: null,
|
||||
noMoreProjects: commonProjectsInitState.noMoreProjects,
|
||||
projects: commonProjectsInitState.projects
|
||||
})),
|
||||
on(setProjectsOrderBy, (state, action) => ({
|
||||
...state,
|
||||
orderBy: action.orderBy,
|
||||
sortOrder: getCorrectSortingOrder(state.sortOrder, state.orderBy, action.orderBy),
|
||||
scrollId: null,
|
||||
noMoreProjects: false,
|
||||
noMoreProjects: commonProjectsInitState.noMoreProjects,
|
||||
projects: commonProjectsInitState.projects
|
||||
})),
|
||||
on(setProjectsSearchQuery, (state, action) => ({
|
||||
...state,
|
||||
searchQuery: (action as ReturnType<typeof setProjectsSearchQuery>),
|
||||
scrollId: null,
|
||||
noMoreProjects: true,
|
||||
noMoreProjects: commonProjectsInitState.noMoreProjects,
|
||||
projects: commonProjectsInitState.projects
|
||||
})),
|
||||
on(resetProjectsSearchQuery, state => ({
|
||||
...state,
|
||||
searchQuery: commonProjectsInitState.searchQuery,
|
||||
scrollId: null,
|
||||
noMoreProjects: true,
|
||||
noMoreProjects: commonProjectsInitState.noMoreProjects,
|
||||
projects: commonProjectsInitState.projects
|
||||
})),
|
||||
on(checkProjectForDeletion, (state, action) => ({
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</sm-project-card>
|
||||
</ng-container>
|
||||
|
||||
<div class="load-more" *ngIf="!noMoreProjects">
|
||||
<div class="load-more" *ngIf="!noMoreProjects && projects?.length > 0">
|
||||
<button (click)="loadMore.emit()" class="btn btn-cml-primary load-more-btn" data-id="Load More" >LOAD MORE</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
<sm-experiment-status-icon-label class="ml-auto" [status]="report?.status"></sm-experiment-status-icon-label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment">
|
||||
{{report?.comment}}
|
||||
</div>
|
||||
<div class="comment">{{report?.comment}}</div>
|
||||
<div footer class="footer-tags">
|
||||
<sm-tag-list
|
||||
class="w-100"
|
||||
|
||||
@@ -54,5 +54,6 @@
|
||||
-webkit-line-clamp: 4;
|
||||
-webkit-box-orient: vertical;
|
||||
color: $blue-200;
|
||||
white-space: pre-line;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</sm-menu>
|
||||
<div class="g-btns">
|
||||
<button class="btn btn-icon" smTooltip="Copy report markdown to clipboard" (click)="copyMarkdown()">
|
||||
<i class="al-icon al-ico-md-copy sm-md"></i>
|
||||
<i class="al-icon al-ico-copy-to-clipboard sm-md"></i>
|
||||
</button>
|
||||
<button class="btn btn-icon" ngxPrint printSectionId="print-element" [printDelay]="3000" [useExistingCss]="true" [printStyle]="printStyle" smTooltip="Export to PDF">
|
||||
<i class="al-icon al-ico-pdf sm-md"></i>
|
||||
@@ -47,8 +47,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="report" class="content report">
|
||||
<div class="hover-button" *ngIf="(smallScreen$ | async)">
|
||||
<button class="btn btn-cml-primary" (click)="drawer.toggle()">
|
||||
<div class="hover-button" *ngIf="!editor.editMode">
|
||||
<button class="btn btn-cml-primary" (click)="showDescription = !showDescription">
|
||||
<i class="al-icon al-ico-caret-right" [class.open]="drawer.opened"></i>
|
||||
Description
|
||||
<i class="al-icon al-ico-caret-right" [class.open]="drawer.opened"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -56,11 +58,12 @@
|
||||
<mat-drawer
|
||||
#drawer
|
||||
[mode]="(smallScreen$ | async) ? 'over' : 'side'"
|
||||
[opened]="!editor.editMode && (smallScreen$ | async) !== true"
|
||||
[opened]="!editor.editMode && showDescription"
|
||||
(closed)="showDescription = false"
|
||||
>
|
||||
<div class="description">
|
||||
<button
|
||||
*ngIf="!example && report && draft && !editDesc"
|
||||
*ngIf="!example && report && !editDesc"
|
||||
class="btn btn-cml-primary edit-button"
|
||||
(click)="editReportDesc(descElement)"
|
||||
>EDIT
|
||||
@@ -69,10 +72,11 @@
|
||||
placeholder="Report description"
|
||||
#descElement
|
||||
[disabled]="!editDesc"
|
||||
(keyup)="cdr.detectChanges()"
|
||||
>{{report?.comment || ''}}</textarea>
|
||||
<div *ngIf="editDesc" class="buttons">
|
||||
<button class="btn btn-outline-dark" (click)="descElement.value = orgDesc; editDesc = false">CANCEL</button>
|
||||
<button class="btn btn-cml-primary" (click)="saveDesc(descElement.value)">SAVE</button>
|
||||
<button [disabled]="orgDesc === descElement.value" class="btn btn-cml-primary" (click)="saveDesc(descElement.value)">SAVE</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-drawer>
|
||||
@@ -86,7 +90,7 @@
|
||||
(saveInfo)="save($event)"
|
||||
(editModeChanged)="editModeChanged();"
|
||||
>
|
||||
<div no-data class="flex-middle placeholder" *ngIf="!example && report && draft">
|
||||
<div no-data class="flex-middle empty-state" *ngIf="!example && report && draft">
|
||||
<i class="al-icon al-ico-reports xxl"></i>
|
||||
<div class="no-data-title">THERE'S NOTHING HERE YET…</div>
|
||||
<button (click)="editor.editClicked()" class="no-data-button btn btn-neon">
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 3px 0 0 24px;
|
||||
padding: 3px 0 0 22px;
|
||||
|
||||
mat-drawer-content {
|
||||
z-index: 2;
|
||||
@@ -42,27 +42,29 @@
|
||||
|
||||
.hover-button {
|
||||
position: absolute;
|
||||
left: 70px;
|
||||
top: 110px;
|
||||
left: 29px;
|
||||
top: 192px;
|
||||
z-index: 14;
|
||||
|
||||
button {
|
||||
padding: 6px;
|
||||
padding: 0 6px;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
transform: rotate(90deg);
|
||||
|
||||
i.open {
|
||||
transform: rotate(180deg);
|
||||
i {
|
||||
transform: rotate(-90deg);
|
||||
|
||||
&.open {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat-drawer {
|
||||
background-color: $blue-800;
|
||||
|
||||
&.mat-drawer-over {
|
||||
box-shadow: 10px 0 5px 5px $blue-800;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
@@ -74,11 +76,11 @@
|
||||
background-color: $blue-900;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
padding: 12px;
|
||||
padding: 12px 12px 12px 42px;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
resize: none;
|
||||
color: $blue-300;
|
||||
color: $blue-250;
|
||||
|
||||
&::placeholder {
|
||||
color: $blue-400;
|
||||
@@ -123,6 +125,9 @@
|
||||
|
||||
&.full-width {
|
||||
max-width: 100%;
|
||||
&:not(.edit) {
|
||||
max-width: calc(100% - 64px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +137,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
.empty-state {
|
||||
margin: 0 auto;
|
||||
max-width: 960px;
|
||||
background-color: $blue-900;
|
||||
border-radius: 4px;
|
||||
@@ -155,3 +161,10 @@
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@media print {
|
||||
|
||||
}
|
||||
@@ -44,15 +44,16 @@ export class ReportComponent implements OnInit, OnDestroy {
|
||||
public reportTags$: Observable<string[]>;
|
||||
public smallScreen$: Observable<boolean>;
|
||||
public printStyle = {
|
||||
table: {border: '1px solid gray'}, th: {border: '1px solid gray'}, td: {border: '1px solid gray'}, details: {border: '1px solid #ccc', margin: '6px 0', padding: '6px'}, iframe: {border: 'none'}
|
||||
table: {border: '1px solid gray'}, th: {border: '1px solid gray'}, td: {border: '1px solid gray'}, details: {border: '1px solid #ccc', margin: '6px 0', padding: '6px'}, iframe: {border: 'none', width: '840px'}
|
||||
};
|
||||
public widthExpanded: boolean = false;
|
||||
public editMode: boolean;
|
||||
@ViewChild(MarkdownEditorComponent) mdEditor: MarkdownEditorComponent;
|
||||
showDescription = false;
|
||||
|
||||
constructor(
|
||||
private store: Store,
|
||||
private cdr: ChangeDetectorRef,
|
||||
public cdr: ChangeDetectorRef,
|
||||
private dialog: MatDialog,
|
||||
private breakpointObserver: BreakpointObserver,
|
||||
private router: Router,
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
<div class="sm-card-list-layout" [class.in-empty-state]="!(!newReport.observed || reports === null || reports?.length > 0)">
|
||||
<ng-content></ng-content>
|
||||
<ng-container *ngIf="!newReport.observed || reports === null || reports?.length > 0; else: empty">
|
||||
<!-- [allTags]="allTags"-->
|
||||
<!-- (addTag)="addTag.emit({report, tag: $event})"-->
|
||||
<!-- (removeTag)="removeTag.emit({report, tag: $event})"-->
|
||||
<!-- (reportCardUpdateName)="reportCardUpdateName.emit({name: $event, report})"-->
|
||||
<!-- (reportCardUpdateMetadata)="reportCardUpdateMetadata.emit({report: report, readOnly: $event})">-->
|
||||
<sm-report-card
|
||||
*ngFor="let report of reports; trackBy: trackByFn"
|
||||
class="report"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!--[hideMenu]="false"-->
|
||||
<sm-reports-list
|
||||
[reports]="reports$ | async"
|
||||
[archive]="archive$ | async"
|
||||
@@ -15,11 +14,6 @@
|
||||
(removeTag)="removeTag($event)"
|
||||
(moveToArchive)="moveToArchive($event)"
|
||||
>
|
||||
<!-- [sortByField]="reportsOrderBy$ | async"-->
|
||||
<!-- [sortOrder]="reportsSortOrder$ | async"-->
|
||||
<!-- [disableCreate]="!navigation"-->
|
||||
<!-- [activeSearch]="false"-->
|
||||
<!-- (orderByChanged)="orderByChanged($event)"-->
|
||||
<sm-reports-header
|
||||
[archive]="archive$ | async"
|
||||
[allTags]="reportsTags$ |async"
|
||||
|
||||
@@ -112,7 +112,7 @@ export class ReportsPageComponent implements OnInit, OnDestroy {
|
||||
|
||||
|
||||
reportSelected(report: Report) {
|
||||
this.router.navigate([(report.project as any).id, report.id], {relativeTo: this.route});
|
||||
this.router.navigate([(report.project as any)?.id ?? '*', report.id], {relativeTo: this.route});
|
||||
|
||||
}
|
||||
|
||||
@@ -131,8 +131,7 @@ export class ReportsPageComponent implements OnInit, OnDestroy {
|
||||
share(report: IReport) {
|
||||
this._clipboardService.copyResponse$
|
||||
.pipe(take(1))
|
||||
.subscribe(() => this.store.dispatch(addMessage(MESSAGES_SEVERITY.SUCCESS,
|
||||
'Resource embed code copied to clipboard.You can paste in your Reports.'))
|
||||
.subscribe(() => this.store.dispatch(addMessage(MESSAGES_SEVERITY.SUCCESS, 'Report link copied to clipboard'))
|
||||
);
|
||||
this._clipboardService.copy(`${window.location.href}/${report.project.id}/${report.id}`);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import {Injectable} from '@angular/core';
|
||||
import {Actions, createEffect, ofType} from '@ngrx/effects';
|
||||
import {Action, Store} from '@ngrx/store';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {catchError, filter, map, mergeMap, switchMap, withLatestFrom} from 'rxjs/operators';
|
||||
import {catchError, filter, map, mergeMap, switchMap, tap, withLatestFrom} from 'rxjs/operators';
|
||||
import {activeLoader, addMessage, deactivateLoader, setServerError} from '../core/actions/layout.actions';
|
||||
import {requestFailed} from '../core/actions/http.actions';
|
||||
import {
|
||||
@@ -161,6 +161,11 @@ export class ReportsEffects {
|
||||
filter(ready => ready),
|
||||
map(() => action))),
|
||||
switchMap(action => this.reportsApiService.reportsGetAllEx({id: [action.id]})),
|
||||
tap(res=> {
|
||||
if(res.tasks.length===0){
|
||||
this.router.navigateByUrl('/404');
|
||||
}
|
||||
}),
|
||||
mergeMap((res: ReportsGetAllExResponse) => [
|
||||
setReport({report: res.tasks[0] as IReport}),
|
||||
deactivateLoader(getReport.type),
|
||||
@@ -220,6 +225,7 @@ export class ReportsEffects {
|
||||
}
|
||||
}),
|
||||
deactivateLoader(moveReport.type),
|
||||
addMessage(MESSAGES_SEVERITY.SUCCESS, `Report moved successfully to ${moveRequest.project_name}`)
|
||||
]),
|
||||
catchError(err => [
|
||||
deactivateLoader(moveReport.type),
|
||||
@@ -261,6 +267,7 @@ export class ReportsEffects {
|
||||
];
|
||||
return [
|
||||
deactivateLoader(action.type),
|
||||
getReports(),
|
||||
...(!action.skipUndo ?
|
||||
[addMessage(MESSAGES_SEVERITY.SUCCESS, 'Report archived successfully', [null, ...undoActions
|
||||
].filter(a => a))] : []),
|
||||
@@ -296,6 +303,7 @@ export class ReportsEffects {
|
||||
];
|
||||
const actions: Action[] = [
|
||||
deactivateLoader(action.type),
|
||||
getReports(),
|
||||
...(!action.skipUndo ?
|
||||
[(addMessage(MESSAGES_SEVERITY.SUCCESS, 'Report restored successfully', [null, ...undoActions].filter(a => a)))] : []),
|
||||
setReportChanges({
|
||||
@@ -322,10 +330,11 @@ export class ReportsEffects {
|
||||
{
|
||||
data: {
|
||||
title: 'DELETE',
|
||||
body: 'Permanently Delete Report',
|
||||
body: '<p class="text-center">Permanently Delete Report</p>',
|
||||
yes: 'Delete',
|
||||
no: 'Cancel',
|
||||
iconClass: 'al-ico-trash',
|
||||
width: 430
|
||||
}
|
||||
}).afterClosed().pipe(
|
||||
filter(confirm => !!confirm),
|
||||
@@ -334,6 +343,7 @@ export class ReportsEffects {
|
||||
this.router.navigate(['reports'], {queryParamsHandling: 'merge'});
|
||||
return [
|
||||
removeReport({id: action.report.id}),
|
||||
getReports(),
|
||||
deactivateLoader(action.type),
|
||||
addMessage(MESSAGES_SEVERITY.SUCCESS, 'Report deleted successfully')
|
||||
];
|
||||
|
||||
@@ -142,7 +142,7 @@ export class BaseAdminService {
|
||||
},
|
||||
...(set.Endpoint && {
|
||||
endpoint: {
|
||||
protocol: set.Endpoint?.startsWith('https') ? 'https' : 'http',
|
||||
protocol: set.Endpoint?.startsWith('https') ? 'https:' : 'http:',
|
||||
hostname,
|
||||
...(!['80', '443'].includes(port) && {port}),
|
||||
path: '',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[required]="true"
|
||||
[editable]="true"
|
||||
(textChanged)="nameChange($event, currentUser)">
|
||||
<div class="text-center">{{currentUser.name}}</div>
|
||||
{{currentUser.name}}
|
||||
</sm-inline-edit>
|
||||
<div *ngIf="currentUser.email" class="profile-email text">Email address:</div>
|
||||
<div>{{currentUser.email}}</div>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<sm-dialog-template header="MARKDOWN CHEAT SHEET" [iconClass]="'al-ico-markdown'" tabindex="0">
|
||||
<div [innerHTML]="mdCheatSheetHtmlFile"></div>
|
||||
</sm-dialog-template>
|
||||
@@ -0,0 +1,4 @@
|
||||
:host{
|
||||
width: 640px;
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MarkdownCheatSheetDialogComponent } from './markdown-cheat-sheet-dialog.component';
|
||||
|
||||
describe('MarkdownCheatSheetDialogComponent', () => {
|
||||
let component: MarkdownCheatSheetDialogComponent;
|
||||
let fixture: ComponentFixture<MarkdownCheatSheetDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MarkdownCheatSheetDialogComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MarkdownCheatSheetDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-markdown-cheat-sheet-dialog',
|
||||
templateUrl: './markdown-cheat-sheet-dialog.component.html',
|
||||
styleUrls: ['./markdown-cheat-sheet-dialog.component.scss']
|
||||
})
|
||||
export class MarkdownCheatSheetDialogComponent implements OnInit {
|
||||
public mdCheatSheetHtmlFile: SafeHtml;
|
||||
|
||||
constructor(private http: HttpClient,
|
||||
private sanitizer: DomSanitizer) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.http.get('app/webapp-common/assets/markdown-cheatsheet.html', {responseType: 'text'}).subscribe(res => {
|
||||
this.mdCheatSheetHtmlFile = this.sanitizer.bypassSecurityTrustHtml(res);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="md-content" [class.edit-mode]="editMode" [class.dirty]="isDirty" [class.editor-hidden]="editorVisible">
|
||||
<div class="actions">
|
||||
<div class="actions-buttons ">
|
||||
<button *ngIf="!readOnly && !editMode && editModeChanged.observed"
|
||||
<button *ngIf="!editMode && editModeChanged.observed"
|
||||
class="btn btn-cml-primary btn-icon btn-expand-view md-button mr-2"
|
||||
[smTooltip]="isExpand? 'Minimize view' : 'Expand view'"
|
||||
matTooltipPosition="below"
|
||||
@@ -30,10 +30,11 @@
|
||||
</div>
|
||||
|
||||
<div class="md-footer">
|
||||
<span [class]="(editMode && isDirty ? 'active' : '') + ' md-warning'">
|
||||
<div *ngIf="duplicateNames">* You have duplicate names on different iframes</div>
|
||||
<div *ngIf="!duplicateNames">* You have unsaved changes</div>
|
||||
</span>
|
||||
<i class="al-icon al-ico-markdown" (click)="openMDCCheatSheet()" smTooltip="Markdown cheat sheet"></i>
|
||||
<div [class]="(editMode && isDirty ? 'active' : '') + ' md-warning'">
|
||||
<span *ngIf="duplicateNames">* You have duplicate names on different iframes</span>
|
||||
<span *ngIf="!duplicateNames">* You have unsaved changes</span>
|
||||
</div>
|
||||
<div *ngIf="editMode" class="right-buttons">
|
||||
<button class="btn btn-cml-primary btn-outline"
|
||||
(click)="cancelClicked()">CANCEL
|
||||
|
||||
@@ -85,13 +85,20 @@
|
||||
margin: 12px 0;
|
||||
}
|
||||
.md-editor-preview.editor .md-footer {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
.al-ico-markdown {
|
||||
cursor: pointer;
|
||||
color: $blue-300;
|
||||
transition: color 0.35s;
|
||||
&:hover {
|
||||
color: $blue-100;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.right-buttons {
|
||||
margin-left: auto;
|
||||
|
||||
button {
|
||||
line-height: normal;
|
||||
}
|
||||
@@ -230,12 +237,19 @@
|
||||
height: unset !important;
|
||||
background-color: transparent !important;
|
||||
|
||||
code.hljs {
|
||||
background-color: $blue-700;
|
||||
code {
|
||||
padding: 1px 4px;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
padding: 16px;
|
||||
border-radius: 4px;
|
||||
color: $blue-100;
|
||||
background-color: $blue-600;
|
||||
border: 1px solid $blue-500;
|
||||
}
|
||||
|
||||
code.hljs {
|
||||
padding: 16px;
|
||||
background-color: $blue-700;
|
||||
}
|
||||
|
||||
pre {
|
||||
@@ -259,6 +273,7 @@
|
||||
// Highlighting Editor
|
||||
.ace-editor.ace_editor.ace-tm {
|
||||
background-color: transparent;
|
||||
line-height: 20px;
|
||||
|
||||
.ace_marker-layer {
|
||||
.ace_selection {
|
||||
|
||||
@@ -12,6 +12,10 @@ import {
|
||||
import {DomSanitizer} from '@angular/platform-browser';
|
||||
import {MarkdownEditorComponent as MDComponent, MdEditorOption} from 'ngx-markdown-editor';
|
||||
import {Ace} from 'ace-builds';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {
|
||||
MarkdownCheatSheetDialogComponent
|
||||
} from '@common/shared/components/markdown-editor/markdown-cheat-sheet-dialog/markdown-cheat-sheet-dialog.component';
|
||||
|
||||
const BREAK_POINT = 990;
|
||||
|
||||
@@ -83,7 +87,8 @@ export class MarkdownEditorComponent {
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private renderer: Renderer2, protected sanitizer: DomSanitizer, protected cdr: ChangeDetectorRef) {
|
||||
constructor(private renderer: Renderer2, protected sanitizer: DomSanitizer, protected cdr: ChangeDetectorRef, protected dialog: MatDialog) {
|
||||
this.editMode = false;
|
||||
}
|
||||
|
||||
save() {
|
||||
@@ -97,7 +102,10 @@ export class MarkdownEditorComponent {
|
||||
this.originalInfo = this.data;
|
||||
this.editMode = true;
|
||||
this.editorVisible = false;
|
||||
setTimeout(() => this.updateEditorVisibility());
|
||||
setTimeout(() => {
|
||||
this.updateEditorVisibility();
|
||||
this.ace.focus();
|
||||
});
|
||||
}
|
||||
|
||||
cancelClicked() {
|
||||
@@ -115,7 +123,7 @@ export class MarkdownEditorComponent {
|
||||
});
|
||||
this.ready = true;
|
||||
this.preview = document.querySelector('.preview-container');
|
||||
this.editor = document.querySelector('.editor-container > div:first-child');
|
||||
this.editor = document.querySelector('.editor-container > div:first-child');
|
||||
}
|
||||
|
||||
togglePreview() {
|
||||
@@ -133,11 +141,17 @@ export class MarkdownEditorComponent {
|
||||
this.renderer.setProperty(this.editorComponent.previewContainer.nativeElement, 'id', 'print-element');
|
||||
|
||||
if (this.data.indexOf('```language') > -1) {
|
||||
const range = this.ace.selection.getRange();
|
||||
this.data = this.data.replace('```language', '```py');
|
||||
this.cdr.detectChanges();
|
||||
range.end.column = range.start.column + 2;
|
||||
window.setTimeout(() => this.ace.selection.setRange(range));
|
||||
const manager = this.ace.session.getUndoManager();
|
||||
const range = this.ace.find('```language',{
|
||||
wrap: true,
|
||||
caseSensitive: true,
|
||||
wholeWord: true,
|
||||
regExp: false,
|
||||
preventScroll: true // do not change selection
|
||||
});
|
||||
const deltas = manager.getDeltas(null, null);
|
||||
this.ace.session.replace(range, '```py');
|
||||
manager['$undoStack'] = deltas;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +161,7 @@ export class MarkdownEditorComponent {
|
||||
}
|
||||
|
||||
private getDuplicateIframes() {
|
||||
const names = Array.from(this.data.matchAll(/<iframe[^>]*?name=(["\'])?((?:.(?!\1|>))*.?)\1?/g)).map(a=>a[2]);
|
||||
const names = Array.from(this.data.matchAll(/<iframe[^>]*?name=(["\'])?((?:.(?!\1|>))*.?)\1?/g)).map(a => a[2]);
|
||||
const uniqueNames = new Set(names);
|
||||
let duplicatedNames = [];
|
||||
for (const name of uniqueNames) {
|
||||
@@ -156,5 +170,9 @@ export class MarkdownEditorComponent {
|
||||
}
|
||||
this.duplicateNames = duplicatedNames.length > 0;
|
||||
}
|
||||
|
||||
openMDCCheatSheet() {
|
||||
this.dialog.open(MarkdownCheatSheetDialogComponent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
>
|
||||
<div *ngIf="!noHoverEffects" class="toolbar top">
|
||||
<div class="clickable-icon d-flex align-items-center justify-content-center pointer"
|
||||
(click)="createEmbedCode.emit();"
|
||||
(click)="createEmbedCodeClicked($event)"
|
||||
smTooltip="Copy to Report">
|
||||
<i class="al-icon al-ico-code sm"></i>
|
||||
</div>
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
<div class="player" *ngSwitchCase="'player'">
|
||||
<video
|
||||
*ngIf="!noHoverEffects"
|
||||
#video
|
||||
preload="metadata"
|
||||
[src]="source"
|
||||
@@ -29,9 +30,19 @@
|
||||
(loadedmetadata)="loadedMedia()"
|
||||
(click)="imageClicked.emit({src: source})"
|
||||
></video>
|
||||
<video
|
||||
*ngIf="noHoverEffects"
|
||||
#video
|
||||
controls
|
||||
preload="metadata"
|
||||
[src]="source"
|
||||
(error)="imageError.emit(); isFailed = true"
|
||||
(loadedmetadata)="loadedMedia()"
|
||||
(click)="imageClicked.emit({src: source})"
|
||||
></video>
|
||||
<div class="toolbar top">
|
||||
<div *ngIf="!noHoverEffects" class="clickable-icon d-flex align-items-center justify-content-center pointer"
|
||||
(click)="createEmbedCode.emit();"
|
||||
(click)="createEmbedCodeClicked($event)"
|
||||
smTooltip="Copy to Report">
|
||||
<i class="al-icon al-ico-code sm"></i>
|
||||
</div>
|
||||
@@ -44,7 +55,7 @@
|
||||
</div>
|
||||
<div *ngIf="!noHoverEffects" class="toolbar">
|
||||
<div class="clickable-icon d-flex align-items-center justify-content-center pointer"
|
||||
(click)="createEmbedCode.emit();"
|
||||
(click)="createEmbedCodeClicked($event)"
|
||||
smTooltip="Copy to Report">
|
||||
<i class="al-icon al-ico-code sm"></i>
|
||||
</div>
|
||||
|
||||
@@ -83,4 +83,8 @@ export class DebugImageSnippetComponent {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
createEmbedCodeClicked($event: MouseEvent) {
|
||||
this.createEmbedCode.emit({x: $event.clientX, y: $event.clientY});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="image-title third">
|
||||
<h1 [smTooltip]="currentDebugImage?.metric? (currentDebugImage?.metric + ' - ' + currentDebugImage?.variant) : ''"
|
||||
smShowTooltipIfEllipsis>{{currentDebugImage?.metric ? (currentDebugImage?.metric + ' - ' + currentDebugImage?.variant) : ''}}</h1>
|
||||
<input #fakeInput name="Don't remove - it take the autofocus from slider" class="invisible">
|
||||
<input #fakeInput name="Don't remove - it take the autofocus from slider" class="hide-me">
|
||||
</div>
|
||||
<div class="third viewer-iteration">
|
||||
<ng-container *ngIf="!data.withoutNavigation">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import "variables";
|
||||
|
||||
:host {
|
||||
.invisible {
|
||||
.hide-me {
|
||||
position: absolute;
|
||||
width:0;
|
||||
height:0;
|
||||
|
||||
@@ -149,7 +149,7 @@ export class ImageViewerComponent extends BaseImageViewerComponent implements On
|
||||
}
|
||||
|
||||
changeIteration(value: number) {
|
||||
if (this.iteration === value || this.url) {
|
||||
if (this.iteration === value || this.data.withoutNavigation) {
|
||||
return;
|
||||
}
|
||||
this.iteration = value;
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
[moveLegendToTitle]="groupBy === groupByCharts.none"
|
||||
[identifier]="generateIdentifier(chartItem)"
|
||||
[exportForReport]="exportForReport"
|
||||
(createEmbedCode)="creatingEmbedCode(chartItem)"
|
||||
(createEmbedCode)="creatingEmbedCode(chartItem, $event)"
|
||||
(hoverModeChanged)="hoverModeChanged.emit($event)"
|
||||
>
|
||||
</sm-single-graph>
|
||||
|
||||
@@ -65,7 +65,7 @@ export class ExperimentGraphsComponent implements OnDestroy {
|
||||
public graphList: Array<any> = [];
|
||||
public noGraphs: boolean = false;
|
||||
public graphsData: { [group: string]: VisibleExtFrame[] };
|
||||
public visibleGraphsData: {[graphId: string]: boolean} = {};
|
||||
public visibleGraphsData: { [graphId: string]: boolean } = {};
|
||||
private observer: IntersectionObserver;
|
||||
private _xAxisType: ScalarKeyEnum;
|
||||
@ViewChild('allMetrics', {static: true}) allMetrics: ElementRef;
|
||||
@@ -130,7 +130,7 @@ export class ExperimentGraphsComponent implements OnDestroy {
|
||||
|
||||
@Input() exportForReport = true;
|
||||
@Output() hoverModeChanged = new EventEmitter<ChartHoverModeEnum>();
|
||||
@Output() createEmbedCode = new EventEmitter<{metrics?: string[]; variants?: string[]}>();
|
||||
@Output() createEmbedCode = new EventEmitter<{ metrics?: string[]; variants?: string[]; domRect: DOMRect }>();
|
||||
|
||||
@ViewChildren('metricGroup') allMetricGroups !: QueryList<ElementRef>;
|
||||
@ViewChildren('singleGraphContainer') singleGraphs !: QueryList<ElementRef>;
|
||||
@@ -411,13 +411,26 @@ export class ExperimentGraphsComponent implements OnDestroy {
|
||||
return Object.entries(sortGraphsData1).reduce((acc, [label, graphs]) =>
|
||||
({
|
||||
...acc,
|
||||
[label]: graphs.map((graph) => ({...graph, id: v4(), visible: this.visibleGraphsData[this.generateIdentifier(graph)]}))
|
||||
[label]: graphs.map((graph) => ({
|
||||
...graph,
|
||||
id: v4(),
|
||||
visible: this.visibleGraphsData[this.generateIdentifier(graph)]
|
||||
}))
|
||||
}), {} as { [label: string]: VisibleExtFrame[] });
|
||||
}
|
||||
|
||||
public generateIdentifier = (chartItem: any) => `${this.singleGraphidPrefix} ${this.experimentGraphidPrefix} ${chartItem.metric} ${chartItem.layout.title} ${chartItem.iter} ${chartItem.variant} ${(chartItem.layout.images && chartItem.layout.images[0]?.source)}`;
|
||||
|
||||
creatingEmbedCode(chartItem: any) {
|
||||
this.createEmbedCode.emit({metrics: [chartItem.metric], variants: [chartItem.variant]});
|
||||
creatingEmbedCode(chartItem: any, domRect: DOMRect) {
|
||||
if (this.groupBy === groupByCharts.none) {
|
||||
// split scalars by variants
|
||||
this.createEmbedCode.emit({
|
||||
metrics: [chartItem.metric.substring(0, chartItem.metric.lastIndexOf('/'))?.trim()],
|
||||
variants: [chartItem.data[0].name],
|
||||
domRect
|
||||
});
|
||||
} else {
|
||||
this.createEmbedCode.emit({metrics: [chartItem.metric], variants: chartItem.variants ?? [chartItem.variant], domRect});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ import {selectColorPreferences} from '../../ui-components/directives/choose-colo
|
||||
import {addUpdateColorPreferences, ColorPreference} from '../../ui-components/directives/choose-color/choose-color.actions';
|
||||
import {getHslContrast, hexToRgb, hslToRgb, RGB2HEX, rgbToHsl} from './color-hash.utils';
|
||||
import stc from 'string-to-color';
|
||||
import hexRgb from 'hex-rgb';
|
||||
|
||||
import tinycolor from 'tinycolor2';
|
||||
export interface ColorCache {[label: string]: number[]}
|
||||
export const DOT_PLACEHOLDER = '--DOT--';
|
||||
|
||||
@@ -34,13 +33,18 @@ export class ColorHashService {
|
||||
.subscribe(preferenceColors => this.batchUpdateColorCache(preferenceColors));
|
||||
}
|
||||
|
||||
public initColor(label: string, initColor?: number[]) {
|
||||
public initColor(label: string, initColor?: number[], lighten = false) {
|
||||
const colorCache = this._colorCache.getValue()?.[label];
|
||||
if (colorCache) {
|
||||
return colorCache;
|
||||
}
|
||||
const {red, green, blue} = hexRgb(stc(label));
|
||||
const color = initColor? initColor: [red, green, blue];
|
||||
const tColor = tinycolor(stc(label));
|
||||
const tLum = tColor.getLuminance();
|
||||
if (tLum < 0.3 && lighten) {
|
||||
tColor.lighten(30 - tLum * 100);
|
||||
}
|
||||
const {r, g, b} = tColor.toRgb();
|
||||
const color = initColor? initColor: [r, g, b];
|
||||
this.setColorForString(label, color, false);
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,10 @@ import {ConfigurationService} from '@common/shared/services/configuration.servic
|
||||
import {ClipboardService} from 'ngx-clipboard';
|
||||
import { take } from 'rxjs';
|
||||
|
||||
interface ReportCodeEmbedConfiguration {
|
||||
export interface ReportCodeEmbedConfiguration {
|
||||
type: 'plot' | 'multiplot' | 'scalar' | 'multiscalar' | 'sample';
|
||||
tasks: string[];
|
||||
domRect: DOMRect;
|
||||
name?: string;
|
||||
metrics?: string[];
|
||||
variants?: string[];
|
||||
@@ -21,22 +22,22 @@ interface ReportCodeEmbedConfiguration {
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ReportCodeEmbedService {
|
||||
export class ReportCodeEmbedBaseService {
|
||||
private workspace: GetCurrentUserResponseUserObjectCompany;
|
||||
private isCommunity: boolean;
|
||||
|
||||
constructor(
|
||||
private store: Store,
|
||||
private locationStrategy: LocationStrategy,
|
||||
private configService: ConfigurationService,
|
||||
private _clipboardService: ClipboardService
|
||||
protected store: Store,
|
||||
protected locationStrategy: LocationStrategy,
|
||||
protected configService: ConfigurationService,
|
||||
protected _clipboardService: ClipboardService
|
||||
) {
|
||||
this.isCommunity = this.configService.getStaticEnvironment().communityServer;
|
||||
this.store.select(selectActiveWorkspace).subscribe(workspace => this.workspace = workspace);
|
||||
}
|
||||
|
||||
createCode(conf: ReportCodeEmbedConfiguration) {
|
||||
const code = this.encode(conf);
|
||||
const code = this.generateEmbedSnippet(conf);
|
||||
this._clipboardService.copyResponse$
|
||||
.pipe(take(1))
|
||||
.subscribe(() => this.store.dispatch(addMessage(MESSAGES_SEVERITY.SUCCESS,
|
||||
@@ -44,8 +45,7 @@ export class ReportCodeEmbedService {
|
||||
);
|
||||
this._clipboardService.copy(code);
|
||||
}
|
||||
|
||||
encode(conf: ReportCodeEmbedConfiguration) {
|
||||
encodeSrc(conf: ReportCodeEmbedConfiguration, internal = true){
|
||||
const url = new URL(window.location.origin + this.locationStrategy.getBaseHref());
|
||||
url.pathname = url.pathname + 'widgets/';
|
||||
url.searchParams.set('type', conf.type);
|
||||
@@ -55,9 +55,11 @@ export class ReportCodeEmbedService {
|
||||
urlStr += '&' + conf[key].map(val => `${key}=${encodeURIComponent(val)}`).join('&');
|
||||
}
|
||||
});
|
||||
if (this.configService && this.workspace?.id) {
|
||||
urlStr += `&company=${this.workspace.id}`;
|
||||
}
|
||||
return urlStr;
|
||||
}
|
||||
|
||||
generateEmbedSnippet(conf: ReportCodeEmbedConfiguration) {
|
||||
const urlStr = this.encodeSrc(conf);
|
||||
return `<iframe
|
||||
src="${urlStr}"
|
||||
name="${conf.name || v4()}"
|
||||
@@ -1,32 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ReportCodeEmbedService } from './report-code-embed.service';
|
||||
import {LocationStrategy} from '@angular/common';
|
||||
import {Store} from '@ngrx/store';
|
||||
|
||||
describe('ReportCodeEmbedService', () => {
|
||||
let service: ReportCodeEmbedService;
|
||||
let store: Store;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ReportCodeEmbedService);
|
||||
store = TestBed.inject(Store);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should create a link with multiple metrics', () => {
|
||||
const fakeUrl = 'http://fake-url/';
|
||||
const fake = { getBaseHref: () => fakeUrl };
|
||||
service = new ReportCodeEmbedService(store, fake as LocationStrategy);
|
||||
expect(service.encode({
|
||||
type: 'plot',
|
||||
task: '123',
|
||||
name: 'test1',
|
||||
metrics: ['m1', 'm2']
|
||||
})).toMatch(/.*metrics=m1&metrics=m2.*/);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {setTagColors, TagColor} from '../../core/actions/projects.actions';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {selectTagColors, selectTagsColors} from '../../core/reducers/projects.reducer';
|
||||
import {Subscription} from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
|
||||
@Injectable({
|
||||
|
||||
@@ -50,6 +50,9 @@ import {ScrollingModule} from '@angular/cdk/scrolling';
|
||||
import {MainPagesHeaderFilterComponent} from './components/main-pages-header-filter/main-pages-header-filter.component';
|
||||
import {MarkdownEditorComponent} from '@common/shared/components/markdown-editor/markdown-editor.component';
|
||||
import {LMarkdownEditorModule} from 'ngx-markdown-editor';
|
||||
import {
|
||||
MarkdownCheatSheetDialogComponent
|
||||
} from './components/markdown-editor/markdown-cheat-sheet-dialog/markdown-cheat-sheet-dialog.component';
|
||||
|
||||
const _declarations = [
|
||||
ExperimentInfoHeaderStatusProgressBarComponent,
|
||||
@@ -103,11 +106,12 @@ const _declarations = [
|
||||
ClearFiltersButtonComponent,
|
||||
BaseEntityHeaderComponent,
|
||||
IdBadgeComponent,
|
||||
MarkdownCheatSheetDialogComponent,
|
||||
],
|
||||
exports: [..._declarations, UiComponentsModule, TableModule, ClipboardModule, SharedPipesModule, MatSnackBarModule,
|
||||
ScatterPlotComponent, ClearFiltersButtonComponent, IdBadgeComponent
|
||||
],
|
||||
providers : [LeavingBeforeSaveAlertGuard, GeneralLeavingBeforeSaveAlertGuard]
|
||||
providers: [LeavingBeforeSaveAlertGuard, GeneralLeavingBeforeSaveAlertGuard]
|
||||
})
|
||||
export class SMSharedModule {
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
[isDarkTheme]="darkTheme"
|
||||
[graphsNumber]="9999"
|
||||
[exportForReport]="!!embedFunction"
|
||||
(createEmbedCode)="embedFunction()"
|
||||
(createEmbedCode)="embedFunction($event)"
|
||||
>
|
||||
</sm-single-graph>
|
||||
<div *ngIf="!isFullDetailsMode && !isCompare && !darkTheme" class="navigation">
|
||||
|
||||
@@ -66,7 +66,7 @@ export class GraphViewerComponent implements AfterViewInit, OnInit, OnDestroy {
|
||||
private isForward: boolean = true;
|
||||
private charts: ExtFrame[];
|
||||
public index: number = null;
|
||||
public embedFunction: () => null;
|
||||
public embedFunction: (DOMRect) => null;
|
||||
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
onKeyDown(e: KeyboardEvent) {
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface ExtFrame extends Omit<Frame, 'data' | 'layout'> {
|
||||
timestamp: number;
|
||||
type: string;
|
||||
variant: string;
|
||||
variants?: string[];
|
||||
worker: string;
|
||||
data: ExtData[];
|
||||
layout: Partial<ExtLayout>;
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
}
|
||||
|
||||
::ng-deep .js-plotly-plot .plotly {
|
||||
.modebar-group {
|
||||
.modebar-group:not(:empty) {
|
||||
border-left: 1px solid $blue-500;
|
||||
|
||||
&:last-child {
|
||||
@@ -141,6 +141,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.modebar-group:empty {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.node-label {
|
||||
fill: white !important;;
|
||||
|
||||
@@ -140,9 +140,10 @@ export class SingleGraphComponent extends PlotlyGraphBaseComponent {
|
||||
}
|
||||
|
||||
@Input() exportForReport = false;
|
||||
@Input() hideDownloadButtons = false;
|
||||
@Input() noMargins = false;
|
||||
@Output() hoverModeChanged = new EventEmitter<ChartHoverModeEnum>();
|
||||
@Output() createEmbedCode = new EventEmitter();
|
||||
@Output() createEmbedCode = new EventEmitter<DOMRect>();
|
||||
@Output() maximizeClicked = new EventEmitter();
|
||||
@ViewChild('drawHere', {static: true}) plotlyContainer: ElementRef;
|
||||
private chartElm;
|
||||
@@ -244,7 +245,8 @@ export class SingleGraphComponent extends PlotlyGraphBaseComponent {
|
||||
|
||||
const graph = this.formatChartLines() as ExtFrame;
|
||||
this.type = getOr(graph.layout.type, 'data[0].type', graph);
|
||||
this.title = this.isDarkTheme ? '' : this.addIterationString(graph.layout.title as string, graph.iter) || (graph.layout.title as Record<string, any>).text;
|
||||
const title = graph.variants?.length > 1? '' : this.addIterationString(graph.layout.title as string, graph.iter) || (graph.layout.title as Record<string, any>).text;
|
||||
this.title = this.isDarkTheme ? '' : title;
|
||||
let layout = {
|
||||
...this.addParametersIfDarkTheme({
|
||||
font: {
|
||||
@@ -514,14 +516,16 @@ export class SingleGraphComponent extends PlotlyGraphBaseComponent {
|
||||
}
|
||||
});
|
||||
}
|
||||
modeBarButtonsToAdd.push({
|
||||
name: 'Download JSON',
|
||||
title: 'Download JSON',
|
||||
icon: this.getJsonDownloadIcon(),
|
||||
click: () => {
|
||||
this.downloadGraphAsJson(cloneDeep(this.originalChart));
|
||||
}
|
||||
});
|
||||
if (!this.hideDownloadButtons) {
|
||||
modeBarButtonsToAdd.push({
|
||||
name: 'Download JSON',
|
||||
title: 'Download JSON',
|
||||
icon: this.getJsonDownloadIcon(),
|
||||
click: () => {
|
||||
this.downloadGraphAsJson(cloneDeep(this.originalChart));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.type === 'table') {
|
||||
modeBarButtonsToAdd.push({
|
||||
name: 'Download CSV',
|
||||
@@ -536,11 +540,12 @@ export class SingleGraphComponent extends PlotlyGraphBaseComponent {
|
||||
if (this.exportForReport) {
|
||||
const button: ModeBarButton = {
|
||||
name: 'Embed',
|
||||
title: 'Copy to Report',
|
||||
title: 'Copy embed code',
|
||||
attr: 'plotly-embedded-modebar-button',
|
||||
icon: this.getEmbedIcon(),
|
||||
click: () => {
|
||||
this.createEmbedCode.emit();
|
||||
click: (event) => {
|
||||
this.createEmbedCode.emit(event.querySelector('[data-title="Copy embed code"]').getBoundingClientRect()
|
||||
);
|
||||
}
|
||||
};
|
||||
modeBarButtonsToAdd.push(button);
|
||||
@@ -549,7 +554,7 @@ export class SingleGraphComponent extends PlotlyGraphBaseComponent {
|
||||
if (this.hideMaximize !== 'hide') {
|
||||
const maximizeButton: ModeBarButton = {
|
||||
name: 'Maximize',
|
||||
title: this.hideMaximize === 'disabled' ? `Can't maximize because an iframe with the same name exists`: 'Maximize Graph',
|
||||
title: this.hideMaximize === 'disabled' ? `Can't maximize because an iframe with the same name exists` : 'Maximize Graph',
|
||||
attr: this.hideMaximize === 'disabled' ? 'plotly-disabled-maximize' : '',
|
||||
icon: this.getMaximizeIcon(),
|
||||
click: () => {
|
||||
@@ -560,7 +565,7 @@ export class SingleGraphComponent extends PlotlyGraphBaseComponent {
|
||||
}
|
||||
|
||||
const config = {
|
||||
modeBarButtonsToRemove: ['sendDataToCloud'] as ModeBarDefaultButtons[],
|
||||
modeBarButtonsToRemove: (this.hideDownloadButtons ? ['sendDataToCloud', 'toImage']: ['sendDataToCloud']) as ModeBarDefaultButtons[],
|
||||
displaylogo: false,
|
||||
modeBarButtonsToAdd
|
||||
};
|
||||
@@ -617,7 +622,7 @@ export class SingleGraphComponent extends PlotlyGraphBaseComponent {
|
||||
const colorKey = this.extractColorKey(graph.data[i].name);
|
||||
const originalColor = this.getOriginalColor(i);
|
||||
if (!Array.isArray(originalColor)) {
|
||||
const color = (originalColor && !this.colorHash.hasColor(colorKey)) ? hexToRgb(this.getOriginalColor(i)) : this.colorHash.initColor(colorKey);
|
||||
const color = (originalColor && !this.colorHash.hasColor(colorKey)) ? hexToRgb(this.getOriginalColor(i)) : this.colorHash.initColor(colorKey, null, this.isDarkTheme);
|
||||
// if (this.colorHash.hasColor(colorKey)) { // We don't save init color in color cache, so we need to recolor everytime
|
||||
this._reColorTrace(graph.data[i], color);
|
||||
// }
|
||||
@@ -885,7 +890,7 @@ export class SingleGraphComponent extends PlotlyGraphBaseComponent {
|
||||
this.zone.run(() => {
|
||||
this.dialog.open(GraphViewerComponent, {
|
||||
data: {
|
||||
...(this.exportForReport && {embedFunction: () => this.createEmbedCode.emit()}),
|
||||
...(this.exportForReport && {embedFunction: (rect: DOMRect) => this.createEmbedCode.emit(rect)}),
|
||||
// signed url are updated after originChart was cloned - need to update images urls!
|
||||
chart: cloneDeep({
|
||||
...this.originalChart,
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
*ngFor="let option of subOptions; trackBy: trackByLabel"
|
||||
[itemLabel]="option.label"
|
||||
[checked]="subValue?.includes(option.value)" [selectable]="true" [itemValue]="option.value"
|
||||
(itemClicked)="onSubFilterChanged({id:'system_tags'},$event)"
|
||||
(itemClicked)="onSubFilterChanged({id :'system_tags'}, $event)"
|
||||
></sm-menu-item>
|
||||
</div>
|
||||
</mat-menu>
|
||||
|
||||
@@ -135,6 +135,7 @@
|
||||
tr {
|
||||
display: flex;
|
||||
transition: height 0.5s linear;
|
||||
width: 100%;
|
||||
|
||||
&.header {
|
||||
height: 48px;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.file-path {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
appearance="legacy"
|
||||
(click)="valueChanged.emit(option.value)"
|
||||
[smTooltip]="option.icon ? option.label : ''"
|
||||
|
||||
matTooltipShowDelay="500"
|
||||
[appendComponentOnTopElement]="option.ripple && rippleEffect" [componentToAppend]="rippleComponent"
|
||||
>
|
||||
|
||||
@@ -39,7 +39,7 @@ $inline-edit-base-width: 93%;
|
||||
:host {
|
||||
::ng-deep .ui-inplace .ui-inplace-display {
|
||||
background-color: transparent;
|
||||
padding: 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.inline-edit-close, .inline-edit-approve {
|
||||
@@ -53,8 +53,9 @@ $inline-edit-base-width: 93%;
|
||||
}
|
||||
|
||||
.inline-edit-approve.disabled {
|
||||
opacity: 0.25;
|
||||
cursor: not-allowed !important;
|
||||
opacity: 0.25;
|
||||
cursor: not-allowed !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.inline-edit-form {
|
||||
@@ -101,12 +102,6 @@ $inline-edit-base-width: 93%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.editable-pen {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(50% - 10px);
|
||||
}
|
||||
}
|
||||
|
||||
.editable-div {
|
||||
|
||||
@@ -6,6 +6,9 @@ import {ExtData, ExtFrame, ExtLayout} from '../shared/single-graph/plotly-graph-
|
||||
import {MetricsPlotEvent} from '../../business-logic/model/events/models';
|
||||
import {KeyValue} from '@angular/common';
|
||||
|
||||
export interface ExtMetricsPlotEvent extends MetricsPlotEvent{
|
||||
variants?: Array<string>;
|
||||
}
|
||||
export interface IMultiplot {
|
||||
[key: string]: { // i.e ROC
|
||||
[key: number]: { // Iteration number
|
||||
@@ -49,7 +52,7 @@ export const getModelDesign = (modelDesc: Task['execution']['model_desc']): { ke
|
||||
return {key, value: key ? modelDesc[key] : modelDesc};
|
||||
};
|
||||
|
||||
export const prepareGraph = (data: ExtData[], layout: Partial<ExtLayout>, config: Partial<Config>, graph: Partial<MetricsPlotEvent>): Partial<ExtFrame> =>
|
||||
export const prepareGraph = (data: ExtData[], layout: Partial<ExtLayout>, config: Partial<Config>, graph: Partial<ExtMetricsPlotEvent>): Partial<ExtFrame> =>
|
||||
({
|
||||
data,
|
||||
layout,
|
||||
@@ -60,6 +63,7 @@ export const prepareGraph = (data: ExtData[], layout: Partial<ExtLayout>, config
|
||||
timestamp: graph.timestamp,
|
||||
type: graph.type as unknown as string,
|
||||
variant: graph.variant,
|
||||
variants: graph.variants,
|
||||
worker: graph['worker'],
|
||||
});
|
||||
|
||||
@@ -124,7 +128,7 @@ export const convertScalars = (scalars: GroupedList, experimentId: string): { [k
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const groupIterations = (plots: MetricsPlotEvent[]): { [title: string]: MetricsPlotEvent[] } => {
|
||||
export const groupIterations = (plots: MetricsPlotEvent[]): { [title: string]: ExtMetricsPlotEvent[] } => {
|
||||
if (!plots.length) {
|
||||
return {};
|
||||
}
|
||||
@@ -139,8 +143,9 @@ export const groupIterations = (plots: MetricsPlotEvent[]): { [title: string]: M
|
||||
if (index > -1 && plotParsed.data && plotParsed.data[0] && ['scatter', 'bar'].includes(plotParsed.data[0]?.type) && previousPlotIsMergable) {
|
||||
const basePlotParsed = tryParseJson(groupedPlots[metric][index].plot_str);
|
||||
groupedPlots[metric][index].plot_str = _mergePlotsData(basePlotParsed, plotParsed);
|
||||
groupedPlots[metric][index].variants.push(plot.variant);
|
||||
} else {
|
||||
groupedPlots[metric].push(plot);
|
||||
groupedPlots[metric].push({...plot, variants: [plot.variant]});
|
||||
}
|
||||
previousPlotIsMergable = index > -1 || (index === -1 && ['scatter', 'bar'].includes(plotParsed.data[0]?.type));
|
||||
return groupedPlots;
|
||||
|
||||
Reference in New Issue
Block a user