117 Commits

Author SHA1 Message Date
d9cff42f5c chore(release): 6.15.0 [skip ci]
# [6.15.0](https://git.datacontroller.io/dc/dc/compare/v6.14.10...v6.15.0) (2025-06-04)

### Bug Fixes

* makedata with context name ([da4d0b2](da4d0b28c7))

### Features

* viya deploy context ([6c96ef7](6c96ef7fb0))
2025-06-04 09:26:02 +00:00
2a3d2b8d0d Merge pull request 'feat: viya deploy context' (#163) from deploy-context into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m3s
Release / Build-and-test-development (push) Successful in 8m39s
Release / release (push) Successful in 8m30s
Reviewed-on: #163
2025-06-04 09:09:28 +00:00
d0f453d291 chore: typo
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m59s
Build / Build-and-test-development (pull_request) Successful in 8m31s
2025-06-04 09:41:06 +02:00
8e65dd0eae style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m59s
Build / Build-and-test-development (pull_request) Successful in 8m25s
2025-06-03 21:06:48 +02:00
da4d0b28c7 fix: makedata with context name
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Successful in 8m32s
2025-06-03 21:00:54 +02:00
6c96ef7fb0 feat: viya deploy context
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 47s
Build / Build-and-test-development (pull_request) Successful in 8m35s
2025-06-03 20:22:39 +02:00
997f09adde chore(release): 6.14.10 [skip ci]
## [6.14.10](https://git.datacontroller.io/dc/dc/compare/v6.14.9...v6.14.10) (2025-06-02)

### Bug Fixes

* bump core ([0e8503e](0e8503ed2b))
* default to home directory for SAS Drive in Viya ([9682b54](9682b548e6))
2025-06-02 17:51:20 +00:00
0e8503ed2b fix: bump core
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m31s
Release / Build-and-test-development (push) Successful in 8m40s
Release / release (push) Successful in 8m20s
2025-06-02 18:32:33 +01:00
97dfcd79b1 Merge pull request 'issue156' (#161) from issue156 into main
Some checks failed
Release / Build-and-test-development (push) Has been cancelled
Release / release (push) Has been cancelled
Release / Build-production-and-ng-test (push) Has been cancelled
Reviewed-on: #161
2025-06-02 17:29:18 +00:00
9a12a2e41f Merge branch 'main' into issue156
Some checks failed
Build / Build-and-ng-test (pull_request) Successful in 4m9s
Build / Build-and-test-development (pull_request) Has been cancelled
2025-06-02 17:27:14 +00:00
5c114e562b chore(release): 6.14.9 [skip ci]
## [6.14.9](https://git.datacontroller.io/dc/dc/compare/v6.14.8...v6.14.9) (2025-06-02)

### Bug Fixes

* default DC path for viya ([f3125ff](f3125ff464))
2025-06-02 10:36:05 +00:00
ae696a0be0 Merge pull request 'Viya default DC Path by user name' (#162) from viya-smooth-deploy into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m56s
Release / Build-and-test-development (push) Successful in 8m33s
Release / release (push) Successful in 8m22s
Reviewed-on: #162
2025-06-02 10:19:51 +00:00
22d46a5dcc style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m8s
Build / Build-and-test-development (pull_request) Successful in 8m34s
2025-06-02 11:24:30 +02:00
f3125ff464 fix: default DC path for viya 2025-06-02 11:24:18 +02:00
9682b548e6 fix: default to home directory for SAS Drive in Viya
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m10s
Build / Build-and-test-development (pull_request) Successful in 8m35s
2025-05-29 15:05:18 +01:00
ec11a74265 chore(release): 6.14.8 [skip ci]
## [6.14.8](https://git.datacontroller.io/dc/dc/compare/v6.14.7...v6.14.8) (2025-05-28)

### Bug Fixes

* CSP issues, clarity local library build, fixed some style issues ([841201a](841201adab))
* deploy page, makedata error handling, added local build of clarity, to address clr-stack-view CSP issues (inline styles) ([7b5e7ae](7b5e7ae184))
* improved deploy flow for Viya ([9604661](9604661f3b))
* requests modal causing VIYA CSP errors ([1dc6934](1dc69341ca))
* sas viya service init timing issue ([9de04e9](9de04e9a0c))
* scss of components transferred to the global styles.scss so we do not cause CSP (inline styles) issues when streaming to Viya ([6c171a6](6c171a6394))
* viya deploy page improved flow ([4bd2154](4bd215491f))
2025-05-28 18:13:32 +00:00
4be0614604 Merge pull request 'chore: package-lock' (#155) from lockfile-fix into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m5s
Release / Build-and-test-development (push) Successful in 8m48s
Release / release (push) Successful in 8m32s
Reviewed-on: #155
2025-05-28 17:56:46 +00:00
27cbff2bc5 chore: remove package.json comment
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m13s
Build / Build-and-test-development (pull_request) Successful in 8m49s
2025-05-28 19:18:32 +02:00
c41c8963f2 chore: package.json comments
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Failing after 50s
2025-05-28 19:03:54 +02:00
7249d4fa29 chore: package-lock
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m14s
Build / Build-and-test-development (pull_request) Successful in 8m50s
2025-05-28 19:01:16 +02:00
2e0c60cc0d chore: fix package-lock 2025-05-28 18:57:43 +02:00
7c5e47f5e4 Merge pull request 'Scss of components transferred to the global styles.scss so we do not cause CSP (inline styles) issues when streaming to Viya' (#153) from css-refactor into main
Some checks failed
Release / Build-production-and-ng-test (push) Failing after 43s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #153
2025-05-28 16:57:20 +00:00
f9decbd366 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 53s
Build / Build-and-test-development (pull_request) Failing after 50s
2025-05-26 15:39:48 +02:00
1dc69341ca fix: requests modal causing VIYA CSP errors 2025-05-26 15:39:28 +02:00
75ae19fa8e style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m11s
Build / Build-and-test-development (pull_request) Failing after 1m18s
2025-05-23 13:35:58 +02:00
9de04e9a0c fix: sas viya service init timing issue
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 48s
Build / Build-and-test-development (pull_request) Failing after 1m15s
2025-05-23 13:35:37 +02:00
983f59cd51 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m11s
Build / Build-and-test-development (pull_request) Failing after 1m11s
2025-05-23 11:32:27 +02:00
7b5e7ae184 fix: deploy page, makedata error handling, added local build of clarity, to address clr-stack-view CSP issues (inline styles) 2025-05-23 11:30:15 +02:00
6e96b1daec style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m57s
Build / Build-and-test-development (pull_request) Successful in 8m28s
2025-05-22 10:47:46 +02:00
9604661f3b fix: improved deploy flow for Viya 2025-05-22 10:47:15 +02:00
4bd215491f fix: viya deploy page improved flow
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m10s
Build / Build-and-test-development (pull_request) Successful in 8m43s
2025-05-21 14:13:03 +02:00
6a7dd451b5 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Failing after 47s
2025-05-21 09:55:05 +02:00
841201adab fix: CSP issues, clarity local library build, fixed some style issues
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Failing after 49s
2025-05-21 09:36:36 +02:00
e013e62776 chore(git): Merge branch 'main' into css-refactor
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 50s
Build / Build-and-test-development (pull_request) Successful in 8m48s
2025-05-13 15:59:08 +02:00
6c171a6394 fix: scss of components transferred to the global styles.scss so we do not cause CSP (inline styles) issues when streaming to Viya 2025-05-13 15:54:57 +02:00
a377f6e8d6 chore(release): 6.14.7 [skip ci]
## [6.14.7](https://git.datacontroller.io/dc/dc/compare/v6.14.6...v6.14.7) (2025-05-08)

### Bug Fixes

* updated hot, clarity and improved accessibility score. ([2844c70](2844c70f95))
2025-05-08 11:54:20 +00:00
0337318e0b Merge pull request 'Updated hot, clarity and improved accessibility score.' (#152) from hot-clarity-accessiblity-update into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m2s
Release / Build-and-test-development (push) Successful in 8m36s
Release / release (push) Successful in 8m19s
Reviewed-on: #152
Reviewed-by: allan <allan@4gl.io>
2025-05-08 11:37:55 +00:00
2844c70f95 fix: updated hot, clarity and improved accessibility score.
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m10s
Build / Build-and-test-development (pull_request) Successful in 8m41s
2025-05-06 15:58:30 +02:00
7e11c8f375 chore(release): 6.14.6 [skip ci]
## [6.14.6](https://git.datacontroller.io/dc/dc/compare/v6.14.5...v6.14.6) (2025-04-03)

### Bug Fixes

* history table modal links styling ([c63fcdd](c63fcdd465))
2025-04-03 08:32:36 +00:00
23cbbce964 Merge pull request 'History table modal links styling' (#151) from history-links into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m47s
Release / Build-and-test-development (push) Successful in 8m19s
Release / release (push) Successful in 7m55s
Reviewed-on: #151
2025-04-03 08:16:57 +00:00
c63fcdd465 fix: history table modal links styling
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m58s
Build / Build-and-test-development (pull_request) Successful in 8m24s
2025-04-02 19:15:29 +02:00
82412b2659 chore(release): 6.14.5 [skip ci]
## [6.14.5](https://git.datacontroller.io/dc/dc/compare/v6.14.4...v6.14.5) (2025-03-24)

### Bug Fixes

* improving accessibility lighthouse score ([7f3577c](7f3577c3ef))
* prevent errors when using sqlrc in a DI job in a HOOK ([d1f0879](d1f0879f0a))
* user profile style fix, new select library and table icons ([69f8830](69f883034f))
2025-03-24 23:10:35 +00:00
eef3832e40 Merge pull request 'User profile style fix, new select library and table icons, improved accessibility score' (#150) from styling into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m53s
Release / Build-and-test-development (push) Successful in 8m14s
Release / release (push) Successful in 8m14s
Reviewed-on: #150
2025-03-24 22:42:40 +00:00
63b75a1c61 Merge branch 'main' into styling
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m51s
Build / Build-and-test-development (pull_request) Successful in 8m14s
2025-03-24 22:42:31 +00:00
d1f0879f0a fix: prevent errors when using sqlrc in a DI job in a HOOK
Some checks failed
Release / release (push) Blocked by required conditions
Release / Build-production-and-ng-test (push) Successful in 3m54s
Release / Build-and-test-development (push) Has been cancelled
2025-03-24 21:35:17 +00:00
36416aab2e style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m53s
Build / Build-and-test-development (pull_request) Successful in 8m16s
2025-03-21 14:08:57 +01:00
7f3577c3ef fix: improving accessibility lighthouse score 2025-03-21 14:08:24 +01:00
69f883034f fix: user profile style fix, new select library and table icons
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 49s
Build / Build-and-test-development (pull_request) Successful in 8m21s
2025-03-20 17:33:35 +01:00
1c56af01d0 chore(release): 6.14.4 [skip ci]
## [6.14.4](https://git.datacontroller.io/dc/dc/compare/v6.14.3...v6.14.4) (2025-03-18)

### Bug Fixes

* removing cli dependency warnings2 ([43c0f73](43c0f73c21))
2025-03-18 14:11:11 +00:00
43c0f73c21 fix: removing cli dependency warnings2
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m57s
Release / Build-and-test-development (push) Successful in 8m16s
Release / release (push) Successful in 8m11s
2025-03-18 13:45:55 +00:00
e8cd3d63da chore: bumping CLI and core
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 3m55s
Release / Build-and-test-development (push) Successful in 8m14s
Release / release (push) Failing after 3m39s
2025-03-18 11:38:16 +00:00
b7f564cb21 chore(release): 6.14.3 [skip ci]
## [6.14.3](https://git.datacontroller.io/dc/dc/compare/v6.14.2...v6.14.3) (2025-03-15)

### Bug Fixes

* NLDAT & NLDATM formats are now being staged ([3f5cb1e](3f5cb1e2de))
2025-03-15 23:06:45 +00:00
3f5cb1e2de fix: NLDAT & NLDATM formats are now being staged
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m54s
Release / Build-and-test-development (push) Successful in 8m17s
Release / release (push) Successful in 8m34s
2025-03-15 22:50:42 +00:00
7d8c0472f0 chore(release): 6.14.2 [skip ci]
## [6.14.2](https://git.datacontroller.io/dc/dc/compare/v6.14.1...v6.14.2) (2025-03-10)

### Bug Fixes

* improving instructions for setup ([83b3d77](83b3d775b6))
2025-03-10 16:13:24 +00:00
b8b516ba77 Merge pull request 'fix: improving instructions for setup' (#146) from intro into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 5m4s
Release / Build-and-test-development (push) Successful in 9m56s
Release / release (push) Successful in 10m43s
Reviewed-on: #146
2025-03-10 15:53:45 +00:00
83b3d775b6 fix: improving instructions for setup
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m14s
Build / Build-and-test-development (pull_request) Successful in 9m48s
2025-03-07 14:44:12 +00:00
aea252ccc6 chore(release): 6.14.1 [skip ci]
## [6.14.1](https://git.datacontroller.io/dc/dc/compare/v6.14.0...v6.14.1) (2025-03-05)

### Bug Fixes

* handle national language datetime formats ([149e318](149e318a87))
* updating logic to use NLDAT formats ([95289aa](95289aa952))
2025-03-05 15:28:45 +00:00
d8908f9c7f Merge pull request 'fix: handle national language datetime formats' (#144) from fix-nldatm into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m48s
Release / Build-and-test-development (push) Successful in 9m38s
Release / release (push) Successful in 10m26s
Reviewed-on: #144
2025-03-05 14:50:58 +00:00
95289aa952 fix: updating logic to use NLDAT formats
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m3s
Build / Build-and-test-development (pull_request) Successful in 9m47s
2025-03-05 14:49:56 +00:00
149e318a87 fix: handle national language datetime formats
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m9s
Build / Build-and-test-development (pull_request) Successful in 9m58s
2025-03-05 14:34:51 +01:00
8657826e60 chore(release): 6.14.0 [skip ci]
# [6.14.0](https://git.datacontroller.io/dc/dc/compare/v6.13.2...v6.14.0) (2025-02-26)

### Features

* uses SORTSEQ=LINGUISTIC for the services/metanav/metadetails service ([a45f5bb](a45f5bb3b2))
2025-02-26 19:44:07 +00:00
a45f5bb3b2 feat: uses SORTSEQ=LINGUISTIC for the services/metanav/metadetails service
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m49s
Release / Build-and-test-development (push) Successful in 9m42s
Release / release (push) Successful in 10m27s
2025-02-26 19:24:53 +00:00
ae9a91a7a1 chore: bumping sasjs/core to 4.54
Some checks failed
Release / Build-and-test-development (push) Has been cancelled
Release / release (push) Has been cancelled
Release / Build-production-and-ng-test (push) Has been cancelled
2025-02-26 19:21:07 +00:00
3638bde633 chore(release): 6.13.2 [skip ci]
## [6.13.2](https://git.datacontroller.io/dc/dc/compare/v6.13.1...v6.13.2) (2025-02-26)

### Bug Fixes

* get metadata email if exists ([1bd0eef](1bd0eef913))
2025-02-26 12:25:46 +00:00
3a2361f42c Merge pull request 'fix: get metadata email if exists' (#143) from fix-mpe_alerts-metadata-emails into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m45s
Release / Build-and-test-development (push) Successful in 9m37s
Release / release (push) Successful in 10m32s
Reviewed-on: #143
2025-02-26 12:06:51 +00:00
1bd0eef913 fix: get metadata email if exists
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m4s
Build / Build-and-test-development (pull_request) Successful in 9m53s
2025-02-26 12:19:58 +01:00
85aa3b38b7 Merge pull request 'fix: Added missing=STRING to enable national chars' (#142) from metadetails-national-chars into main
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 4m55s
Release / Build-and-test-development (push) Successful in 9m52s
Release / release (push) Failing after 4m37s
Reviewed-on: #142
2025-02-18 11:59:51 +00:00
678859a68d Merge branch 'main' into metadetails-national-chars
Some checks failed
Build / Build-and-test-development (pull_request) Has been cancelled
Build / Build-and-ng-test (pull_request) Has been cancelled
2025-02-18 11:54:15 +00:00
e531acee3f Added missing=STRING to enable national chars
Some checks failed
Build / Build-and-ng-test (pull_request) Successful in 5m25s
Build / Build-and-test-development (pull_request) Has been cancelled
2025-02-18 12:46:45 +01:00
4a45ebfe3b chore(release): 6.13.1 [skip ci]
## [6.13.1](https://git.datacontroller.io/dc/dc/compare/v6.13.0...v6.13.1) (2025-02-18)

### Bug Fixes

* Avoiding LATIN1 unprintables in various UI locations ([bce1fd5](bce1fd57ef))
* updated @sasjs/adapter, crypto-browserify ([4e64f28](4e64f28732))
2025-02-18 10:37:07 +00:00
8fb9a344f2 Merge pull request 'fix: updated @sasjs/adapter, crypto-browserify' (#141) from vulnerabilities-fix into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m49s
Release / Build-and-test-development (push) Successful in 9m37s
Release / release (push) Successful in 10m17s
Reviewed-on: #141
2025-02-18 10:18:09 +00:00
8829b60220 chore: licence checker
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m57s
Build / Build-and-test-development (pull_request) Successful in 9m39s
2025-02-18 10:31:46 +01:00
4e64f28732 fix: updated @sasjs/adapter, crypto-browserify
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m43s
Build / Build-and-test-development (pull_request) Successful in 9m56s
2025-02-18 10:23:42 +01:00
a0749de700 Merge pull request 'fix: Avoiding LATIN1 unprintables in various UI locations' (#140) from issue137 into main
Some checks failed
Release / Build-production-and-ng-test (push) Failing after 1m39s
Release / Build-and-test-development (push) Has been skipped
Release / release (push) Has been skipped
Reviewed-on: #140
2025-02-17 16:36:57 +00:00
f9623e046e Merge branch 'main' into issue137
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m12s
Build / Build-and-test-development (pull_request) Successful in 9m45s
2025-02-17 16:36:44 +00:00
bce1fd57ef fix: Avoiding LATIN1 unprintables in various UI locations
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m8s
2025-02-17 16:35:18 +00:00
c6a1c53b46 chore(release): 6.13.0 [skip ci]
# [6.13.0](https://git.datacontroller.io/dc/dc/compare/v6.12.3...v6.13.0) (2025-01-31)

### Bug Fixes

* editor page csv upload ([217220f](217220ffaa))

### Features

* csv test ([c53ab85](c53ab85107))
2025-01-31 17:18:34 +00:00
e36229e4c4 Merge pull request 'issue-136' (#139) from issue-136 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 5m6s
Release / Build-and-test-development (push) Successful in 9m56s
Release / release (push) Successful in 10m28s
Reviewed-on: #139
2025-01-31 16:58:51 +00:00
853c1bc23e chore: cypress csv test fix
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m28s
Build / Build-and-test-development (pull_request) Successful in 10m1s
2025-01-31 17:25:12 +01:00
3b6f6853bc chore: cypress fix
Some checks failed
Build / Build-and-ng-test (pull_request) Successful in 5m2s
Build / Build-and-test-development (pull_request) Failing after 9m55s
2025-01-31 16:37:15 +01:00
b415437662 chore: package-lock fix
Some checks failed
Build / Build-and-ng-test (pull_request) Successful in 5m18s
Build / Build-and-test-development (pull_request) Has been cancelled
2025-01-31 15:56:58 +01:00
24df878abe ci: fix
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m5s
Build / Build-and-test-development (pull_request) Failing after 1m6s
2025-01-31 15:53:59 +01:00
f474673a14 chore: package-lock.json
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m1s
2025-01-31 11:42:25 +01:00
c53ab85107 feat: csv test
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m7s
2025-01-31 11:19:48 +01:00
5c4dd7c9e3 style: lint
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m8s
2025-01-30 16:25:47 +01:00
217220ffaa fix: editor page csv upload 2025-01-30 16:25:18 +01:00
c89a3049fa chore(release): 6.12.3 [skip ci]
## [6.12.3](https://git.datacontroller.io/dc/dc/compare/v6.12.2...v6.12.3) (2025-01-27)

### Bug Fixes

* adding missing=STRING to three services ([30d5e51](30d5e51d0b))
2025-01-27 15:18:30 +00:00
bb12bff9db Merge pull request 'fix: adding missing=STRING to three services' (#138) from issue137 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m58s
Release / Build-and-test-development (push) Successful in 9m36s
Release / release (push) Successful in 10m29s
Reviewed-on: #138
2025-01-27 14:59:19 +00:00
30d5e51d0b fix: adding missing=STRING to three services
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m8s
This results in the use of data step instead of proc json in the mp_jsonout macro - which results in the correct management of latin1 encoded characters in utf-8 output
2025-01-27 14:51:17 +00:00
cd3bcc0ee3 chore(release): 6.12.2 [skip ci]
## [6.12.2](https://git.datacontroller.io/dc/dc/compare/v6.12.1...v6.12.2) (2025-01-27)

### Bug Fixes

* unnecessary zeros when adding new row (data schema default values) ([a1a9051](a1a90519c5))
2025-01-27 11:30:01 +00:00
ef1ba824c3 Merge pull request 'Unnecessary zeros when adding new row (data schema default values)' (#132) from issue-131 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 5m7s
Release / Build-and-test-development (push) Successful in 9m40s
Release / release (push) Successful in 10m31s
Reviewed-on: #132
2025-01-27 11:10:32 +00:00
bc45e92138 Merge branch 'main' into issue-131
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m38s
2025-01-27 11:04:33 +00:00
c2f36f5419 chore(release): 6.12.1 [skip ci]
## [6.12.1](https://git.datacontroller.io/dc/dc/compare/v6.12.0...v6.12.1) (2024-12-31)

### Bug Fixes

* no upcase of pk fields in MPE_TABLES in delete scenario ([3de095f](3de095fe77)), closes [#134](#134)
* reduce length of tmp table names.  Closes [#130](#130) ([f9c2491](f9c2491ab6))
2024-12-31 15:52:37 +00:00
31a31612fc Merge pull request 'fix: no upcase of pk fields in MPE_TABLES in delete scenario' (#135) from issue134 into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 4m47s
Release / Build-and-test-development (push) Successful in 9m5s
Release / release (push) Successful in 10m20s
Reviewed-on: #135
2024-12-31 15:26:21 +00:00
f9c2491ab6 fix: reduce length of tmp table names. Closes #130
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m55s
2024-12-31 15:25:37 +00:00
3de095fe77 fix: no upcase of pk fields in MPE_TABLES in delete scenario
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 5m3s
closes #134
2024-12-31 15:23:25 +00:00
e4e04a193f chore: fixed tsconfig cypress and angular karma conflicts
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m13s
2024-09-27 14:05:43 +02:00
a1a90519c5 fix: unnecessary zeros when adding new row (data schema default values)
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 1m59s
2024-09-27 13:36:50 +02:00
522979835c chore(release): 6.12.0 [skip ci]
# [6.12.0](https://git.datacontroller.io/dc/dc/compare/v6.11.1...v6.12.0) (2024-09-02)

### Bug Fixes

* added appLoc to the system page ([dd2138a](dd2138ac5e))
* bumping core for passthrough fix, [#124](#124) ([caa9854](caa9854ff0))
* excel with password flow, introducing web worker for XLSX.read ([a3ce367](a3ce367950))
* implemented the new request wrapper usage, added XLSX read with a Web Worker, multi load preview data, full height ([4218da9](4218da91cd))
* **multi load:** xlsx read file ahead of time, while user choose datasets ([6547461](6547461637))
* refactored adapter request wrapper function to return job log as well ([67436f4](67436f4ff9))
* using temporary names for temporary tables ([ce50365](ce503653cd)), closes [#124](#124)

### Features

* searching data in excel files using new algorithm (massive performance improvement) ([bbb725c](bbb725c64c))
2024-09-02 07:50:00 +00:00
fe049256b5 Merge pull request 'Multi Load Tests Fix (timing issues)' (#126) from multi-load-tests into main
All checks were successful
Release / Build-production-and-ng-test (push) Successful in 3m54s
Release / Build-and-test-development (push) Successful in 8m3s
Release / release (push) Successful in 8m24s
Reviewed-on: #126
2024-09-02 07:30:55 +00:00
31d1870198 chore: removed log
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m2s
2024-09-02 09:29:48 +02:00
6edf0dfb31 chore: multi load test fix (timing issues) 2024-09-02 09:29:22 +02:00
3bf3dceaa2 Merge pull request 'feat: searching data in excel files using new algorithm (massive performance improvement)' (#123) from issue-120 into main
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 4m3s
Release / Build-and-test-development (push) Failing after 8m4s
Release / release (push) Has been skipped
Reviewed-on: #123
2024-08-31 14:23:48 +00:00
ce503653cd fix: using temporary names for temporary tables
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 4m6s
Closes #124
2024-08-31 16:19:04 +02:00
caa9854ff0 fix: bumping core for passthrough fix, #124 2024-08-31 16:06:46 +02:00
20c3a338c5 style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m58s
2024-08-12 10:33:54 +02:00
6547461637 fix(multi load): xlsx read file ahead of time, while user choose datasets
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 44s
2024-08-09 16:09:53 +02:00
bbb725c64c feat: searching data in excel files using new algorithm (massive performance improvement)
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 45s
2024-08-07 17:53:52 +02:00
403d08c86a Merge pull request 'XLSX Performance Improvements (multi load)' (#118) from multi-load-performance into main
Some checks failed
Release / Build-production-and-ng-test (push) Successful in 3m55s
Release / Build-and-test-development (push) Failing after 8m2s
Release / release (push) Has been skipped
Reviewed-on: #118
2024-07-26 11:34:20 +00:00
f66d9f511a chore(git): Merge branch 'multi-load-performance' of ssh://git.datacontroller.io:29419/dc/dc into multi-load-performance
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m58s
2024-07-08 10:15:38 +02:00
dd2138ac5e fix: added appLoc to the system page 2024-07-08 10:15:24 +02:00
74e9979c67 Merge branch 'main' into multi-load-performance
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m58s
2024-07-05 13:46:47 +00:00
ddc22e5200 style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m54s
2024-07-05 15:46:16 +02:00
4218da91cd fix: implemented the new request wrapper usage, added XLSX read with a Web Worker, multi load preview data, full height
Some checks failed
Build / Build-and-ng-test (pull_request) Failing after 43s
2024-07-05 15:45:06 +02:00
857b94f44f style: lint
All checks were successful
Build / Build-and-ng-test (pull_request) Successful in 3m58s
2024-07-03 18:01:01 +02:00
a3ce367950 fix: excel with password flow, introducing web worker for XLSX.read 2024-07-03 18:00:41 +02:00
67436f4ff9 fix: refactored adapter request wrapper function to return job log as well 2024-07-02 15:54:59 +02:00
177 changed files with 15384 additions and 11853 deletions

View File

@ -48,4 +48,94 @@ jobs:
- name: Production Build - name: Production Build
run: | run: |
cd client cd client
npm run build npm run build
Build-and-test-development:
runs-on: ubuntu-latest
needs: Build-production-and-ng-test
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v4
with:
node-version: 20.14.0
- name: Write .npmrc file
run: |
touch client/.npmrc
echo '${{ secrets.NPMRC}}' > client/.npmrc
- run: apt-get update
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- run: apt install -y ./google-chrome*.deb;
- run: export CHROME_BIN=/usr/bin/google-chrome
- run: apt-get update -y
- run: apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
- run: apt -y install jq
- name: Write cypress credentials
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
shell: bash
env:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- name: Install dependencies
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
# Install pm2 and prepare SASJS server
- run: npm i -g pm2
- run: curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
- run: unzip linux.zip
- run: touch .env
- run: echo RUN_TIMES=js >> .env
- run: echo NODE_PATH=node >> .env
- run: echo CORS=enable >> .env
- run: echo WHITELIST=http://localhost:4200 >> .env
- run: cat .env
- run: pm2 start api-linux --wait-ready
- name: Deploy mocked services
run: |
cd ./sas/mocks/sasjs
npm install -g @sasjs/cli
npm install -g replace-in-files-cli
sasjs cbd -t server-ci
# sasjs request services/admin/makedata -t server-ci -d ./deploy/makeData4GL.json -c ./deploy/requestConfig.json -o ./output.json
- name: Install ZIP
run: |
apt-get update
apt-get install zip
- name: Prepare and run frontend and cypress
run: |
cd ./client
mv ./cypress.env.example.json ./cypress.env.json
replace-in-files --regex='"username".*' --replacement='"username":"'${{ secrets.CYPRESS_USERNAME_SASJS }}'",' ./cypress.env.json
replace-in-files --regex='"password".*' --replacement='"password":"'${{ secrets.CYPRESS_PWD_SASJS }}'" ' ./cypress.env.json
cat ./cypress.env.json
npm run postinstall
# Prepare index.html to SASJS local
replace-in-files --regex='serverUrl=".*?"' --replacement='serverUrl="http://localhost:5000"' ./src/index.html
replace-in-files --regex='appLoc=".*?"' --replacement='appLoc="/Public/app/devtest"' ./src/index.html
replace-in-files --regex='serverType=".*?"' --replacement='serverType="SASJS"' ./src/index.html
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
cat ./cypress.config.ts
# Start frontend and run cypress
npm start & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
- name: Zip Cypress videos
if: always()
run: |
zip -r cypress-videos ./client/cypress/videos
- name: Add cypress videos artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: cypress-videos.zip
path: cypress-videos.zip

View File

@ -136,7 +136,7 @@ jobs:
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
cat ./cypress.config.ts cat ./cypress.config.ts
# Start frontend and run cypress # Start frontend and run cypress
npm start & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts" npm start & npx wait-on http://localhost:4200 && npx cypress run --browser chrome --spec "cypress/e2e/liveness.cy.ts,cypress/e2e/editor.cy.ts,cypress/e2e/excel-multi-load.cy.ts,cypress/e2e/excel.cy.ts,cypress/e2e/csv.cy.ts,cypress/e2e/filtering.cy.ts,cypress/e2e/licensing.cy.ts"
- name: Zip Cypress videos - name: Zip Cypress videos
if: always() if: always()

View File

@ -1,3 +1,169 @@
# [6.15.0](https://git.datacontroller.io/dc/dc/compare/v6.14.10...v6.15.0) (2025-06-04)
### Bug Fixes
* makedata with context name ([da4d0b2](https://git.datacontroller.io/dc/dc/commit/da4d0b28c7109afd6f96455e1e0e80a40d25a942))
### Features
* viya deploy context ([6c96ef7](https://git.datacontroller.io/dc/dc/commit/6c96ef7fb0a55754a84ff0a8bbab838b78c1acaf))
## [6.14.10](https://git.datacontroller.io/dc/dc/compare/v6.14.9...v6.14.10) (2025-06-02)
### Bug Fixes
* bump core ([0e8503e](https://git.datacontroller.io/dc/dc/commit/0e8503ed2bb22a0fc3924ac929e7f19626772e0a))
* default to home directory for SAS Drive in Viya ([9682b54](https://git.datacontroller.io/dc/dc/commit/9682b548e6106d99d97dcc023a35d93addfd5170))
## [6.14.9](https://git.datacontroller.io/dc/dc/compare/v6.14.8...v6.14.9) (2025-06-02)
### Bug Fixes
* default DC path for viya ([f3125ff](https://git.datacontroller.io/dc/dc/commit/f3125ff4641e47e33cb203228f5b1014ea3343bc))
## [6.14.8](https://git.datacontroller.io/dc/dc/compare/v6.14.7...v6.14.8) (2025-05-28)
### Bug Fixes
* CSP issues, clarity local library build, fixed some style issues ([841201a](https://git.datacontroller.io/dc/dc/commit/841201adab582149b1cca3a42e75f7cac75167f9))
* deploy page, makedata error handling, added local build of clarity, to address clr-stack-view CSP issues (inline styles) ([7b5e7ae](https://git.datacontroller.io/dc/dc/commit/7b5e7ae18414152f9b9d8f2d94fc94de43152003))
* improved deploy flow for Viya ([9604661](https://git.datacontroller.io/dc/dc/commit/9604661f3b76111387bc9474cc26348d73ab112e))
* requests modal causing VIYA CSP errors ([1dc6934](https://git.datacontroller.io/dc/dc/commit/1dc69341cadb837e1f11624d5cf35788bbb98d96))
* sas viya service init timing issue ([9de04e9](https://git.datacontroller.io/dc/dc/commit/9de04e9a0ce016e1a9fb8b19c656077079ddcf2f))
* scss of components transferred to the global styles.scss so we do not cause CSP (inline styles) issues when streaming to Viya ([6c171a6](https://git.datacontroller.io/dc/dc/commit/6c171a6394aba8104fe0f50aa8a4e6b9fa8023a2))
* viya deploy page improved flow ([4bd2154](https://git.datacontroller.io/dc/dc/commit/4bd215491f8cdc68f78bade68e7cb98e07edc81e))
## [6.14.7](https://git.datacontroller.io/dc/dc/compare/v6.14.6...v6.14.7) (2025-05-08)
### Bug Fixes
* updated hot, clarity and improved accessibility score. ([2844c70](https://git.datacontroller.io/dc/dc/commit/2844c70f9507036216b8b621900c2bb9010c1d34))
## [6.14.6](https://git.datacontroller.io/dc/dc/compare/v6.14.5...v6.14.6) (2025-04-03)
### Bug Fixes
* history table modal links styling ([c63fcdd](https://git.datacontroller.io/dc/dc/commit/c63fcdd465950ada439d7d69622a3886e8f3a783))
## [6.14.5](https://git.datacontroller.io/dc/dc/compare/v6.14.4...v6.14.5) (2025-03-24)
### Bug Fixes
* improving accessibility lighthouse score ([7f3577c](https://git.datacontroller.io/dc/dc/commit/7f3577c3ef9f44e55a58bc64fbf89a3a64006dd4))
* prevent errors when using sqlrc in a DI job in a HOOK ([d1f0879](https://git.datacontroller.io/dc/dc/commit/d1f0879f0acf7e816c80f7635fd02f4f284214ed))
* user profile style fix, new select library and table icons ([69f8830](https://git.datacontroller.io/dc/dc/commit/69f883034fabbed31aa5d832e20561c4ae3042db))
## [6.14.4](https://git.datacontroller.io/dc/dc/compare/v6.14.3...v6.14.4) (2025-03-18)
### Bug Fixes
* removing cli dependency warnings2 ([43c0f73](https://git.datacontroller.io/dc/dc/commit/43c0f73c2189ff762986a964caae6b0b108164fc))
## [6.14.3](https://git.datacontroller.io/dc/dc/compare/v6.14.2...v6.14.3) (2025-03-15)
### Bug Fixes
* NLDAT & NLDATM formats are now being staged ([3f5cb1e](https://git.datacontroller.io/dc/dc/commit/3f5cb1e2defe390220e904e4bf04a165cb31fec4))
## [6.14.2](https://git.datacontroller.io/dc/dc/compare/v6.14.1...v6.14.2) (2025-03-10)
### Bug Fixes
* improving instructions for setup ([83b3d77](https://git.datacontroller.io/dc/dc/commit/83b3d775b6e33653b087ca9f4eb3ad5b0dbbd479))
## [6.14.1](https://git.datacontroller.io/dc/dc/compare/v6.14.0...v6.14.1) (2025-03-05)
### Bug Fixes
* handle national language datetime formats ([149e318](https://git.datacontroller.io/dc/dc/commit/149e318a8787be0109f25aeec3a1270ea75a97b2))
* updating logic to use NLDAT formats ([95289aa](https://git.datacontroller.io/dc/dc/commit/95289aa9524d3cb2b1c248cfb84f6b0d0a490c32))
# [6.14.0](https://git.datacontroller.io/dc/dc/compare/v6.13.2...v6.14.0) (2025-02-26)
### Features
* uses SORTSEQ=LINGUISTIC for the services/metanav/metadetails service ([a45f5bb](https://git.datacontroller.io/dc/dc/commit/a45f5bb3b27a86da5f55ff28c9c7669956484ddf))
## [6.13.2](https://git.datacontroller.io/dc/dc/compare/v6.13.1...v6.13.2) (2025-02-26)
### Bug Fixes
* get metadata email if exists ([1bd0eef](https://git.datacontroller.io/dc/dc/commit/1bd0eef913593579771c647d80010ce628c00819))
## [6.13.1](https://git.datacontroller.io/dc/dc/compare/v6.13.0...v6.13.1) (2025-02-18)
### Bug Fixes
* Avoiding LATIN1 unprintables in various UI locations ([bce1fd5](https://git.datacontroller.io/dc/dc/commit/bce1fd57ef397cfdd030552c3f424a8407174729))
* updated @sasjs/adapter, crypto-browserify ([4e64f28](https://git.datacontroller.io/dc/dc/commit/4e64f28732868b07ec2ab403912bd384c62c7e1d))
# [6.13.0](https://git.datacontroller.io/dc/dc/compare/v6.12.3...v6.13.0) (2025-01-31)
### Bug Fixes
* editor page csv upload ([217220f](https://git.datacontroller.io/dc/dc/commit/217220ffaaf688133321cc68d770aaf1e50590a9))
### Features
* csv test ([c53ab85](https://git.datacontroller.io/dc/dc/commit/c53ab85107f10c023117a099cc06321afc3e1f03))
## [6.12.3](https://git.datacontroller.io/dc/dc/compare/v6.12.2...v6.12.3) (2025-01-27)
### Bug Fixes
* adding missing=STRING to three services ([30d5e51](https://git.datacontroller.io/dc/dc/commit/30d5e51d0b9cf27774038476bd90559600952304))
## [6.12.2](https://git.datacontroller.io/dc/dc/compare/v6.12.1...v6.12.2) (2025-01-27)
### Bug Fixes
* unnecessary zeros when adding new row (data schema default values) ([a1a9051](https://git.datacontroller.io/dc/dc/commit/a1a90519c535ca25e00822b4d3358c991ac9662e))
## [6.12.1](https://git.datacontroller.io/dc/dc/compare/v6.12.0...v6.12.1) (2024-12-31)
### Bug Fixes
* no upcase of pk fields in MPE_TABLES in delete scenario ([3de095f](https://git.datacontroller.io/dc/dc/commit/3de095fe7797cde60f0e232c188305fe423c27eb)), closes [#134](https://git.datacontroller.io/dc/dc/issues/134)
* reduce length of tmp table names. Closes [#130](https://git.datacontroller.io/dc/dc/issues/130) ([f9c2491](https://git.datacontroller.io/dc/dc/commit/f9c2491ab6e7b528b7ffc011fd9e45c963c5f6bf))
# [6.12.0](https://git.datacontroller.io/dc/dc/compare/v6.11.1...v6.12.0) (2024-09-02)
### Bug Fixes
* added appLoc to the system page ([dd2138a](https://git.datacontroller.io/dc/dc/commit/dd2138ac5e6067de310e83d16fccc9b9764ba3ff))
* bumping core for passthrough fix, [#124](https://git.datacontroller.io/dc/dc/issues/124) ([caa9854](https://git.datacontroller.io/dc/dc/commit/caa9854ff0431ccbb6ff1d6d3509dc877362cceb))
* excel with password flow, introducing web worker for XLSX.read ([a3ce367](https://git.datacontroller.io/dc/dc/commit/a3ce36795007a4e3b6ac3499ffd119dc3758f387))
* implemented the new request wrapper usage, added XLSX read with a Web Worker, multi load preview data, full height ([4218da9](https://git.datacontroller.io/dc/dc/commit/4218da91cd193aa45346ad7e34ccc00ca89df4fb))
* **multi load:** xlsx read file ahead of time, while user choose datasets ([6547461](https://git.datacontroller.io/dc/dc/commit/65474616379e1dacc1329b3bdc5eb14f34428bb1))
* refactored adapter request wrapper function to return job log as well ([67436f4](https://git.datacontroller.io/dc/dc/commit/67436f4ff9bb4d77d5f897f47a3e3d472981f275))
* using temporary names for temporary tables ([ce50365](https://git.datacontroller.io/dc/dc/commit/ce503653cd9fc36f72fb172bd14816e07c792e14)), closes [#124](https://git.datacontroller.io/dc/dc/issues/124)
### Features
* searching data in excel files using new algorithm (massive performance improvement) ([bbb725c](https://git.datacontroller.io/dc/dc/commit/bbb725c64cc23ed701b189623992408c42fdde8f))
## [6.11.1](https://git.datacontroller.io/dc/dc/compare/v6.11.0...v6.11.1) (2024-07-02) ## [6.11.1](https://git.datacontroller.io/dc/dc/compare/v6.11.0...v6.11.1) (2024-07-02)

View File

@ -69,7 +69,8 @@
], ],
"scripts": [ "scripts": [
"node_modules/marked/marked.min.js" "node_modules/marked/marked.min.js"
] ],
"webWorkerTsConfig": "tsconfig.worker.json"
}, },
"configurations": { "configurations": {
"production": { "production": {
@ -148,7 +149,8 @@
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [], "scripts": [],
"karmaConfig": "karma.conf.js" "karmaConfig": "karma.conf.js",
"webWorkerTsConfig": "tsconfig.worker.json"
} }
}, },
"lint": { "lint": {

View File

@ -0,0 +1,115 @@
import { Callbacks } from 'cypress/types/jquery/index'
const username = Cypress.env('username')
const password = Cypress.env('password')
const hostUrl = Cypress.env('hosturl')
const appLocation = Cypress.env('appLocation')
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
const serverType = Cypress.env('serverType')
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
const fixturePath = 'csvs/'
context('excel tests: ', function () {
this.beforeAll(() => {
cy.visit(`${hostUrl}/SASLogon/logout`)
cy.loginAndUpdateValidKey(true)
})
this.beforeEach(() => {
cy.visit(hostUrl + appLocation)
visitPage('home')
colorLog(
`TEST START ---> ${
Cypress.mocha.getRunner().suite.ctx.currentTest.title
}`,
'#3498DB'
)
})
it('1 | Uploads regular csv file', () => {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
attachExcelFile('regular.csv', () => {
cy.get('#approval-btn', { timeout: 60000 })
.should('be.visible')
// .then(() => {
// cy.get('#hotInstance', { timeout: 30000 })
// .find('div.ht_master.handsontable')
// .find('div.wtHolder')
// .find('div.wtHider')
// .find('div.wtSpreader')
// .find('table.htCore')
// .find('tbody')
// .then((data) => {
// let cell: any = data[0].children[0].children[1]
// expect(cell.innerText).to.equal('0')
// cell = data[0].children[0].children[2]
// expect(cell.innerText).to.equal('44')
// cell = data[0].children[0].children[3]
// expect(cell.innerText).to.equal('abc')
// cell = data[0].children[0].children[6]
// expect(cell.innerText).to.equal('Option abc')
// })
// })
})
})
this.afterEach(() => {
colorLog(`TEST END -------------`, '#3498DB')
})
})
const openTableFromTree = (libNameIncludes: string, tablename: string) => {
cy.get('.app-loading', { timeout: longerCommandTimeout })
.should('not.exist')
.then(() => {
cy.get('.nav-tree clr-tree > clr-tree-node', {
timeout: longerCommandTimeout
}).then((treeNodes: any) => {
let viyaLib
for (let node of treeNodes) {
if (node.innerText.toLowerCase().includes(libNameIncludes)) {
viyaLib = node
break
}
}
cy.get(viyaLib).within(() => {
cy.get('.clr-tree-node-content-container > button').click()
cy.get('.clr-treenode-link').then((innerNodes: any) => {
for (let innerNode of innerNodes) {
if (innerNode.innerText.toLowerCase().includes(tablename)) {
innerNode.click()
break
}
}
})
})
})
})
}
const attachExcelFile = (excelFilename: string, callback?: any) => {
cy.get('.buttonBar button:last-child')
.should('exist')
.click()
.then(() => {
cy.get('input[type="file"]#file-upload')
.attachFile(`/${fixturePath}/${excelFilename}`)
.then(() => {
if (callback) callback()
})
})
}
const visitPage = (url: string) => {
cy.visit(`${hostUrl}${appLocation}/#/${url}`)
}
const colorLog = (msg: string, color: string) => {
console.log('%c' + msg, 'color:' + color + ';font-weight:bold;')
}

View File

@ -192,7 +192,10 @@ const checkIfTreeHasTables = (tables: string[], clickOnNode?: string, callback?:
} }
} }
if (nodeToClick) cy.get(nodeToClick).trigger('click') if (nodeToClick) {
cy.wait(1000)
cy.get(nodeToClick).trigger('click')
}
if (callback) callback(nodesCorrect) if (callback) callback(nodesCorrect)
}) })
@ -201,6 +204,7 @@ const checkIfTreeHasTables = (tables: string[], clickOnNode?: string, callback?:
const submitTables = () => { const submitTables = () => {
cy.get('#submit-all').trigger('click') cy.get('#submit-all').trigger('click')
cy.get('#submit-tables').trigger('click') cy.get('#submit-tables').trigger('click')
cy.wait(1000)
} }
const hasSuccessSubmits = (expectedNoOfSubmits: number, callback: (valid: boolean) => void) => { const hasSuccessSubmits = (expectedNoOfSubmits: number, callback: (valid: boolean) => void) => {

View File

@ -109,13 +109,8 @@ context('excel tests: ', function () {
openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') openTableFromTree(libraryToOpenIncludes, 'mpe_x_test')
attachExcelFile('duplicate_column_excel.xlsx', () => { attachExcelFile('duplicate_column_excel.xlsx', () => {
cy.get('.abortMsg', { timeout: longerCommandTimeout }) submitExcel()
.should('exist') rejectExcel(done)
.then((elements: any) => {
if (elements[0]) {
if (elements[0].innerText.toLowerCase().includes('missing')) done()
}
})
}) })
}) })

View File

@ -0,0 +1,497 @@
PRIMARY_KEY_FIELD,SOME_CHAR,SOME_DROPDOWN,SOME_NUM,SOME_DATE,SOME_DATETIME,SOME_TIME,SOME_SHORTNUM,SOME_BESTNUM
0,abc,Option abc,42,12FEB1960,01JAN1960:00:00:42,0:00:42,3,44
1,more dummy data,Option 2,42,12FEB1960,01JAN1960:00:00:42,0:07:02,3,44
2,even more dummy data,Option 3,42,12FEB1960,01JAN1960:00:00:42,0:02:22,3,44
3,"It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told:",Option 2,1613.001,27FEB1961,01JAN1960:00:07:03,0:00:44,3,44
4,if you can fill the unforgiving minute,Option 1,1613.0011235,02AUG1971,29MAY1973:06:12:03,0:06:52,3,44
1010,10 bottles of beer on the wall,Option 1,0.9153696885,04MAR1962,01JAN1960:12:47:55,0:01:40,92,76
1011,11 bottles of beer on the wall,Option 1,0.3531217558,29MAR1960,01JAN1960:03:33:24,0:01:03,80,29
1012,12 bottles of beer on the wall,Option 1,0.6743748717,02AUG1962,01JAN1960:07:25:59,0:00:10,16,98
1013,13 bottles of beer on the wall,Option 1,0.1305445992,11SEP1960,01JAN1960:13:51:32,0:00:35,73,15
1014,14 bottles of beer on the wall,Option 1,0.7409067949,26JUL1960,01JAN1960:05:18:10,0:00:41,30,89
1015,15 bottles of beer on the wall,Option 1,0.0869016028,28FEB1961,01JAN1960:13:23:45,0:00:44,80,3
1016,16 bottles of beer on the wall,Option 1,0.0462121419,09AUG1962,01JAN1960:07:42:38,0:01:17,62,2
1017,17 bottles of beer on the wall,Option 1,0.7501918947,14MAY1962,01JAN1960:04:40:20,0:00:15,53,65
1018,18 bottles of beer on the wall,Option 1,0.7300173294,03AUG1962,01JAN1960:03:20:41,0:00:41,21,60
1019,19 bottles of beer on the wall,Option 1,0.6960950437,01JUN1960,01JAN1960:01:58:52,0:01:08,38,5
1020,20 bottles of beer on the wall,Option 1,0.6190566065,30MAY1961,01JAN1960:09:04:20,0:01:07,92,23
1021,21 bottles of beer on the wall,Option 1,0.5173368238,07JAN1961,01JAN1960:07:52:34,0:00:52,57,21
1022,22 bottles of beer on the wall,Option 1,0.4720626452,07NOV1960,01JAN1960:12:12:00,0:00:26,53,32
1023,23 bottles of beer on the wall,Option 1,0.2856596393,08AUG1960,01JAN1960:06:09:25,0:00:28,40,12
1024,24 bottles of beer on the wall,Option 1,0.5160869418,02JUN1960,01JAN1960:06:36:06,0:01:10,41,81
1025,25 bottles of beer on the wall,Option 1,0.1683158517,05JAN1961,01JAN1960:08:14:35,0:00:06,18,53
1026,26 bottles of beer on the wall,Option 1,0.8951142248,28NOV1961,01JAN1960:03:31:17,0:00:58,79,54
1027,27 bottles of beer on the wall,Option 1,0.7037817481,01SEP1961,01JAN1960:05:48:34,0:00:29,50,15
1028,28 bottles of beer on the wall,Option 1,0.6193826714,31MAR1962,01JAN1960:02:49:39,0:00:24,78,87
1029,29 bottles of beer on the wall,Option 1,0.9339028457,06DEC1961,01JAN1960:02:57:57,0:00:24,73,64
1030,30 bottles of beer on the wall,Option 1,0.5647351339,10AUG1960,01JAN1960:11:02:59,0:00:55,39,28
1031,31 bottles of beer on the wall,Option 1,0.1218988607,19JUN1961,01JAN1960:04:19:32,0:00:58,51,32
1032,32 bottles of beer on the wall,Option 1,0.3459929113,14MAY1962,01JAN1960:05:42:48,0:00:54,96,46
1033,33 bottles of beer on the wall,Option 1,0.092664999,31AUG1962,01JAN1960:00:08:34,0:00:51,69,90
1034,34 bottles of beer on the wall,Option 1,0.9793458097,08FEB1960,01JAN1960:01:55:23,0:00:42,45,28
1035,35 bottles of beer on the wall,Option 1,0.8964386624,18DEC1961,01JAN1960:04:42:45,0:00:07,49,97
1036,36 bottles of beer on the wall,Option 1,0.0961652911,13NOV1960,01JAN1960:03:44:53,0:01:25,62,59
1037,37 bottles of beer on the wall,Option 1,0.3475089201,16JAN1962,01JAN1960:01:35:19,0:00:15,23,50
1038,38 bottles of beer on the wall,Option 1,0.3096271312,21MAY1960,01JAN1960:09:51:33,0:00:15,2,71
1039,39 bottles of beer on the wall,Option 1,0.9445223114,28AUG1962,01JAN1960:07:09:31,0:00:12,30,31
1040,40 bottles of beer on the wall,Option 1,0.5626084667,06NOV1960,01JAN1960:01:42:16,0:01:14,18,97
1041,41 bottles of beer on the wall,Option 1,0.9432962513,01JUN1962,01JAN1960:03:30:04,0:00:11,20,34
1042,42 bottles of beer on the wall,Option 1,0.5802429382,08JUL1961,01JAN1960:08:12:43,0:01:26,18,5
1043,43 bottles of beer on the wall,Option 1,0.1970176255,27MAR1961,01JAN1960:00:19:45,0:01:29,13,76
1044,44 bottles of beer on the wall,Option 1,0.4980671608,05JAN1961,01JAN1960:13:36:08,0:00:56,4,36
1045,45 bottles of beer on the wall,Option 1,0.2486515531,05MAY1962,01JAN1960:08:47:09,0:00:42,2,23
1046,46 bottles of beer on the wall,Option 1,0.4097825794,20JUN1960,01JAN1960:03:33:26,0:00:31,98,71
1047,47 bottles of beer on the wall,Option 1,0.138754441,28JAN1960,01JAN1960:00:57:41,0:00:18,80,32
1048,48 bottles of beer on the wall,Option 1,0.0249874415,03MAR1960,01JAN1960:11:33:53,0:00:04,96,76
1049,49 bottles of beer on the wall,Option 1,0.8395310011,06NOV1961,01JAN1960:09:54:04,0:00:52,28,45
1050,50 bottles of beer on the wall,Option 1,0.0942291618,14APR1962,01JAN1960:08:09:30,0:01:36,37,86
1051,51 bottles of beer on the wall,Option 1,0.1670458001,13NOV1961,01JAN1960:01:05:55,0:00:25,42,83
1052,52 bottles of beer on the wall,Option 1,0.3122402715,04JUN1960,01JAN1960:03:47:47,0:01:01,18,78
1053,53 bottles of beer on the wall,Option 1,0.3854694261,14JUN1960,01JAN1960:02:43:08,0:00:06,22,67
1054,54 bottles of beer on the wall,Option 1,0.1950434345,14NOV1961,01JAN1960:02:46:34,0:00:55,42,0
1055,55 bottles of beer on the wall,Option 1,0.4948673586,29MAR1962,01JAN1960:00:48:06,0:01:04,28,4
1056,56 bottles of beer on the wall,Option 1,0.6464513832,06SEP1962,01JAN1960:10:08:36,0:01:02,43,82
1057,57 bottles of beer on the wall,Option 1,0.0724864798,20JUN1961,01JAN1960:12:22:51,0:01:27,82,53
1058,58 bottles of beer on the wall,Option 1,0.8114467793,20MAR1962,01JAN1960:06:11:33,0:01:29,40,89
1059,59 bottles of beer on the wall,Option 1,0.6348024321,28JUN1962,01JAN1960:05:21:21,0:01:37,55,41
1060,60 bottles of beer on the wall,Option 1,0.8019492933,08APR1961,01JAN1960:12:37:00,0:01:29,49,88
1061,61 bottles of beer on the wall,Option 1,0.4695742002,29JAN1962,01JAN1960:08:54:24,0:00:15,40,91
1062,62 bottles of beer on the wall,Option 1,0.902706475,15JUN1961,01JAN1960:09:46:49,0:00:23,74,70
1063,63 bottles of beer on the wall,Option 1,0.4557614594,16JUL1961,01JAN1960:02:06:05,0:01:09,7,3
1064,64 bottles of beer on the wall,Option 1,0.6632444466,20MAY1961,01JAN1960:02:44:44,0:00:20,42,100
1065,65 bottles of beer on the wall,Option 1,0.3901674,31AUG1961,01JAN1960:07:56:49,0:00:32,98,3
1066,66 bottles of beer on the wall,Option 1,0.8453234848,30JUN1962,01JAN1960:04:51:54,0:01:02,51,22
1067,67 bottles of beer on the wall,Option 1,0.9370150906,26APR1960,01JAN1960:04:05:08,0:01:39,28,86
1068,68 bottles of beer on the wall,Option 1,0.8854161277,22MAR1962,01JAN1960:10:38:49,0:01:07,60,85
1069,69 bottles of beer on the wall,Option 1,0.1327841906,24MAY1960,01JAN1960:01:18:46,0:01:15,88,85
1070,70 bottles of beer on the wall,Option 1,0.5846563226,27JUL1962,01JAN1960:03:52:31,0:00:09,20,8
1071,71 bottles of beer on the wall,Option 1,0.0257193684,18FEB1961,01JAN1960:03:25:01,0:00:29,1,62
1072,72 bottles of beer on the wall,Option 1,0.9471486034,01JUN1962,01JAN1960:04:05:25,0:01:22,65,20
1073,73 bottles of beer on the wall,Option 1,0.3037446282,16MAY1962,01JAN1960:05:10:19,0:00:01,14,34
1074,74 bottles of beer on the wall,Option 1,0.2508690675,01NOV1961,01JAN1960:11:26:03,0:00:15,8,74
1075,75 bottles of beer on the wall,Option 1,0.814380363,17SEP1960,01JAN1960:09:00:38,0:00:25,95,1
1076,76 bottles of beer on the wall,Option 1,0.3761493621,16AUG1961,01JAN1960:01:48:17,0:00:52,5,38
1077,77 bottles of beer on the wall,Option 1,0.3621215761,25JUL1961,01JAN1960:11:48:47,0:01:20,86,90
1078,78 bottles of beer on the wall,Option 1,0.0268799584,20MAY1961,01JAN1960:12:43:34,0:01:00,70,96
1079,79 bottles of beer on the wall,Option 1,0.4112483945,27JUL1962,01JAN1960:01:20:24,0:01:26,66,20
1080,80 bottles of beer on the wall,Option 1,0.9501868011,15APR1961,01JAN1960:09:58:20,0:00:51,93,79
1081,81 bottles of beer on the wall,Option 1,0.9866548018,13SEP1961,01JAN1960:05:20:04,0:00:14,28,97
1082,82 bottles of beer on the wall,Option 1,0.9907830073,22FEB1962,01JAN1960:03:29:03,0:00:17,16,91
1083,83 bottles of beer on the wall,Option 1,0.8927816567,11MAR1960,01JAN1960:05:52:48,0:01:26,54,14
1084,84 bottles of beer on the wall,Option 1,0.12871663,02FEB1961,01JAN1960:10:34:37,0:00:44,52,90
1085,85 bottles of beer on the wall,Option 1,0.5490252802,02JAN1960,01JAN1960:06:11:58,0:00:27,4,98
1086,86 bottles of beer on the wall,Option 1,0.5432773864,11FEB1960,01JAN1960:08:40:00,0:01:15,79,19
1087,87 bottles of beer on the wall,Option 1,0.8223943137,01OCT1960,01JAN1960:08:11:33,0:01:19,2,86
1088,88 bottles of beer on the wall,Option 1,0.8496777699,09FEB1962,01JAN1960:03:10:35,0:00:15,95,6
1089,89 bottles of beer on the wall,Option 1,0.9308730536,27MAY1962,01JAN1960:11:57:53,0:01:18,86,90
1090,90 bottles of beer on the wall,Option 1,0.3072653344,23FEB1962,01JAN1960:04:52:38,0:00:25,85,17
1091,91 bottles of beer on the wall,Option 1,0.7687679575,12FEB1960,01JAN1960:08:47:11,0:01:20,8,7
1092,92 bottles of beer on the wall,Option 1,0.1873595105,29SEP1961,01JAN1960:04:29:58,0:00:29,17,78
1093,93 bottles of beer on the wall,Option 1,0.0495966631,03OCT1961,01JAN1960:03:18:50,0:00:39,89,56
1094,94 bottles of beer on the wall,Option 1,0.2607690526,19SEP1960,01JAN1960:03:22:28,0:00:29,81,16
1095,95 bottles of beer on the wall,Option 1,0.549640266,07JUN1962,01JAN1960:06:15:32,0:00:04,57,70
1096,96 bottles of beer on the wall,Option 1,0.9993291092,08MAR1961,01JAN1960:13:49:08,0:00:33,37,28
1097,97 bottles of beer on the wall,Option 1,0.9517237963,02SEP1960,01JAN1960:05:16:03,0:00:40,77,61
1098,98 bottles of beer on the wall,Option 1,0.5952155588,14FEB1962,01JAN1960:05:05:11,0:01:29,63,83
1099,99 bottles of beer on the wall,Option 1,0.7526210732,05MAY1961,01JAN1960:06:58:36,0:00:02,95,1
10100,100 bottles of beer on the wall,Option 1,0.307558153,17MAY1961,01JAN1960:06:13:01,0:01:37,68,7
10101,101 bottles of beer on the wall,Option 1,0.6596710829,15APR1962,01JAN1960:08:34:02,0:00:43,66,43
10102,102 bottles of beer on the wall,Option 1,0.0202811998,31AUG1961,01JAN1960:07:22:35,0:01:31,57,35
10103,103 bottles of beer on the wall,Option 1,0.6699061034,02MAY1962,01JAN1960:05:13:17,0:00:36,30,23
10104,104 bottles of beer on the wall,Option 1,0.330972748,04JUN1961,01JAN1960:06:47:20,0:01:05,69,82
10105,105 bottles of beer on the wall,Option 1,0.2274839176,25JAN1961,01JAN1960:05:34:51,0:00:56,63,68
10106,106 bottles of beer on the wall,Option 1,0.5612243989,27JUN1962,01JAN1960:04:32:03,0:01:15,19,73
10107,107 bottles of beer on the wall,Option 1,0.7398902111,03SEP1962,01JAN1960:08:34:07,0:00:17,90,6
10108,108 bottles of beer on the wall,Option 1,0.6124899791,08AUG1960,01JAN1960:04:59:34,0:00:25,56,12
10109,109 bottles of beer on the wall,Option 1,0.882404773,26JAN1961,01JAN1960:01:29:15,0:01:26,36,4
10110,110 bottles of beer on the wall,Option 1,0.4427004733,27FEB1961,01JAN1960:06:16:49,0:01:40,97,84
10111,111 bottles of beer on the wall,Option 1,0.3609524622,10JAN1962,01JAN1960:09:48:37,0:01:11,87,62
10112,112 bottles of beer on the wall,Option 1,0.9408929562,03AUG1960,01JAN1960:06:54:26,0:00:08,19,33
10113,113 bottles of beer on the wall,Option 1,0.3149107319,10AUG1962,01JAN1960:13:01:00,0:00:04,75,60
10114,114 bottles of beer on the wall,Option 1,0.0525069181,17APR1962,01JAN1960:13:00:52,0:00:35,9,23
10115,115 bottles of beer on the wall,Option 1,0.145448105,14FEB1962,01JAN1960:04:06:08,0:00:26,45,91
10116,116 bottles of beer on the wall,Option 1,0.2444279959,10OCT1961,01JAN1960:08:03:12,0:01:37,12,41
10117,117 bottles of beer on the wall,Option 1,0.4619846043,30JUL1960,01JAN1960:09:40:16,0:00:50,2,88
10118,118 bottles of beer on the wall,Option 1,0.0316203502,13JUL1961,01JAN1960:08:31:39,0:01:05,60,94
10119,119 bottles of beer on the wall,Option 1,0.4738720574,13AUG1960,01JAN1960:01:31:05,0:01:17,43,79
10120,120 bottles of beer on the wall,Option 1,0.8058761856,11JUN1960,01JAN1960:12:56:35,0:00:08,92,36
10121,121 bottles of beer on the wall,Option 1,0.2955600979,08JUL1962,01JAN1960:06:09:22,0:00:03,94,80
10122,122 bottles of beer on the wall,Option 1,0.0064115427,18SEP1962,01JAN1960:00:06:24,0:00:13,72,75
10123,123 bottles of beer on the wall,Option 1,0.5678159327,21APR1960,01JAN1960:10:54:21,0:00:16,75,67
10124,124 bottles of beer on the wall,Option 1,0.1431510994,10JAN1962,01JAN1960:01:57:00,0:00:12,48,31
10125,125 bottles of beer on the wall,Option 1,0.3805634409,26JAN1962,01JAN1960:03:03:19,0:01:29,83,52
10126,126 bottles of beer on the wall,Option 1,0.3833517993,26APR1960,01JAN1960:11:27:41,0:00:44,99,36
10127,127 bottles of beer on the wall,Option 1,0.5669089111,04MAR1961,01JAN1960:05:36:22,0:01:18,43,27
10128,128 bottles of beer on the wall,Option 1,0.1514211843,01NOV1960,01JAN1960:07:45:50,0:01:02,22,12
10129,129 bottles of beer on the wall,Option 1,0.0446588583,05JAN1961,01JAN1960:02:13:55,0:00:42,27,46
10130,130 bottles of beer on the wall,Option 1,0.7892141611,22APR1962,01JAN1960:04:17:54,0:01:05,75,84
10131,131 bottles of beer on the wall,Option 1,0.5012088001,24DEC1960,01JAN1960:13:03:23,0:01:22,87,82
10132,132 bottles of beer on the wall,Option 1,0.2327582944,07APR1961,01JAN1960:01:33:15,0:01:14,18,46
10133,133 bottles of beer on the wall,Option 1,0.2234651173,20MAR1961,01JAN1960:13:52:02,0:01:06,42,58
10134,134 bottles of beer on the wall,Option 1,0.4954405918,10FEB1961,01JAN1960:13:51:14,0:01:36,35,11
10135,135 bottles of beer on the wall,Option 1,0.7874922891,15AUG1960,01JAN1960:00:21:57,0:00:52,45,36
10136,136 bottles of beer on the wall,Option 1,0.3992494891,06SEP1961,01JAN1960:09:51:46,0:01:25,26,9
10137,137 bottles of beer on the wall,Option 1,0.3964866136,25MAY1960,01JAN1960:03:19:48,0:00:28,44,3
10138,138 bottles of beer on the wall,Option 1,0.9466173323,06APR1962,01JAN1960:13:19:18,0:01:27,78,51
10139,139 bottles of beer on the wall,Option 1,0.6525219277,09APR1960,01JAN1960:05:43:49,0:00:21,63,6
10140,140 bottles of beer on the wall,Option 1,0.4684071925,29MAY1961,01JAN1960:02:53:36,0:00:46,68,4
10141,141 bottles of beer on the wall,Option 1,0.8581724013,16MAY1960,01JAN1960:01:45:44,0:01:32,31,85
10142,142 bottles of beer on the wall,Option 1,0.825792401,23APR1961,01JAN1960:12:03:13,0:00:49,36,45
10143,143 bottles of beer on the wall,Option 1,0.3172852538,20FEB1962,01JAN1960:12:38:31,0:01:34,51,78
10144,144 bottles of beer on the wall,Option 1,0.670397946,27JAN1962,01JAN1960:04:59:37,0:00:39,38,99
10145,145 bottles of beer on the wall,Option 1,0.3304372441,04JUN1960,01JAN1960:00:39:12,0:00:29,88,76
10146,146 bottles of beer on the wall,Option 1,0.845151971,31JUL1962,01JAN1960:05:03:34,0:00:13,2,80
10147,147 bottles of beer on the wall,Option 1,0.7957223709,02FEB1961,01JAN1960:00:03:07,0:01:11,29,99
10148,148 bottles of beer on the wall,Option 1,0.323337108,29FEB1960,01JAN1960:01:58:05,0:01:17,23,65
10149,149 bottles of beer on the wall,Option 1,0.1813316611,29JUN1960,01JAN1960:02:18:08,0:00:40,45,52
10150,150 bottles of beer on the wall,Option 1,0.7860426655,05MAR1962,01JAN1960:01:57:15,0:00:26,31,91
10151,151 bottles of beer on the wall,Option 1,0.3305453571,09APR1960,01JAN1960:07:08:32,0:01:30,72,15
10152,152 bottles of beer on the wall,Option 1,0.9367212513,18AUG1962,01JAN1960:10:36:03,0:01:26,85,81
10153,153 bottles of beer on the wall,Option 1,0.3385623458,19MAR1962,01JAN1960:04:21:46,0:01:29,11,54
10154,154 bottles of beer on the wall,Option 1,0.9756794413,17JUN1961,01JAN1960:08:35:40,0:00:34,3,72
10155,155 bottles of beer on the wall,Option 1,0.6385958868,21OCT1961,01JAN1960:08:51:00,0:00:50,1,6
10156,156 bottles of beer on the wall,Option 1,0.3569769959,14AUG1960,01JAN1960:10:57:16,0:00:05,5,94
10157,157 bottles of beer on the wall,Option 1,0.8559997239,23MAR1962,01JAN1960:12:03:38,0:00:08,96,68
10158,158 bottles of beer on the wall,Option 1,0.2293701918,13AUG1960,01JAN1960:06:36:47,0:00:07,87,87
10159,159 bottles of beer on the wall,Option 1,0.0007910165,20SEP1962,01JAN1960:11:49:02,0:00:55,18,69
10160,160 bottles of beer on the wall,Option 1,0.5876370373,08JAN1960,01JAN1960:00:59:15,0:01:26,27,36
10161,161 bottles of beer on the wall,Option 1,0.2354667514,11OCT1961,01JAN1960:01:27:14,0:01:04,90,28
10162,162 bottles of beer on the wall,Option 1,0.0144103263,11AUG1961,01JAN1960:02:37:41,0:01:39,92,8
10163,163 bottles of beer on the wall,Option 1,0.7087855668,03JUL1962,01JAN1960:02:07:23,0:01:35,51,33
10164,164 bottles of beer on the wall,Option 1,0.7251478106,20MAY1960,01JAN1960:12:15:23,0:01:28,58,7
10165,165 bottles of beer on the wall,Option 1,0.9629398403,06APR1962,01JAN1960:01:05:24,0:01:39,84,6
10166,166 bottles of beer on the wall,Option 1,0.5155049164,14OCT1960,01JAN1960:04:02:06,0:00:30,63,96
10167,167 bottles of beer on the wall,Option 1,0.1016342775,11MAY1960,01JAN1960:05:55:03,0:00:01,31,44
10168,168 bottles of beer on the wall,Option 1,0.3690353596,12NOV1961,01JAN1960:12:49:02,0:00:03,2,79
10169,169 bottles of beer on the wall,Option 1,0.5573803501,02SEP1962,01JAN1960:12:59:56,0:00:31,7,95
10170,170 bottles of beer on the wall,Option 1,0.2008119497,10JUN1961,01JAN1960:05:59:06,0:01:29,88,4
10171,171 bottles of beer on the wall,Option 1,0.6939068505,25MAY1962,01JAN1960:07:20:06,0:01:08,87,8
10172,172 bottles of beer on the wall,Option 1,0.7013406594,14JUL1960,01JAN1960:04:04:24,0:00:11,44,96
10173,173 bottles of beer on the wall,Option 1,0.83506724,30APR1961,01JAN1960:12:44:40,0:00:10,74,70
10174,174 bottles of beer on the wall,Option 1,0.9339991943,26JAN1962,01JAN1960:04:59:32,0:01:09,13,66
10175,175 bottles of beer on the wall,Option 1,0.8333402787,18FEB1961,01JAN1960:07:25:44,0:00:28,47,2
10176,176 bottles of beer on the wall,Option 1,0.5998844433,03MAR1962,01JAN1960:03:45:33,0:00:52,2,61
10177,177 bottles of beer on the wall,Option 1,0.6161394634,18DEC1960,01JAN1960:05:35:25,0:00:50,70,22
10178,178 bottles of beer on the wall,Option 1,0.0821002392,21APR1960,01JAN1960:10:08:56,0:01:40,28,9
10179,179 bottles of beer on the wall,Option 1,0.6845213462,23MAY1960,01JAN1960:13:15:46,0:00:03,89,13
10180,180 bottles of beer on the wall,Option 1,0.3839034477,14MAY1960,01JAN1960:03:22:17,0:01:11,15,38
10181,181 bottles of beer on the wall,Option 1,0.7949567609,21AUG1962,01JAN1960:02:41:01,0:00:57,90,93
10182,182 bottles of beer on the wall,Option 1,0.5079025419,23SEP1962,01JAN1960:02:22:31,0:01:07,83,32
10183,183 bottles of beer on the wall,Option 1,0.3215162574,26DEC1961,01JAN1960:09:03:00,0:01:38,46,94
10184,184 bottles of beer on the wall,Option 1,0.3322958058,12MAY1961,01JAN1960:02:48:05,0:00:46,80,54
10185,185 bottles of beer on the wall,Option 1,0.6510801453,07SEP1960,01JAN1960:11:49:02,0:00:59,51,47
10186,186 bottles of beer on the wall,Option 1,0.060995535,15AUG1960,01JAN1960:02:21:08,0:01:40,5,61
10187,187 bottles of beer on the wall,Option 1,0.8541180551,14SEP1960,01JAN1960:13:29:33,0:01:23,17,14
10188,188 bottles of beer on the wall,Option 1,0.9427926219,23JUL1960,01JAN1960:05:19:05,0:01:13,22,97
10189,189 bottles of beer on the wall,Option 1,0.2325015186,01FEB1960,01JAN1960:11:50:07,0:01:22,6,53
10190,190 bottles of beer on the wall,Option 1,0.3687101493,21FEB1962,01JAN1960:06:44:23,0:00:13,16,30
10191,191 bottles of beer on the wall,Option 1,0.7647511232,09JAN1960,01JAN1960:13:06:29,0:01:35,6,97
10192,192 bottles of beer on the wall,Option 1,0.4105463565,17AUG1961,01JAN1960:11:04:32,0:01:14,38,33
10193,193 bottles of beer on the wall,Option 1,0.8785403831,12JUL1962,01JAN1960:04:11:05,0:00:29,19,82
10194,194 bottles of beer on the wall,Option 1,0.9304303433,11JUL1961,01JAN1960:12:36:57,0:01:02,20,35
10195,195 bottles of beer on the wall,Option 1,0.7302505256,01MAR1961,01JAN1960:01:38:35,0:00:42,16,35
10196,196 bottles of beer on the wall,Option 1,0.2536906177,04SEP1962,01JAN1960:05:18:23,0:01:18,91,50
10197,197 bottles of beer on the wall,Option 1,0.1181504503,08AUG1961,01JAN1960:09:27:54,0:01:26,20,13
10198,198 bottles of beer on the wall,Option 1,0.9275614228,17JUL1961,01JAN1960:01:52:18,0:00:06,52,73
10199,199 bottles of beer on the wall,Option 1,0.7495222128,04APR1961,01JAN1960:09:28:04,0:00:42,30,41
10200,200 bottles of beer on the wall,Option 1,0.925741082,02FEB1962,01JAN1960:12:23:10,0:00:07,79,17
10201,201 bottles of beer on the wall,Option 1,0.2591843359,04DEC1960,01JAN1960:12:46:41,0:00:00,53,58
10202,202 bottles of beer on the wall,Option 1,0.4289995704,17NOV1961,01JAN1960:02:20:52,0:00:35,41,25
10203,203 bottles of beer on the wall,Option 1,0.4625803807,24JAN1960,01JAN1960:08:20:44,0:01:11,84,66
10204,204 bottles of beer on the wall,Option 1,0.858440102,31AUG1962,01JAN1960:08:51:40,0:00:12,18,51
10205,205 bottles of beer on the wall,Option 1,0.8964499016,01SEP1962,01JAN1960:05:33:47,0:00:23,34,77
10206,206 bottles of beer on the wall,Option 1,0.5742789063,24OCT1961,01JAN1960:02:31:04,0:01:08,27,66
10207,207 bottles of beer on the wall,Option 1,0.4864150954,29SEP1960,01JAN1960:09:27:46,0:01:28,31,26
10208,208 bottles of beer on the wall,Option 1,0.4511992249,04DEC1960,01JAN1960:09:39:26,0:00:42,49,98
10209,209 bottles of beer on the wall,Option 1,0.4218624157,13SEP1961,01JAN1960:01:40:55,0:01:39,35,50
10210,210 bottles of beer on the wall,Option 1,0.1572868331,15FEB1960,01JAN1960:07:01:15,0:00:51,43,1
10211,211 bottles of beer on the wall,Option 1,0.713915177,23MAR1960,01JAN1960:11:08:53,0:00:15,18,61
10212,212 bottles of beer on the wall,Option 1,0.5677882165,19MAY1960,01JAN1960:01:27:23,0:01:02,34,89
10213,213 bottles of beer on the wall,Option 1,0.7552938581,12SEP1961,01JAN1960:11:47:33,0:00:38,44,46
10214,214 bottles of beer on the wall,Option 1,0.6071256071,28DEC1961,01JAN1960:05:28:18,0:01:23,84,66
10215,215 bottles of beer on the wall,Option 1,0.7717189266,12MAR1960,01JAN1960:01:21:26,0:01:00,28,22
10216,216 bottles of beer on the wall,Option 1,0.8985594329,24MAR1961,01JAN1960:10:48:58,0:01:31,93,2
10217,217 bottles of beer on the wall,Option 1,0.3156879904,13AUG1960,01JAN1960:07:10:46,0:01:18,100,54
10218,218 bottles of beer on the wall,Option 1,0.3408455315,08JUN1961,01JAN1960:02:26:49,0:00:05,65,82
10219,219 bottles of beer on the wall,Option 1,0.6263580553,08JUN1962,01JAN1960:05:59:46,0:01:03,76,88
10220,220 bottles of beer on the wall,Option 1,0.2878925355,19DEC1961,01JAN1960:08:23:41,0:00:00,92,1
10221,221 bottles of beer on the wall,Option 1,0.0901017348,19JUL1962,01JAN1960:09:50:47,0:00:43,21,84
10222,222 bottles of beer on the wall,Option 1,0.8967759362,14SEP1960,01JAN1960:12:25:58,0:01:22,34,50
10223,223 bottles of beer on the wall,Option 1,0.9878171943,03DEC1961,01JAN1960:03:43:09,0:00:17,11,84
10224,224 bottles of beer on the wall,Option 1,0.5275036886,13DEC1961,01JAN1960:03:12:56,0:01:36,85,49
10225,225 bottles of beer on the wall,Option 1,0.442012436,12JUN1960,01JAN1960:11:40:23,0:01:40,76,87
10226,226 bottles of beer on the wall,Option 1,0.582103689,10FEB1961,01JAN1960:01:50:49,0:00:59,53,29
10227,227 bottles of beer on the wall,Option 1,0.5757669842,01NOV1960,01JAN1960:13:47:33,0:00:43,55,6
10228,228 bottles of beer on the wall,Option 1,0.4786617507,07JAN1960,01JAN1960:13:36:24,0:01:22,91,53
10229,229 bottles of beer on the wall,Option 1,0.1386274957,06APR1962,01JAN1960:03:48:29,0:01:27,36,48
10230,230 bottles of beer on the wall,Option 1,0.4188394893,31MAY1962,01JAN1960:10:30:51,0:00:54,5,87
10231,231 bottles of beer on the wall,Option 1,0.9250617777,18OCT1960,01JAN1960:04:29:52,0:00:38,34,94
10232,232 bottles of beer on the wall,Option 1,0.3077528124,05FEB1960,01JAN1960:09:37:42,0:01:13,58,75
10233,233 bottles of beer on the wall,Option 1,0.7316332277,29NOV1960,01JAN1960:08:56:57,0:01:13,34,53
10234,234 bottles of beer on the wall,Option 1,0.5666298352,21NOV1960,01JAN1960:07:51:09,0:01:08,97,71
10235,235 bottles of beer on the wall,Option 1,0.5736639409,03JUL1962,01JAN1960:11:57:25,0:00:51,15,49
10236,236 bottles of beer on the wall,Option 1,0.6785667616,11FEB1962,01JAN1960:09:47:20,0:00:50,65,21
10237,237 bottles of beer on the wall,Option 1,0.3721726869,05JUL1962,01JAN1960:11:58:22,0:01:32,82,21
10238,238 bottles of beer on the wall,Option 1,0.0332283876,17AUG1961,01JAN1960:13:11:34,0:00:54,83,30
10239,239 bottles of beer on the wall,Option 1,0.9734656848,02JAN1961,01JAN1960:00:36:43,0:00:19,31,54
10240,240 bottles of beer on the wall,Option 1,0.3022106021,16FEB1961,01JAN1960:13:50:38,0:00:40,22,66
10241,241 bottles of beer on the wall,Option 1,0.7546903294,06JUL1961,01JAN1960:12:36:17,0:01:29,16,85
10242,242 bottles of beer on the wall,Option 1,0.2509871834,07MAR1962,01JAN1960:10:38:28,0:00:39,7,8
10243,243 bottles of beer on the wall,Option 1,0.9526996668,15JAN1960,01JAN1960:04:24:42,0:01:01,69,80
10244,244 bottles of beer on the wall,Option 1,0.1816610122,06FEB1962,01JAN1960:08:46:51,0:00:54,89,91
10245,245 bottles of beer on the wall,Option 1,0.3928658876,21JUL1962,01JAN1960:12:59:42,0:00:38,24,27
10246,246 bottles of beer on the wall,Option 1,0.3774878524,18FEB1961,01JAN1960:07:40:49,0:01:31,88,93
10247,247 bottles of beer on the wall,Option 1,0.6063659362,01NOV1960,01JAN1960:01:19:07,0:00:05,82,73
10248,248 bottles of beer on the wall,Option 1,0.119603098,14JUN1960,01JAN1960:04:29:22,0:00:58,87,47
10249,249 bottles of beer on the wall,Option 1,0.4833748445,03JUL1960,01JAN1960:01:53:54,0:00:37,34,33
10250,250 bottles of beer on the wall,Option 1,0.2244539946,10AUG1961,01JAN1960:06:19:01,0:01:15,87,97
10251,251 bottles of beer on the wall,Option 1,0.9368193191,11JUN1962,01JAN1960:06:37:14,0:00:46,94,39
10252,252 bottles of beer on the wall,Option 1,0.1791427751,10NOV1961,01JAN1960:00:49:22,0:00:47,96,21
10253,253 bottles of beer on the wall,Option 1,0.5836302874,06JUN1961,01JAN1960:08:39:34,0:01:01,78,49
10254,254 bottles of beer on the wall,Option 1,0.1289398275,28DEC1960,01JAN1960:12:25:05,0:00:43,67,99
10255,255 bottles of beer on the wall,Option 1,0.7833669785,05SEP1962,01JAN1960:02:47:35,0:00:20,25,2
10256,256 bottles of beer on the wall,Option 1,0.4945342483,29JAN1960,01JAN1960:00:54:13,0:01:13,72,56
10257,257 bottles of beer on the wall,Option 1,0.0635836129,05JAN1961,01JAN1960:08:10:04,0:00:52,11,10
10258,258 bottles of beer on the wall,Option 1,0.8188241654,09FEB1962,01JAN1960:06:33:00,0:01:21,41,96
10259,259 bottles of beer on the wall,Option 1,0.3398916076,11FEB1960,01JAN1960:07:12:29,0:00:56,18,76
10260,260 bottles of beer on the wall,Option 1,0.0814064155,21MAY1961,01JAN1960:11:03:51,0:01:18,78,29
10261,261 bottles of beer on the wall,Option 1,0.6653245542,20JAN1962,01JAN1960:08:03:31,0:00:18,39,95
10262,262 bottles of beer on the wall,Option 1,0.4036777021,04AUG1962,01JAN1960:12:32:27,0:00:08,57,63
10263,263 bottles of beer on the wall,Option 1,0.8931138603,07JAN1962,01JAN1960:09:04:24,0:00:32,6,27
10264,264 bottles of beer on the wall,Option 1,0.528584433,06APR1962,01JAN1960:09:43:19,0:01:00,24,41
10265,265 bottles of beer on the wall,Option 1,0.8267822945,29JUL1960,01JAN1960:00:48:11,0:00:01,81,78
10266,266 bottles of beer on the wall,Option 1,0.7218411401,17FEB1960,01JAN1960:07:30:38,0:00:08,35,81
10267,267 bottles of beer on the wall,Option 1,0.1475262773,11NOV1960,01JAN1960:13:44:20,0:00:57,36,68
10268,268 bottles of beer on the wall,Option 1,0.9412727286,30DEC1960,01JAN1960:02:46:30,0:01:19,5,92
10269,269 bottles of beer on the wall,Option 1,0.3038877548,27NOV1960,01JAN1960:10:50:10,0:01:21,43,95
10270,270 bottles of beer on the wall,Option 1,0.2756435532,15APR1962,01JAN1960:09:05:28,0:01:34,11,14
10271,271 bottles of beer on the wall,Option 1,0.7056001121,31AUG1960,01JAN1960:08:48:52,0:00:02,9,51
10272,272 bottles of beer on the wall,Option 1,0.5273708508,21SEP1962,01JAN1960:12:58:13,0:00:28,97,69
10273,273 bottles of beer on the wall,Option 1,0.6002807215,03MAY1960,01JAN1960:10:14:48,0:00:40,52,32
10274,274 bottles of beer on the wall,Option 1,0.6100557971,20JUN1960,01JAN1960:08:11:55,0:00:27,90,14
10275,275 bottles of beer on the wall,Option 1,0.4197408638,07JUN1961,01JAN1960:12:07:18,0:00:26,64,100
10276,276 bottles of beer on the wall,Option 1,0.4903712498,19JAN1960,01JAN1960:01:06:26,0:00:03,35,24
10277,277 bottles of beer on the wall,Option 1,0.6658435406,04NOV1960,01JAN1960:00:04:17,0:00:37,7,84
10278,278 bottles of beer on the wall,Option 1,0.5491365942,14JAN1961,01JAN1960:04:12:49,0:00:27,99,47
10279,279 bottles of beer on the wall,Option 1,0.4473488622,13MAY1961,01JAN1960:12:06:34,0:01:16,19,20
10280,280 bottles of beer on the wall,Option 1,0.4511988663,06JUL1962,01JAN1960:10:05:51,0:00:56,76,34
10281,281 bottles of beer on the wall,Option 1,0.0783031066,11JUN1961,01JAN1960:09:58:43,0:01:05,9,63
10282,282 bottles of beer on the wall,Option 1,0.776985302,20JUL1962,01JAN1960:10:44:29,0:01:00,59,10
10283,283 bottles of beer on the wall,Option 1,0.468099362,31AUG1962,01JAN1960:05:26:33,0:00:20,35,52
10284,284 bottles of beer on the wall,Option 1,0.4040679696,20FEB1962,01JAN1960:06:27:25,0:00:04,76,30
10285,285 bottles of beer on the wall,Option 1,0.4549995947,20FEB1962,01JAN1960:10:36:57,0:00:34,2,43
10286,286 bottles of beer on the wall,Option 1,0.7455339361,16SEP1961,01JAN1960:08:39:35,0:01:00,42,44
10287,287 bottles of beer on the wall,Option 1,0.0209561712,04JAN1960,01JAN1960:05:52:58,0:00:24,32,7
10288,288 bottles of beer on the wall,Option 1,0.4955981842,04JAN1962,01JAN1960:02:56:03,0:00:30,85,31
10289,289 bottles of beer on the wall,Option 1,0.4131368219,10FEB1960,01JAN1960:11:57:31,0:00:16,37,88
10290,290 bottles of beer on the wall,Option 1,0.3282186721,17OCT1960,01JAN1960:10:54:04,0:00:56,72,28
10291,291 bottles of beer on the wall,Option 1,0.2116929005,18JAN1962,01JAN1960:06:56:27,0:00:11,87,82
10292,292 bottles of beer on the wall,Option 1,0.8483731937,12FEB1962,01JAN1960:05:05:41,0:01:36,12,83
10293,293 bottles of beer on the wall,Option 1,0.1560111345,13NOV1960,01JAN1960:10:04:22,0:00:03,94,4
10294,294 bottles of beer on the wall,Option 1,0.7046207808,12APR1962,01JAN1960:13:50:47,0:00:32,31,97
10295,295 bottles of beer on the wall,Option 1,0.2716620403,04AUG1961,01JAN1960:01:52:29,0:00:57,99,44
10296,296 bottles of beer on the wall,Option 1,0.5543203496,12SEP1960,01JAN1960:13:43:54,0:00:44,49,1
10297,297 bottles of beer on the wall,Option 1,0.983109036,31JUL1962,01JAN1960:01:07:33,0:00:36,4,10
10298,298 bottles of beer on the wall,Option 1,0.8123072115,14SEP1962,01JAN1960:06:16:12,0:01:25,88,96
10299,299 bottles of beer on the wall,Option 1,0.4276896559,05OCT1960,01JAN1960:02:55:07,0:00:58,83,76
10300,300 bottles of beer on the wall,Option 1,0.8921809042,19JAN1962,01JAN1960:02:05:38,0:00:12,80,13
10301,301 bottles of beer on the wall,Option 1,0.6041374279,10DEC1961,01JAN1960:01:06:29,0:01:27,62,9
10302,302 bottles of beer on the wall,Option 1,0.0460550185,31MAY1962,01JAN1960:03:03:56,0:00:05,33,88
10303,303 bottles of beer on the wall,Option 1,0.1868385622,12APR1962,01JAN1960:12:42:44,0:01:05,65,18
10304,304 bottles of beer on the wall,Option 1,0.3386632657,28SEP1961,01JAN1960:11:24:06,0:00:42,2,93
10305,305 bottles of beer on the wall,Option 1,0.6400271019,01JUN1960,01JAN1960:13:33:07,0:01:30,60,72
10306,306 bottles of beer on the wall,Option 1,0.9534907304,18NOV1961,01JAN1960:02:02:51,0:00:54,7,57
10307,307 bottles of beer on the wall,Option 1,0.6663103745,06SEP1961,01JAN1960:05:36:49,0:00:43,88,2
10308,308 bottles of beer on the wall,Option 1,0.5392553073,13FEB1962,01JAN1960:11:28:18,0:01:08,16,8
10309,309 bottles of beer on the wall,Option 1,0.0747909025,17OCT1961,01JAN1960:08:36:12,0:00:41,49,42
10310,310 bottles of beer on the wall,Option 1,0.3249381847,30SEP1960,01JAN1960:08:12:54,0:00:09,96,89
10311,311 bottles of beer on the wall,Option 1,0.9231011951,19MAY1962,01JAN1960:05:10:33,0:00:50,30,9
10312,312 bottles of beer on the wall,Option 1,0.4658221637,21MAY1961,01JAN1960:12:55:25,0:01:39,16,20
10313,313 bottles of beer on the wall,Option 1,0.7215524673,21FEB1960,01JAN1960:02:00:07,0:01:40,95,94
10314,314 bottles of beer on the wall,Option 1,0.7328679942,28OCT1961,01JAN1960:09:07:00,0:00:25,42,71
10315,315 bottles of beer on the wall,Option 1,0.1276036776,12JUN1960,01JAN1960:01:54:08,0:00:56,57,42
10316,316 bottles of beer on the wall,Option 1,0.1270824723,15SEP1960,01JAN1960:03:19:25,0:00:21,85,9
10317,317 bottles of beer on the wall,Option 1,0.3750520117,13JUN1961,01JAN1960:04:33:09,0:01:15,24,20
10318,318 bottles of beer on the wall,Option 1,0.5777822102,10DEC1960,01JAN1960:13:32:14,0:00:09,98,28
10319,319 bottles of beer on the wall,Option 1,0.140476402,27AUG1962,01JAN1960:08:52:46,0:01:08,64,83
10320,320 bottles of beer on the wall,Option 1,0.2589205551,31MAY1961,01JAN1960:08:33:06,0:00:53,28,98
10321,321 bottles of beer on the wall,Option 1,0.7350722825,16SEP1962,01JAN1960:05:47:44,0:01:17,79,95
10322,322 bottles of beer on the wall,Option 1,0.1476364542,15JAN1960,01JAN1960:12:21:20,0:00:20,86,62
10323,323 bottles of beer on the wall,Option 1,0.8700561099,15MAY1962,01JAN1960:00:47:05,0:00:20,90,15
10324,324 bottles of beer on the wall,Option 1,0.6408788802,12SEP1962,01JAN1960:11:50:31,0:00:53,41,72
10325,325 bottles of beer on the wall,Option 1,0.6961101623,27NOV1960,01JAN1960:00:10:49,0:01:17,28,72
10326,326 bottles of beer on the wall,Option 1,0.1467710059,24FEB1961,01JAN1960:01:13:38,0:00:33,14,5
10327,327 bottles of beer on the wall,Option 1,0.0215573572,09JUN1961,01JAN1960:11:47:17,0:00:21,57,10
10328,328 bottles of beer on the wall,Option 1,0.4173900054,25JUL1962,01JAN1960:12:28:20,0:00:23,73,90
10329,329 bottles of beer on the wall,Option 1,0.6395625713,02NOV1961,01JAN1960:08:49:34,0:00:37,77,79
10330,330 bottles of beer on the wall,Option 1,0.0091438908,18MAY1962,01JAN1960:05:10:05,0:00:41,15,31
10331,331 bottles of beer on the wall,Option 1,0.1024675197,11DEC1960,01JAN1960:13:12:57,0:00:23,50,13
10332,332 bottles of beer on the wall,Option 1,0.057470562,11MAY1961,01JAN1960:03:43:04,0:00:17,48,14
10333,333 bottles of beer on the wall,Option 1,0.8478633872,21JUL1961,01JAN1960:03:45:42,0:01:31,22,40
10334,334 bottles of beer on the wall,Option 1,0.3442252541,24JUN1960,01JAN1960:01:19:31,0:00:48,82,25
10335,335 bottles of beer on the wall,Option 1,0.7338460184,06JUN1962,01JAN1960:03:32:34,0:01:04,6,31
10336,336 bottles of beer on the wall,Option 1,0.6217917342,09MAR1961,01JAN1960:06:37:39,0:00:50,70,84
10337,337 bottles of beer on the wall,Option 1,0.6684890807,10OCT1961,01JAN1960:05:34:24,0:01:20,66,18
10338,338 bottles of beer on the wall,Option 1,0.3695247562,05SEP1962,01JAN1960:00:25:21,0:01:18,48,37
10339,339 bottles of beer on the wall,Option 1,0.9429265987,06DEC1960,01JAN1960:07:11:17,0:00:38,59,1
10340,340 bottles of beer on the wall,Option 1,0.9266307265,17JUL1960,01JAN1960:06:33:59,0:00:21,12,13
10341,341 bottles of beer on the wall,Option 1,0.7280535543,23FEB1961,01JAN1960:05:01:10,0:00:34,73,25
10342,342 bottles of beer on the wall,Option 1,0.488654495,15AUG1962,01JAN1960:01:24:33,0:00:56,59,25
10343,343 bottles of beer on the wall,Option 1,0.9526806548,28DEC1960,01JAN1960:07:26:17,0:00:58,97,61
10344,344 bottles of beer on the wall,Option 1,0.526025336,14JAN1960,01JAN1960:10:02:08,0:00:55,11,77
10345,345 bottles of beer on the wall,Option 1,0.807215352,03JUL1961,01JAN1960:12:49:47,0:00:01,40,7
10346,346 bottles of beer on the wall,Option 1,0.9305162979,28FEB1960,01JAN1960:09:46:40,0:00:59,56,28
10347,347 bottles of beer on the wall,Option 1,0.7591318552,18FEB1962,01JAN1960:13:25:32,0:01:10,41,9
10348,348 bottles of beer on the wall,Option 1,0.4177664911,11SEP1961,01JAN1960:09:55:17,0:01:39,76,82
10349,349 bottles of beer on the wall,Option 1,0.4690050443,05DEC1961,01JAN1960:11:05:15,0:01:09,63,40
10350,350 bottles of beer on the wall,Option 1,0.7541399979,31AUG1961,01JAN1960:12:30:45,0:00:33,57,12
10351,351 bottles of beer on the wall,Option 1,0.1392844325,17MAR1962,01JAN1960:08:20:38,0:00:41,85,45
10352,352 bottles of beer on the wall,Option 1,0.1020530235,23DEC1961,01JAN1960:09:46:20,0:00:01,55,56
10353,353 bottles of beer on the wall,Option 1,0.0257998794,04DEC1961,01JAN1960:09:47:10,0:00:31,100,2
10354,354 bottles of beer on the wall,Option 1,0.1238113316,20MAR1962,01JAN1960:09:15:30,0:00:01,74,11
10355,355 bottles of beer on the wall,Option 1,0.4214245292,24NOV1960,01JAN1960:04:24:09,0:01:01,79,83
10356,356 bottles of beer on the wall,Option 1,0.3243381057,12FEB1961,01JAN1960:00:55:59,0:00:50,30,52
10357,357 bottles of beer on the wall,Option 1,0.9735697345,24NOV1960,01JAN1960:07:10:56,0:01:33,64,2
10358,358 bottles of beer on the wall,Option 1,0.4796259461,28JAN1961,01JAN1960:11:51:29,0:01:03,19,29
10359,359 bottles of beer on the wall,Option 1,0.003359966,01SEP1960,01JAN1960:13:24:25,0:00:09,66,60
10360,360 bottles of beer on the wall,Option 1,0.5773700334,21JAN1960,01JAN1960:10:15:32,0:00:40,9,21
10361,361 bottles of beer on the wall,Option 1,0.0792848342,25JAN1962,01JAN1960:06:00:35,0:01:25,73,73
10362,362 bottles of beer on the wall,Option 1,0.9339359365,30MAY1961,01JAN1960:09:08:13,0:00:56,12,56
10363,363 bottles of beer on the wall,Option 1,0.3517632132,12FEB1961,01JAN1960:12:07:19,0:00:01,74,69
10364,364 bottles of beer on the wall,Option 1,0.4088954895,17MAR1961,01JAN1960:08:04:51,0:01:01,70,66
10365,365 bottles of beer on the wall,Option 1,0.1254259623,30DEC1961,01JAN1960:06:47:12,0:00:01,79,43
10366,366 bottles of beer on the wall,Option 1,0.9190132958,28MAY1961,01JAN1960:06:35:42,0:00:07,19,31
10367,367 bottles of beer on the wall,Option 1,0.3033860015,17MAY1962,01JAN1960:05:32:03,0:00:32,57,73
10368,368 bottles of beer on the wall,Option 1,0.1463442846,02SEP1962,01JAN1960:01:24:29,0:00:53,19,64
10369,369 bottles of beer on the wall,Option 1,0.5516236343,18JUN1962,01JAN1960:10:52:23,0:00:11,51,40
10370,370 bottles of beer on the wall,Option 1,0.5205378246,19JAN1960,01JAN1960:06:54:14,0:00:58,2,72
10371,371 bottles of beer on the wall,Option 1,0.9941610768,28MAR1962,01JAN1960:04:55:58,0:00:22,46,65
10372,372 bottles of beer on the wall,Option 1,0.065678,07MAY1961,01JAN1960:10:17:35,0:00:10,54,100
10373,373 bottles of beer on the wall,Option 1,0.7222646138,17JUL1961,01JAN1960:01:47:32,0:00:51,26,96
10374,374 bottles of beer on the wall,Option 1,0.8772228294,23JUL1960,01JAN1960:00:30:51,0:00:40,18,45
10375,375 bottles of beer on the wall,Option 1,0.3651081847,11DEC1961,01JAN1960:00:46:15,0:00:15,14,90
10376,376 bottles of beer on the wall,Option 1,0.3800431529,15AUG1960,01JAN1960:05:30:55,0:00:19,13,74
10377,377 bottles of beer on the wall,Option 1,0.1077003503,26FEB1960,01JAN1960:04:48:40,0:00:08,51,53
10378,378 bottles of beer on the wall,Option 1,0.7945664035,06MAR1961,01JAN1960:05:36:47,0:00:45,65,39
10379,379 bottles of beer on the wall,Option 1,0.8754883054,06JUN1960,01JAN1960:06:09:33,0:00:04,3,3
10380,380 bottles of beer on the wall,Option 1,0.9975561108,10AUG1960,01JAN1960:10:34:33,0:00:28,92,29
10381,381 bottles of beer on the wall,Option 1,0.9817031599,07JUL1960,01JAN1960:01:40:00,0:00:39,63,45
10382,382 bottles of beer on the wall,Option 1,0.4427802341,07JAN1962,01JAN1960:01:21:32,0:01:31,7,54
10383,383 bottles of beer on the wall,Option 1,0.03180474,17JUL1962,01JAN1960:07:15:54,0:01:08,72,60
10384,384 bottles of beer on the wall,Option 1,0.1031627707,10MAY1962,01JAN1960:02:52:58,0:01:31,6,64
10385,385 bottles of beer on the wall,Option 1,0.911744344,01MAY1960,01JAN1960:03:51:16,0:01:04,1,54
10386,386 bottles of beer on the wall,Option 1,0.4912374353,13FEB1961,01JAN1960:07:22:49,0:01:21,70,71
10387,387 bottles of beer on the wall,Option 1,0.8803869509,04JUL1960,01JAN1960:12:14:05,0:00:18,78,88
10388,388 bottles of beer on the wall,Option 1,0.0596609544,17DEC1960,01JAN1960:08:29:20,0:00:53,13,84
10389,389 bottles of beer on the wall,Option 1,0.6625022132,12JAN1961,01JAN1960:11:15:39,0:01:05,10,31
10390,390 bottles of beer on the wall,Option 1,0.2669677358,05OCT1961,01JAN1960:00:48:03,0:01:33,29,31
10391,391 bottles of beer on the wall,Option 1,0.8095563454,04DEC1961,01JAN1960:07:54:13,0:01:15,95,2
10392,392 bottles of beer on the wall,Option 1,0.6406704796,27JAN1961,01JAN1960:03:35:55,0:00:36,92,47
10393,393 bottles of beer on the wall,Option 1,0.2188341847,02MAR1960,01JAN1960:04:05:21,0:00:11,28,34
10394,394 bottles of beer on the wall,Option 1,0.7052043424,09JUN1962,01JAN1960:07:00:21,0:01:14,84,19
10395,395 bottles of beer on the wall,Option 1,0.9843204464,18APR1960,01JAN1960:04:54:31,0:01:29,46,46
10396,396 bottles of beer on the wall,Option 1,0.329249541,10SEP1961,01JAN1960:13:21:04,0:00:21,70,11
10397,397 bottles of beer on the wall,Option 1,0.2073628675,13JUL1962,01JAN1960:11:16:26,0:01:23,17,97
10398,398 bottles of beer on the wall,Option 1,0.7881676665,29JUN1962,01JAN1960:11:36:47,0:00:50,39,72
10399,399 bottles of beer on the wall,Option 1,0.4347905537,31AUG1962,01JAN1960:02:30:30,0:00:21,86,61
10400,400 bottles of beer on the wall,Option 1,0.2937454084,05APR1962,01JAN1960:02:44:06,0:00:32,27,81
10401,401 bottles of beer on the wall,Option 1,0.6659902565,10APR1961,01JAN1960:11:53:59,0:00:01,80,17
10402,402 bottles of beer on the wall,Option 1,0.7637229686,07APR1962,01JAN1960:05:45:25,0:01:28,11,17
10403,403 bottles of beer on the wall,Option 1,0.3325480941,19MAR1961,01JAN1960:09:57:05,0:00:27,9,99
10404,404 bottles of beer on the wall,Option 1,0.580015553,10AUG1960,01JAN1960:06:15:44,0:00:25,27,99
10405,405 bottles of beer on the wall,Option 1,0.2514696071,08APR1961,01JAN1960:10:37:45,0:00:17,29,59
10406,406 bottles of beer on the wall,Option 1,0.3792107284,19MAR1962,01JAN1960:04:49:30,0:00:07,44,38
10407,407 bottles of beer on the wall,Option 1,0.4702913125,13DEC1961,01JAN1960:09:03:31,0:00:38,95,90
10408,408 bottles of beer on the wall,Option 1,0.4207190659,03FEB1961,01JAN1960:00:37:47,0:00:38,37,65
10409,409 bottles of beer on the wall,Option 1,0.2485069117,08DEC1961,01JAN1960:07:18:28,0:01:39,91,64
10410,410 bottles of beer on the wall,Option 1,0.3257893502,27NOV1961,01JAN1960:11:33:02,0:00:54,41,7
10411,411 bottles of beer on the wall,Option 1,0.0967283533,11AUG1962,01JAN1960:07:23:15,0:00:21,90,1
10412,412 bottles of beer on the wall,Option 1,0.532676542,01SEP1960,01JAN1960:02:46:29,0:00:45,31,48
10413,413 bottles of beer on the wall,Option 1,0.2564602151,15JAN1961,01JAN1960:01:09:11,0:01:21,16,89
10414,414 bottles of beer on the wall,Option 1,0.0865634024,20MAR1962,01JAN1960:12:56:45,0:00:05,75,51
10415,415 bottles of beer on the wall,Option 1,0.2926342321,07FEB1960,01JAN1960:06:48:55,0:00:36,66,33
10416,416 bottles of beer on the wall,Option 1,0.0678594029,06OCT1961,01JAN1960:07:14:58,0:01:26,50,16
10417,417 bottles of beer on the wall,Option 1,0.6376664767,09JUN1960,01JAN1960:05:00:13,0:01:37,7,92
10418,418 bottles of beer on the wall,Option 1,0.4795483525,14JUN1962,01JAN1960:00:00:50,0:00:50,33,74
10419,419 bottles of beer on the wall,Option 1,0.3492934342,22MAR1960,01JAN1960:08:54:52,0:01:26,61,72
10420,420 bottles of beer on the wall,Option 1,0.5085071356,14MAR1960,01JAN1960:09:47:51,0:00:56,36,63
10421,421 bottles of beer on the wall,Option 1,0.414953564,25MAR1961,01JAN1960:03:35:19,0:00:30,47,30
10422,422 bottles of beer on the wall,Option 1,0.2431976652,25SEP1960,01JAN1960:08:47:25,0:00:17,6,42
10423,423 bottles of beer on the wall,Option 1,0.2370444998,04MAR1962,01JAN1960:00:16:44,0:01:36,54,100
10424,424 bottles of beer on the wall,Option 1,0.8687893324,12OCT1961,01JAN1960:03:16:33,0:00:56,97,25
10425,425 bottles of beer on the wall,Option 1,0.0470510335,10MAY1960,01JAN1960:02:44:07,0:01:05,83,59
10426,426 bottles of beer on the wall,Option 1,0.7114342887,12APR1960,01JAN1960:02:17:40,0:00:39,57,97
10427,427 bottles of beer on the wall,Option 1,0.6176668283,05JUL1962,01JAN1960:09:09:12,0:01:03,92,38
10428,428 bottles of beer on the wall,Option 1,0.0657907743,01JUL1962,01JAN1960:11:03:37,0:01:35,97,63
10429,429 bottles of beer on the wall,Option 1,0.280104912,13MAR1962,01JAN1960:10:35:08,0:01:07,42,35
10430,430 bottles of beer on the wall,Option 1,0.3639827088,02APR1960,01JAN1960:12:00:50,0:00:40,91,84
10431,431 bottles of beer on the wall,Option 1,0.2130561137,25AUG1961,01JAN1960:12:07:22,0:01:37,70,9
10432,432 bottles of beer on the wall,Option 1,0.9656705889,23DEC1960,01JAN1960:02:36:48,0:01:28,23,92
10433,433 bottles of beer on the wall,Option 1,0.5824601052,22JAN1961,01JAN1960:01:19:48,0:00:32,73,6
10434,434 bottles of beer on the wall,Option 1,0.5207124942,23APR1961,01JAN1960:10:45:16,0:01:08,45,85
10435,435 bottles of beer on the wall,Option 1,0.9280530661,01NOV1960,01JAN1960:12:46:59,0:01:36,57,4
10436,436 bottles of beer on the wall,Option 1,0.8982810149,29NOV1961,01JAN1960:09:23:13,0:01:15,100,68
10437,437 bottles of beer on the wall,Option 1,0.3268259467,04MAR1960,01JAN1960:01:22:42,0:00:04,99,61
10438,438 bottles of beer on the wall,Option 1,0.2038002704,12FEB1962,01JAN1960:09:03:24,0:00:40,97,78
10439,439 bottles of beer on the wall,Option 1,0.0798850833,11MAY1960,01JAN1960:00:32:50,0:00:37,7,49
10440,440 bottles of beer on the wall,Option 1,0.6697000566,01SEP1960,01JAN1960:08:04:03,0:00:30,7,68
10441,441 bottles of beer on the wall,Option 1,0.550699767,11JAN1960,01JAN1960:06:33:15,0:01:09,19,10
10442,442 bottles of beer on the wall,Option 1,0.9514032798,07FEB1962,01JAN1960:06:03:16,0:01:13,65,87
10443,443 bottles of beer on the wall,Option 1,0.1182718324,08FEB1960,01JAN1960:11:28:52,0:00:46,61,59
10444,444 bottles of beer on the wall,Option 1,0.5772118874,11FEB1962,01JAN1960:01:14:29,0:01:07,16,22
10445,445 bottles of beer on the wall,Option 1,0.9828714463,06DEC1960,01JAN1960:05:47:08,0:01:40,66,4
10446,446 bottles of beer on the wall,Option 1,0.1229167269,02JUN1960,01JAN1960:09:43:51,0:01:34,29,24
10447,447 bottles of beer on the wall,Option 1,0.2102257522,09MAR1961,01JAN1960:04:06:09,0:01:27,53,78
10448,448 bottles of beer on the wall,Option 1,0.9958884935,20FEB1962,01JAN1960:06:43:53,0:00:07,42,65
10449,449 bottles of beer on the wall,Option 1,0.3530408085,04MAY1961,01JAN1960:11:26:59,0:01:37,76,35
10450,450 bottles of beer on the wall,Option 1,0.2848354258,03SEP1960,01JAN1960:13:26:36,0:00:25,69,38
10451,451 bottles of beer on the wall,Option 1,0.1461860818,10JUN1960,01JAN1960:01:24:25,0:00:02,61,23
10452,452 bottles of beer on the wall,Option 1,0.2033735766,29AUG1960,01JAN1960:12:30:17,0:00:19,32,75
10453,453 bottles of beer on the wall,Option 1,0.7973149958,26JUN1961,01JAN1960:04:46:06,0:00:21,94,14
10454,454 bottles of beer on the wall,Option 1,0.6812086425,07NOV1961,01JAN1960:01:59:51,0:00:12,89,59
10455,455 bottles of beer on the wall,Option 1,0.6111464974,24FEB1962,01JAN1960:07:42:56,0:00:15,23,92
10456,456 bottles of beer on the wall,Option 1,0.9542484209,09APR1961,01JAN1960:09:09:17,0:01:26,87,93
10457,457 bottles of beer on the wall,Option 1,0.9818577077,18AUG1960,01JAN1960:12:24:47,0:01:08,18,20
10458,458 bottles of beer on the wall,Option 1,0.9331120881,06JUL1961,01JAN1960:10:10:02,0:01:40,78,36
10459,459 bottles of beer on the wall,Option 1,0.8953045587,22APR1960,01JAN1960:04:47:34,0:00:37,13,46
10460,460 bottles of beer on the wall,Option 1,0.1584420461,08JAN1961,01JAN1960:07:47:49,0:00:05,85,61
10461,461 bottles of beer on the wall,Option 1,0.9324573814,24DEC1960,01JAN1960:02:13:36,0:00:22,55,94
10462,462 bottles of beer on the wall,Option 1,0.0813189871,21AUG1961,01JAN1960:09:18:28,0:00:36,42,49
10463,463 bottles of beer on the wall,Option 1,0.8736312594,17MAY1962,01JAN1960:12:52:58,0:00:30,45,7
10464,464 bottles of beer on the wall,Option 1,0.1894013794,06NOV1960,01JAN1960:12:56:13,0:01:30,11,50
10465,465 bottles of beer on the wall,Option 1,0.9149773772,07JUN1960,01JAN1960:08:53:28,0:00:22,23,17
10466,466 bottles of beer on the wall,Option 1,0.6329479598,27JUN1962,01JAN1960:07:38:23,0:00:06,68,14
10467,467 bottles of beer on the wall,Option 1,0.1897066413,21AUG1961,01JAN1960:10:15:28,0:01:12,59,46
10468,468 bottles of beer on the wall,Option 1,0.8352205767,16MAR1961,01JAN1960:06:48:50,0:00:44,60,37
10469,469 bottles of beer on the wall,Option 1,0.3339443432,09OCT1960,01JAN1960:00:52:53,0:00:27,70,44
10470,470 bottles of beer on the wall,Option 1,0.1096137567,09JUL1962,01JAN1960:08:28:10,0:01:15,88,81
10471,471 bottles of beer on the wall,Option 1,0.1481936733,10JAN1962,01JAN1960:06:38:41,0:00:56,21,77
10472,472 bottles of beer on the wall,Option 1,0.5630390432,01MAR1960,01JAN1960:06:35:09,0:01:08,70,32
10473,473 bottles of beer on the wall,Option 1,0.0604085713,12APR1962,01JAN1960:02:41:26,0:00:46,36,17
10474,474 bottles of beer on the wall,Option 1,0.6011423606,25DEC1961,01JAN1960:02:57:45,0:01:11,36,18
10475,475 bottles of beer on the wall,Option 1,0.7698335782,31JUL1962,01JAN1960:07:46:29,0:01:34,72,17
10476,476 bottles of beer on the wall,Option 1,0.3823641224,17MAR1962,01JAN1960:03:34:37,0:00:09,27,86
10477,477 bottles of beer on the wall,Option 1,0.9225378446,12JAN1962,01JAN1960:08:41:25,0:01:06,45,20
10478,478 bottles of beer on the wall,Option 1,0.2979750453,23JUN1962,01JAN1960:10:06:36,0:01:28,88,7
10479,479 bottles of beer on the wall,Option 1,0.4988665942,15JUL1961,01JAN1960:02:33:06,0:01:33,85,1
10480,480 bottles of beer on the wall,Option 1,0.5243853585,29JUL1960,01JAN1960:00:26:18,0:00:31,48,77
10481,481 bottles of beer on the wall,Option 1,0.6049248826,12JUL1962,01JAN1960:10:01:41,0:00:16,66,45
10482,482 bottles of beer on the wall,Option 1,0.6312438024,01OCT1961,01JAN1960:05:18:12,0:00:04,43,27
10483,483 bottles of beer on the wall,Option 1,0.3366865974,17MAY1962,01JAN1960:01:44:17,0:00:01,31,25
10484,484 bottles of beer on the wall,Option 1,0.0813266188,08AUG1962,01JAN1960:06:18:27,0:00:08,71,63
10485,485 bottles of beer on the wall,Option 1,0.3601163455,23JAN1962,01JAN1960:11:26:01,0:00:37,41,88
10486,486 bottles of beer on the wall,Option 1,0.9033777345,13AUG1961,01JAN1960:00:16:14,0:00:56,82,33
10487,487 bottles of beer on the wall,Option 1,0.1716986667,23DEC1960,01JAN1960:12:06:34,0:01:13,2,32
10488,488 bottles of beer on the wall,Option 1,0.9734818912,11SEP1961,01JAN1960:00:34:41,0:00:28,17,7
10489,489 bottles of beer on the wall,Option 1,0.0618140223,17DEC1961,01JAN1960:06:26:30,0:00:38,94,40
10490,490 bottles of beer on the wall,Option 1,0.0204490144,22AUG1960,01JAN1960:01:50:18,0:00:19,40,56
10491,491 bottles of beer on the wall,Option 1,0.8719323929,23MAY1960,01JAN1960:12:36:06,0:00:08,49,44
10492,492 bottles of beer on the wall,Option 1,0.2656292181,28JUN1962,01JAN1960:05:32:50,0:01:35,15,39
10493,493 bottles of beer on the wall,Option 1,0.37794835,23JUL1962,01JAN1960:13:15:43,0:00:56,6,86
10494,494 bottles of beer on the wall,Option 1,0.6395865733,17JAN1961,01JAN1960:12:20:12,0:01:31,22,87
10495,495 bottles of beer on the wall,Option 1,0.4408595098,05JUL1960,01JAN1960:12:21:53,0:01:40,25,27
10496,496 bottles of beer on the wall,Option 1,0.4846261169,02DEC1961,01JAN1960:12:41:08,0:00:06,7,63
10497,497 bottles of beer on the wall,Option 1,0.7903671478,14MAY1962,01JAN1960:05:09:21,0:01:03,98,25
10498,498 bottles of beer on the wall,Option 1,0.60474184,13OCT1961,01JAN1960:03:52:59,0:01:22,98,98
10499,499 bottles of beer on the wall,Option 1,0.1213259218,23APR1962,01JAN1960:06:29:47,0:01:35,38,70
10500,500 bottles of beer on the wall,Option 1,0.7882370487,17FEB1962,01JAN1960:02:00:25,0:00:12,1,74
1 PRIMARY_KEY_FIELD SOME_CHAR SOME_DROPDOWN SOME_NUM SOME_DATE SOME_DATETIME SOME_TIME SOME_SHORTNUM SOME_BESTNUM
2 0 abc Option abc 42 12FEB1960 01JAN1960:00:00:42 0:00:42 3 44
3 1 more dummy data Option 2 42 12FEB1960 01JAN1960:00:00:42 0:07:02 3 44
4 2 even more dummy data Option 3 42 12FEB1960 01JAN1960:00:00:42 0:02:22 3 44
5 3 It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: It was a dark and stormy night. The wind was blowing a gale! The captain said to his mate - mate, tell us a tale. And this, is the tale he told: Option 2 1613.001 27FEB1961 01JAN1960:00:07:03 0:00:44 3 44
6 4 if you can fill the unforgiving minute Option 1 1613.0011235 02AUG1971 29MAY1973:06:12:03 0:06:52 3 44
7 1010 10 bottles of beer on the wall Option 1 0.9153696885 04MAR1962 01JAN1960:12:47:55 0:01:40 92 76
8 1011 11 bottles of beer on the wall Option 1 0.3531217558 29MAR1960 01JAN1960:03:33:24 0:01:03 80 29
9 1012 12 bottles of beer on the wall Option 1 0.6743748717 02AUG1962 01JAN1960:07:25:59 0:00:10 16 98
10 1013 13 bottles of beer on the wall Option 1 0.1305445992 11SEP1960 01JAN1960:13:51:32 0:00:35 73 15
11 1014 14 bottles of beer on the wall Option 1 0.7409067949 26JUL1960 01JAN1960:05:18:10 0:00:41 30 89
12 1015 15 bottles of beer on the wall Option 1 0.0869016028 28FEB1961 01JAN1960:13:23:45 0:00:44 80 3
13 1016 16 bottles of beer on the wall Option 1 0.0462121419 09AUG1962 01JAN1960:07:42:38 0:01:17 62 2
14 1017 17 bottles of beer on the wall Option 1 0.7501918947 14MAY1962 01JAN1960:04:40:20 0:00:15 53 65
15 1018 18 bottles of beer on the wall Option 1 0.7300173294 03AUG1962 01JAN1960:03:20:41 0:00:41 21 60
16 1019 19 bottles of beer on the wall Option 1 0.6960950437 01JUN1960 01JAN1960:01:58:52 0:01:08 38 5
17 1020 20 bottles of beer on the wall Option 1 0.6190566065 30MAY1961 01JAN1960:09:04:20 0:01:07 92 23
18 1021 21 bottles of beer on the wall Option 1 0.5173368238 07JAN1961 01JAN1960:07:52:34 0:00:52 57 21
19 1022 22 bottles of beer on the wall Option 1 0.4720626452 07NOV1960 01JAN1960:12:12:00 0:00:26 53 32
20 1023 23 bottles of beer on the wall Option 1 0.2856596393 08AUG1960 01JAN1960:06:09:25 0:00:28 40 12
21 1024 24 bottles of beer on the wall Option 1 0.5160869418 02JUN1960 01JAN1960:06:36:06 0:01:10 41 81
22 1025 25 bottles of beer on the wall Option 1 0.1683158517 05JAN1961 01JAN1960:08:14:35 0:00:06 18 53
23 1026 26 bottles of beer on the wall Option 1 0.8951142248 28NOV1961 01JAN1960:03:31:17 0:00:58 79 54
24 1027 27 bottles of beer on the wall Option 1 0.7037817481 01SEP1961 01JAN1960:05:48:34 0:00:29 50 15
25 1028 28 bottles of beer on the wall Option 1 0.6193826714 31MAR1962 01JAN1960:02:49:39 0:00:24 78 87
26 1029 29 bottles of beer on the wall Option 1 0.9339028457 06DEC1961 01JAN1960:02:57:57 0:00:24 73 64
27 1030 30 bottles of beer on the wall Option 1 0.5647351339 10AUG1960 01JAN1960:11:02:59 0:00:55 39 28
28 1031 31 bottles of beer on the wall Option 1 0.1218988607 19JUN1961 01JAN1960:04:19:32 0:00:58 51 32
29 1032 32 bottles of beer on the wall Option 1 0.3459929113 14MAY1962 01JAN1960:05:42:48 0:00:54 96 46
30 1033 33 bottles of beer on the wall Option 1 0.092664999 31AUG1962 01JAN1960:00:08:34 0:00:51 69 90
31 1034 34 bottles of beer on the wall Option 1 0.9793458097 08FEB1960 01JAN1960:01:55:23 0:00:42 45 28
32 1035 35 bottles of beer on the wall Option 1 0.8964386624 18DEC1961 01JAN1960:04:42:45 0:00:07 49 97
33 1036 36 bottles of beer on the wall Option 1 0.0961652911 13NOV1960 01JAN1960:03:44:53 0:01:25 62 59
34 1037 37 bottles of beer on the wall Option 1 0.3475089201 16JAN1962 01JAN1960:01:35:19 0:00:15 23 50
35 1038 38 bottles of beer on the wall Option 1 0.3096271312 21MAY1960 01JAN1960:09:51:33 0:00:15 2 71
36 1039 39 bottles of beer on the wall Option 1 0.9445223114 28AUG1962 01JAN1960:07:09:31 0:00:12 30 31
37 1040 40 bottles of beer on the wall Option 1 0.5626084667 06NOV1960 01JAN1960:01:42:16 0:01:14 18 97
38 1041 41 bottles of beer on the wall Option 1 0.9432962513 01JUN1962 01JAN1960:03:30:04 0:00:11 20 34
39 1042 42 bottles of beer on the wall Option 1 0.5802429382 08JUL1961 01JAN1960:08:12:43 0:01:26 18 5
40 1043 43 bottles of beer on the wall Option 1 0.1970176255 27MAR1961 01JAN1960:00:19:45 0:01:29 13 76
41 1044 44 bottles of beer on the wall Option 1 0.4980671608 05JAN1961 01JAN1960:13:36:08 0:00:56 4 36
42 1045 45 bottles of beer on the wall Option 1 0.2486515531 05MAY1962 01JAN1960:08:47:09 0:00:42 2 23
43 1046 46 bottles of beer on the wall Option 1 0.4097825794 20JUN1960 01JAN1960:03:33:26 0:00:31 98 71
44 1047 47 bottles of beer on the wall Option 1 0.138754441 28JAN1960 01JAN1960:00:57:41 0:00:18 80 32
45 1048 48 bottles of beer on the wall Option 1 0.0249874415 03MAR1960 01JAN1960:11:33:53 0:00:04 96 76
46 1049 49 bottles of beer on the wall Option 1 0.8395310011 06NOV1961 01JAN1960:09:54:04 0:00:52 28 45
47 1050 50 bottles of beer on the wall Option 1 0.0942291618 14APR1962 01JAN1960:08:09:30 0:01:36 37 86
48 1051 51 bottles of beer on the wall Option 1 0.1670458001 13NOV1961 01JAN1960:01:05:55 0:00:25 42 83
49 1052 52 bottles of beer on the wall Option 1 0.3122402715 04JUN1960 01JAN1960:03:47:47 0:01:01 18 78
50 1053 53 bottles of beer on the wall Option 1 0.3854694261 14JUN1960 01JAN1960:02:43:08 0:00:06 22 67
51 1054 54 bottles of beer on the wall Option 1 0.1950434345 14NOV1961 01JAN1960:02:46:34 0:00:55 42 0
52 1055 55 bottles of beer on the wall Option 1 0.4948673586 29MAR1962 01JAN1960:00:48:06 0:01:04 28 4
53 1056 56 bottles of beer on the wall Option 1 0.6464513832 06SEP1962 01JAN1960:10:08:36 0:01:02 43 82
54 1057 57 bottles of beer on the wall Option 1 0.0724864798 20JUN1961 01JAN1960:12:22:51 0:01:27 82 53
55 1058 58 bottles of beer on the wall Option 1 0.8114467793 20MAR1962 01JAN1960:06:11:33 0:01:29 40 89
56 1059 59 bottles of beer on the wall Option 1 0.6348024321 28JUN1962 01JAN1960:05:21:21 0:01:37 55 41
57 1060 60 bottles of beer on the wall Option 1 0.8019492933 08APR1961 01JAN1960:12:37:00 0:01:29 49 88
58 1061 61 bottles of beer on the wall Option 1 0.4695742002 29JAN1962 01JAN1960:08:54:24 0:00:15 40 91
59 1062 62 bottles of beer on the wall Option 1 0.902706475 15JUN1961 01JAN1960:09:46:49 0:00:23 74 70
60 1063 63 bottles of beer on the wall Option 1 0.4557614594 16JUL1961 01JAN1960:02:06:05 0:01:09 7 3
61 1064 64 bottles of beer on the wall Option 1 0.6632444466 20MAY1961 01JAN1960:02:44:44 0:00:20 42 100
62 1065 65 bottles of beer on the wall Option 1 0.3901674 31AUG1961 01JAN1960:07:56:49 0:00:32 98 3
63 1066 66 bottles of beer on the wall Option 1 0.8453234848 30JUN1962 01JAN1960:04:51:54 0:01:02 51 22
64 1067 67 bottles of beer on the wall Option 1 0.9370150906 26APR1960 01JAN1960:04:05:08 0:01:39 28 86
65 1068 68 bottles of beer on the wall Option 1 0.8854161277 22MAR1962 01JAN1960:10:38:49 0:01:07 60 85
66 1069 69 bottles of beer on the wall Option 1 0.1327841906 24MAY1960 01JAN1960:01:18:46 0:01:15 88 85
67 1070 70 bottles of beer on the wall Option 1 0.5846563226 27JUL1962 01JAN1960:03:52:31 0:00:09 20 8
68 1071 71 bottles of beer on the wall Option 1 0.0257193684 18FEB1961 01JAN1960:03:25:01 0:00:29 1 62
69 1072 72 bottles of beer on the wall Option 1 0.9471486034 01JUN1962 01JAN1960:04:05:25 0:01:22 65 20
70 1073 73 bottles of beer on the wall Option 1 0.3037446282 16MAY1962 01JAN1960:05:10:19 0:00:01 14 34
71 1074 74 bottles of beer on the wall Option 1 0.2508690675 01NOV1961 01JAN1960:11:26:03 0:00:15 8 74
72 1075 75 bottles of beer on the wall Option 1 0.814380363 17SEP1960 01JAN1960:09:00:38 0:00:25 95 1
73 1076 76 bottles of beer on the wall Option 1 0.3761493621 16AUG1961 01JAN1960:01:48:17 0:00:52 5 38
74 1077 77 bottles of beer on the wall Option 1 0.3621215761 25JUL1961 01JAN1960:11:48:47 0:01:20 86 90
75 1078 78 bottles of beer on the wall Option 1 0.0268799584 20MAY1961 01JAN1960:12:43:34 0:01:00 70 96
76 1079 79 bottles of beer on the wall Option 1 0.4112483945 27JUL1962 01JAN1960:01:20:24 0:01:26 66 20
77 1080 80 bottles of beer on the wall Option 1 0.9501868011 15APR1961 01JAN1960:09:58:20 0:00:51 93 79
78 1081 81 bottles of beer on the wall Option 1 0.9866548018 13SEP1961 01JAN1960:05:20:04 0:00:14 28 97
79 1082 82 bottles of beer on the wall Option 1 0.9907830073 22FEB1962 01JAN1960:03:29:03 0:00:17 16 91
80 1083 83 bottles of beer on the wall Option 1 0.8927816567 11MAR1960 01JAN1960:05:52:48 0:01:26 54 14
81 1084 84 bottles of beer on the wall Option 1 0.12871663 02FEB1961 01JAN1960:10:34:37 0:00:44 52 90
82 1085 85 bottles of beer on the wall Option 1 0.5490252802 02JAN1960 01JAN1960:06:11:58 0:00:27 4 98
83 1086 86 bottles of beer on the wall Option 1 0.5432773864 11FEB1960 01JAN1960:08:40:00 0:01:15 79 19
84 1087 87 bottles of beer on the wall Option 1 0.8223943137 01OCT1960 01JAN1960:08:11:33 0:01:19 2 86
85 1088 88 bottles of beer on the wall Option 1 0.8496777699 09FEB1962 01JAN1960:03:10:35 0:00:15 95 6
86 1089 89 bottles of beer on the wall Option 1 0.9308730536 27MAY1962 01JAN1960:11:57:53 0:01:18 86 90
87 1090 90 bottles of beer on the wall Option 1 0.3072653344 23FEB1962 01JAN1960:04:52:38 0:00:25 85 17
88 1091 91 bottles of beer on the wall Option 1 0.7687679575 12FEB1960 01JAN1960:08:47:11 0:01:20 8 7
89 1092 92 bottles of beer on the wall Option 1 0.1873595105 29SEP1961 01JAN1960:04:29:58 0:00:29 17 78
90 1093 93 bottles of beer on the wall Option 1 0.0495966631 03OCT1961 01JAN1960:03:18:50 0:00:39 89 56
91 1094 94 bottles of beer on the wall Option 1 0.2607690526 19SEP1960 01JAN1960:03:22:28 0:00:29 81 16
92 1095 95 bottles of beer on the wall Option 1 0.549640266 07JUN1962 01JAN1960:06:15:32 0:00:04 57 70
93 1096 96 bottles of beer on the wall Option 1 0.9993291092 08MAR1961 01JAN1960:13:49:08 0:00:33 37 28
94 1097 97 bottles of beer on the wall Option 1 0.9517237963 02SEP1960 01JAN1960:05:16:03 0:00:40 77 61
95 1098 98 bottles of beer on the wall Option 1 0.5952155588 14FEB1962 01JAN1960:05:05:11 0:01:29 63 83
96 1099 99 bottles of beer on the wall Option 1 0.7526210732 05MAY1961 01JAN1960:06:58:36 0:00:02 95 1
97 10100 100 bottles of beer on the wall Option 1 0.307558153 17MAY1961 01JAN1960:06:13:01 0:01:37 68 7
98 10101 101 bottles of beer on the wall Option 1 0.6596710829 15APR1962 01JAN1960:08:34:02 0:00:43 66 43
99 10102 102 bottles of beer on the wall Option 1 0.0202811998 31AUG1961 01JAN1960:07:22:35 0:01:31 57 35
100 10103 103 bottles of beer on the wall Option 1 0.6699061034 02MAY1962 01JAN1960:05:13:17 0:00:36 30 23
101 10104 104 bottles of beer on the wall Option 1 0.330972748 04JUN1961 01JAN1960:06:47:20 0:01:05 69 82
102 10105 105 bottles of beer on the wall Option 1 0.2274839176 25JAN1961 01JAN1960:05:34:51 0:00:56 63 68
103 10106 106 bottles of beer on the wall Option 1 0.5612243989 27JUN1962 01JAN1960:04:32:03 0:01:15 19 73
104 10107 107 bottles of beer on the wall Option 1 0.7398902111 03SEP1962 01JAN1960:08:34:07 0:00:17 90 6
105 10108 108 bottles of beer on the wall Option 1 0.6124899791 08AUG1960 01JAN1960:04:59:34 0:00:25 56 12
106 10109 109 bottles of beer on the wall Option 1 0.882404773 26JAN1961 01JAN1960:01:29:15 0:01:26 36 4
107 10110 110 bottles of beer on the wall Option 1 0.4427004733 27FEB1961 01JAN1960:06:16:49 0:01:40 97 84
108 10111 111 bottles of beer on the wall Option 1 0.3609524622 10JAN1962 01JAN1960:09:48:37 0:01:11 87 62
109 10112 112 bottles of beer on the wall Option 1 0.9408929562 03AUG1960 01JAN1960:06:54:26 0:00:08 19 33
110 10113 113 bottles of beer on the wall Option 1 0.3149107319 10AUG1962 01JAN1960:13:01:00 0:00:04 75 60
111 10114 114 bottles of beer on the wall Option 1 0.0525069181 17APR1962 01JAN1960:13:00:52 0:00:35 9 23
112 10115 115 bottles of beer on the wall Option 1 0.145448105 14FEB1962 01JAN1960:04:06:08 0:00:26 45 91
113 10116 116 bottles of beer on the wall Option 1 0.2444279959 10OCT1961 01JAN1960:08:03:12 0:01:37 12 41
114 10117 117 bottles of beer on the wall Option 1 0.4619846043 30JUL1960 01JAN1960:09:40:16 0:00:50 2 88
115 10118 118 bottles of beer on the wall Option 1 0.0316203502 13JUL1961 01JAN1960:08:31:39 0:01:05 60 94
116 10119 119 bottles of beer on the wall Option 1 0.4738720574 13AUG1960 01JAN1960:01:31:05 0:01:17 43 79
117 10120 120 bottles of beer on the wall Option 1 0.8058761856 11JUN1960 01JAN1960:12:56:35 0:00:08 92 36
118 10121 121 bottles of beer on the wall Option 1 0.2955600979 08JUL1962 01JAN1960:06:09:22 0:00:03 94 80
119 10122 122 bottles of beer on the wall Option 1 0.0064115427 18SEP1962 01JAN1960:00:06:24 0:00:13 72 75
120 10123 123 bottles of beer on the wall Option 1 0.5678159327 21APR1960 01JAN1960:10:54:21 0:00:16 75 67
121 10124 124 bottles of beer on the wall Option 1 0.1431510994 10JAN1962 01JAN1960:01:57:00 0:00:12 48 31
122 10125 125 bottles of beer on the wall Option 1 0.3805634409 26JAN1962 01JAN1960:03:03:19 0:01:29 83 52
123 10126 126 bottles of beer on the wall Option 1 0.3833517993 26APR1960 01JAN1960:11:27:41 0:00:44 99 36
124 10127 127 bottles of beer on the wall Option 1 0.5669089111 04MAR1961 01JAN1960:05:36:22 0:01:18 43 27
125 10128 128 bottles of beer on the wall Option 1 0.1514211843 01NOV1960 01JAN1960:07:45:50 0:01:02 22 12
126 10129 129 bottles of beer on the wall Option 1 0.0446588583 05JAN1961 01JAN1960:02:13:55 0:00:42 27 46
127 10130 130 bottles of beer on the wall Option 1 0.7892141611 22APR1962 01JAN1960:04:17:54 0:01:05 75 84
128 10131 131 bottles of beer on the wall Option 1 0.5012088001 24DEC1960 01JAN1960:13:03:23 0:01:22 87 82
129 10132 132 bottles of beer on the wall Option 1 0.2327582944 07APR1961 01JAN1960:01:33:15 0:01:14 18 46
130 10133 133 bottles of beer on the wall Option 1 0.2234651173 20MAR1961 01JAN1960:13:52:02 0:01:06 42 58
131 10134 134 bottles of beer on the wall Option 1 0.4954405918 10FEB1961 01JAN1960:13:51:14 0:01:36 35 11
132 10135 135 bottles of beer on the wall Option 1 0.7874922891 15AUG1960 01JAN1960:00:21:57 0:00:52 45 36
133 10136 136 bottles of beer on the wall Option 1 0.3992494891 06SEP1961 01JAN1960:09:51:46 0:01:25 26 9
134 10137 137 bottles of beer on the wall Option 1 0.3964866136 25MAY1960 01JAN1960:03:19:48 0:00:28 44 3
135 10138 138 bottles of beer on the wall Option 1 0.9466173323 06APR1962 01JAN1960:13:19:18 0:01:27 78 51
136 10139 139 bottles of beer on the wall Option 1 0.6525219277 09APR1960 01JAN1960:05:43:49 0:00:21 63 6
137 10140 140 bottles of beer on the wall Option 1 0.4684071925 29MAY1961 01JAN1960:02:53:36 0:00:46 68 4
138 10141 141 bottles of beer on the wall Option 1 0.8581724013 16MAY1960 01JAN1960:01:45:44 0:01:32 31 85
139 10142 142 bottles of beer on the wall Option 1 0.825792401 23APR1961 01JAN1960:12:03:13 0:00:49 36 45
140 10143 143 bottles of beer on the wall Option 1 0.3172852538 20FEB1962 01JAN1960:12:38:31 0:01:34 51 78
141 10144 144 bottles of beer on the wall Option 1 0.670397946 27JAN1962 01JAN1960:04:59:37 0:00:39 38 99
142 10145 145 bottles of beer on the wall Option 1 0.3304372441 04JUN1960 01JAN1960:00:39:12 0:00:29 88 76
143 10146 146 bottles of beer on the wall Option 1 0.845151971 31JUL1962 01JAN1960:05:03:34 0:00:13 2 80
144 10147 147 bottles of beer on the wall Option 1 0.7957223709 02FEB1961 01JAN1960:00:03:07 0:01:11 29 99
145 10148 148 bottles of beer on the wall Option 1 0.323337108 29FEB1960 01JAN1960:01:58:05 0:01:17 23 65
146 10149 149 bottles of beer on the wall Option 1 0.1813316611 29JUN1960 01JAN1960:02:18:08 0:00:40 45 52
147 10150 150 bottles of beer on the wall Option 1 0.7860426655 05MAR1962 01JAN1960:01:57:15 0:00:26 31 91
148 10151 151 bottles of beer on the wall Option 1 0.3305453571 09APR1960 01JAN1960:07:08:32 0:01:30 72 15
149 10152 152 bottles of beer on the wall Option 1 0.9367212513 18AUG1962 01JAN1960:10:36:03 0:01:26 85 81
150 10153 153 bottles of beer on the wall Option 1 0.3385623458 19MAR1962 01JAN1960:04:21:46 0:01:29 11 54
151 10154 154 bottles of beer on the wall Option 1 0.9756794413 17JUN1961 01JAN1960:08:35:40 0:00:34 3 72
152 10155 155 bottles of beer on the wall Option 1 0.6385958868 21OCT1961 01JAN1960:08:51:00 0:00:50 1 6
153 10156 156 bottles of beer on the wall Option 1 0.3569769959 14AUG1960 01JAN1960:10:57:16 0:00:05 5 94
154 10157 157 bottles of beer on the wall Option 1 0.8559997239 23MAR1962 01JAN1960:12:03:38 0:00:08 96 68
155 10158 158 bottles of beer on the wall Option 1 0.2293701918 13AUG1960 01JAN1960:06:36:47 0:00:07 87 87
156 10159 159 bottles of beer on the wall Option 1 0.0007910165 20SEP1962 01JAN1960:11:49:02 0:00:55 18 69
157 10160 160 bottles of beer on the wall Option 1 0.5876370373 08JAN1960 01JAN1960:00:59:15 0:01:26 27 36
158 10161 161 bottles of beer on the wall Option 1 0.2354667514 11OCT1961 01JAN1960:01:27:14 0:01:04 90 28
159 10162 162 bottles of beer on the wall Option 1 0.0144103263 11AUG1961 01JAN1960:02:37:41 0:01:39 92 8
160 10163 163 bottles of beer on the wall Option 1 0.7087855668 03JUL1962 01JAN1960:02:07:23 0:01:35 51 33
161 10164 164 bottles of beer on the wall Option 1 0.7251478106 20MAY1960 01JAN1960:12:15:23 0:01:28 58 7
162 10165 165 bottles of beer on the wall Option 1 0.9629398403 06APR1962 01JAN1960:01:05:24 0:01:39 84 6
163 10166 166 bottles of beer on the wall Option 1 0.5155049164 14OCT1960 01JAN1960:04:02:06 0:00:30 63 96
164 10167 167 bottles of beer on the wall Option 1 0.1016342775 11MAY1960 01JAN1960:05:55:03 0:00:01 31 44
165 10168 168 bottles of beer on the wall Option 1 0.3690353596 12NOV1961 01JAN1960:12:49:02 0:00:03 2 79
166 10169 169 bottles of beer on the wall Option 1 0.5573803501 02SEP1962 01JAN1960:12:59:56 0:00:31 7 95
167 10170 170 bottles of beer on the wall Option 1 0.2008119497 10JUN1961 01JAN1960:05:59:06 0:01:29 88 4
168 10171 171 bottles of beer on the wall Option 1 0.6939068505 25MAY1962 01JAN1960:07:20:06 0:01:08 87 8
169 10172 172 bottles of beer on the wall Option 1 0.7013406594 14JUL1960 01JAN1960:04:04:24 0:00:11 44 96
170 10173 173 bottles of beer on the wall Option 1 0.83506724 30APR1961 01JAN1960:12:44:40 0:00:10 74 70
171 10174 174 bottles of beer on the wall Option 1 0.9339991943 26JAN1962 01JAN1960:04:59:32 0:01:09 13 66
172 10175 175 bottles of beer on the wall Option 1 0.8333402787 18FEB1961 01JAN1960:07:25:44 0:00:28 47 2
173 10176 176 bottles of beer on the wall Option 1 0.5998844433 03MAR1962 01JAN1960:03:45:33 0:00:52 2 61
174 10177 177 bottles of beer on the wall Option 1 0.6161394634 18DEC1960 01JAN1960:05:35:25 0:00:50 70 22
175 10178 178 bottles of beer on the wall Option 1 0.0821002392 21APR1960 01JAN1960:10:08:56 0:01:40 28 9
176 10179 179 bottles of beer on the wall Option 1 0.6845213462 23MAY1960 01JAN1960:13:15:46 0:00:03 89 13
177 10180 180 bottles of beer on the wall Option 1 0.3839034477 14MAY1960 01JAN1960:03:22:17 0:01:11 15 38
178 10181 181 bottles of beer on the wall Option 1 0.7949567609 21AUG1962 01JAN1960:02:41:01 0:00:57 90 93
179 10182 182 bottles of beer on the wall Option 1 0.5079025419 23SEP1962 01JAN1960:02:22:31 0:01:07 83 32
180 10183 183 bottles of beer on the wall Option 1 0.3215162574 26DEC1961 01JAN1960:09:03:00 0:01:38 46 94
181 10184 184 bottles of beer on the wall Option 1 0.3322958058 12MAY1961 01JAN1960:02:48:05 0:00:46 80 54
182 10185 185 bottles of beer on the wall Option 1 0.6510801453 07SEP1960 01JAN1960:11:49:02 0:00:59 51 47
183 10186 186 bottles of beer on the wall Option 1 0.060995535 15AUG1960 01JAN1960:02:21:08 0:01:40 5 61
184 10187 187 bottles of beer on the wall Option 1 0.8541180551 14SEP1960 01JAN1960:13:29:33 0:01:23 17 14
185 10188 188 bottles of beer on the wall Option 1 0.9427926219 23JUL1960 01JAN1960:05:19:05 0:01:13 22 97
186 10189 189 bottles of beer on the wall Option 1 0.2325015186 01FEB1960 01JAN1960:11:50:07 0:01:22 6 53
187 10190 190 bottles of beer on the wall Option 1 0.3687101493 21FEB1962 01JAN1960:06:44:23 0:00:13 16 30
188 10191 191 bottles of beer on the wall Option 1 0.7647511232 09JAN1960 01JAN1960:13:06:29 0:01:35 6 97
189 10192 192 bottles of beer on the wall Option 1 0.4105463565 17AUG1961 01JAN1960:11:04:32 0:01:14 38 33
190 10193 193 bottles of beer on the wall Option 1 0.8785403831 12JUL1962 01JAN1960:04:11:05 0:00:29 19 82
191 10194 194 bottles of beer on the wall Option 1 0.9304303433 11JUL1961 01JAN1960:12:36:57 0:01:02 20 35
192 10195 195 bottles of beer on the wall Option 1 0.7302505256 01MAR1961 01JAN1960:01:38:35 0:00:42 16 35
193 10196 196 bottles of beer on the wall Option 1 0.2536906177 04SEP1962 01JAN1960:05:18:23 0:01:18 91 50
194 10197 197 bottles of beer on the wall Option 1 0.1181504503 08AUG1961 01JAN1960:09:27:54 0:01:26 20 13
195 10198 198 bottles of beer on the wall Option 1 0.9275614228 17JUL1961 01JAN1960:01:52:18 0:00:06 52 73
196 10199 199 bottles of beer on the wall Option 1 0.7495222128 04APR1961 01JAN1960:09:28:04 0:00:42 30 41
197 10200 200 bottles of beer on the wall Option 1 0.925741082 02FEB1962 01JAN1960:12:23:10 0:00:07 79 17
198 10201 201 bottles of beer on the wall Option 1 0.2591843359 04DEC1960 01JAN1960:12:46:41 0:00:00 53 58
199 10202 202 bottles of beer on the wall Option 1 0.4289995704 17NOV1961 01JAN1960:02:20:52 0:00:35 41 25
200 10203 203 bottles of beer on the wall Option 1 0.4625803807 24JAN1960 01JAN1960:08:20:44 0:01:11 84 66
201 10204 204 bottles of beer on the wall Option 1 0.858440102 31AUG1962 01JAN1960:08:51:40 0:00:12 18 51
202 10205 205 bottles of beer on the wall Option 1 0.8964499016 01SEP1962 01JAN1960:05:33:47 0:00:23 34 77
203 10206 206 bottles of beer on the wall Option 1 0.5742789063 24OCT1961 01JAN1960:02:31:04 0:01:08 27 66
204 10207 207 bottles of beer on the wall Option 1 0.4864150954 29SEP1960 01JAN1960:09:27:46 0:01:28 31 26
205 10208 208 bottles of beer on the wall Option 1 0.4511992249 04DEC1960 01JAN1960:09:39:26 0:00:42 49 98
206 10209 209 bottles of beer on the wall Option 1 0.4218624157 13SEP1961 01JAN1960:01:40:55 0:01:39 35 50
207 10210 210 bottles of beer on the wall Option 1 0.1572868331 15FEB1960 01JAN1960:07:01:15 0:00:51 43 1
208 10211 211 bottles of beer on the wall Option 1 0.713915177 23MAR1960 01JAN1960:11:08:53 0:00:15 18 61
209 10212 212 bottles of beer on the wall Option 1 0.5677882165 19MAY1960 01JAN1960:01:27:23 0:01:02 34 89
210 10213 213 bottles of beer on the wall Option 1 0.7552938581 12SEP1961 01JAN1960:11:47:33 0:00:38 44 46
211 10214 214 bottles of beer on the wall Option 1 0.6071256071 28DEC1961 01JAN1960:05:28:18 0:01:23 84 66
212 10215 215 bottles of beer on the wall Option 1 0.7717189266 12MAR1960 01JAN1960:01:21:26 0:01:00 28 22
213 10216 216 bottles of beer on the wall Option 1 0.8985594329 24MAR1961 01JAN1960:10:48:58 0:01:31 93 2
214 10217 217 bottles of beer on the wall Option 1 0.3156879904 13AUG1960 01JAN1960:07:10:46 0:01:18 100 54
215 10218 218 bottles of beer on the wall Option 1 0.3408455315 08JUN1961 01JAN1960:02:26:49 0:00:05 65 82
216 10219 219 bottles of beer on the wall Option 1 0.6263580553 08JUN1962 01JAN1960:05:59:46 0:01:03 76 88
217 10220 220 bottles of beer on the wall Option 1 0.2878925355 19DEC1961 01JAN1960:08:23:41 0:00:00 92 1
218 10221 221 bottles of beer on the wall Option 1 0.0901017348 19JUL1962 01JAN1960:09:50:47 0:00:43 21 84
219 10222 222 bottles of beer on the wall Option 1 0.8967759362 14SEP1960 01JAN1960:12:25:58 0:01:22 34 50
220 10223 223 bottles of beer on the wall Option 1 0.9878171943 03DEC1961 01JAN1960:03:43:09 0:00:17 11 84
221 10224 224 bottles of beer on the wall Option 1 0.5275036886 13DEC1961 01JAN1960:03:12:56 0:01:36 85 49
222 10225 225 bottles of beer on the wall Option 1 0.442012436 12JUN1960 01JAN1960:11:40:23 0:01:40 76 87
223 10226 226 bottles of beer on the wall Option 1 0.582103689 10FEB1961 01JAN1960:01:50:49 0:00:59 53 29
224 10227 227 bottles of beer on the wall Option 1 0.5757669842 01NOV1960 01JAN1960:13:47:33 0:00:43 55 6
225 10228 228 bottles of beer on the wall Option 1 0.4786617507 07JAN1960 01JAN1960:13:36:24 0:01:22 91 53
226 10229 229 bottles of beer on the wall Option 1 0.1386274957 06APR1962 01JAN1960:03:48:29 0:01:27 36 48
227 10230 230 bottles of beer on the wall Option 1 0.4188394893 31MAY1962 01JAN1960:10:30:51 0:00:54 5 87
228 10231 231 bottles of beer on the wall Option 1 0.9250617777 18OCT1960 01JAN1960:04:29:52 0:00:38 34 94
229 10232 232 bottles of beer on the wall Option 1 0.3077528124 05FEB1960 01JAN1960:09:37:42 0:01:13 58 75
230 10233 233 bottles of beer on the wall Option 1 0.7316332277 29NOV1960 01JAN1960:08:56:57 0:01:13 34 53
231 10234 234 bottles of beer on the wall Option 1 0.5666298352 21NOV1960 01JAN1960:07:51:09 0:01:08 97 71
232 10235 235 bottles of beer on the wall Option 1 0.5736639409 03JUL1962 01JAN1960:11:57:25 0:00:51 15 49
233 10236 236 bottles of beer on the wall Option 1 0.6785667616 11FEB1962 01JAN1960:09:47:20 0:00:50 65 21
234 10237 237 bottles of beer on the wall Option 1 0.3721726869 05JUL1962 01JAN1960:11:58:22 0:01:32 82 21
235 10238 238 bottles of beer on the wall Option 1 0.0332283876 17AUG1961 01JAN1960:13:11:34 0:00:54 83 30
236 10239 239 bottles of beer on the wall Option 1 0.9734656848 02JAN1961 01JAN1960:00:36:43 0:00:19 31 54
237 10240 240 bottles of beer on the wall Option 1 0.3022106021 16FEB1961 01JAN1960:13:50:38 0:00:40 22 66
238 10241 241 bottles of beer on the wall Option 1 0.7546903294 06JUL1961 01JAN1960:12:36:17 0:01:29 16 85
239 10242 242 bottles of beer on the wall Option 1 0.2509871834 07MAR1962 01JAN1960:10:38:28 0:00:39 7 8
240 10243 243 bottles of beer on the wall Option 1 0.9526996668 15JAN1960 01JAN1960:04:24:42 0:01:01 69 80
241 10244 244 bottles of beer on the wall Option 1 0.1816610122 06FEB1962 01JAN1960:08:46:51 0:00:54 89 91
242 10245 245 bottles of beer on the wall Option 1 0.3928658876 21JUL1962 01JAN1960:12:59:42 0:00:38 24 27
243 10246 246 bottles of beer on the wall Option 1 0.3774878524 18FEB1961 01JAN1960:07:40:49 0:01:31 88 93
244 10247 247 bottles of beer on the wall Option 1 0.6063659362 01NOV1960 01JAN1960:01:19:07 0:00:05 82 73
245 10248 248 bottles of beer on the wall Option 1 0.119603098 14JUN1960 01JAN1960:04:29:22 0:00:58 87 47
246 10249 249 bottles of beer on the wall Option 1 0.4833748445 03JUL1960 01JAN1960:01:53:54 0:00:37 34 33
247 10250 250 bottles of beer on the wall Option 1 0.2244539946 10AUG1961 01JAN1960:06:19:01 0:01:15 87 97
248 10251 251 bottles of beer on the wall Option 1 0.9368193191 11JUN1962 01JAN1960:06:37:14 0:00:46 94 39
249 10252 252 bottles of beer on the wall Option 1 0.1791427751 10NOV1961 01JAN1960:00:49:22 0:00:47 96 21
250 10253 253 bottles of beer on the wall Option 1 0.5836302874 06JUN1961 01JAN1960:08:39:34 0:01:01 78 49
251 10254 254 bottles of beer on the wall Option 1 0.1289398275 28DEC1960 01JAN1960:12:25:05 0:00:43 67 99
252 10255 255 bottles of beer on the wall Option 1 0.7833669785 05SEP1962 01JAN1960:02:47:35 0:00:20 25 2
253 10256 256 bottles of beer on the wall Option 1 0.4945342483 29JAN1960 01JAN1960:00:54:13 0:01:13 72 56
254 10257 257 bottles of beer on the wall Option 1 0.0635836129 05JAN1961 01JAN1960:08:10:04 0:00:52 11 10
255 10258 258 bottles of beer on the wall Option 1 0.8188241654 09FEB1962 01JAN1960:06:33:00 0:01:21 41 96
256 10259 259 bottles of beer on the wall Option 1 0.3398916076 11FEB1960 01JAN1960:07:12:29 0:00:56 18 76
257 10260 260 bottles of beer on the wall Option 1 0.0814064155 21MAY1961 01JAN1960:11:03:51 0:01:18 78 29
258 10261 261 bottles of beer on the wall Option 1 0.6653245542 20JAN1962 01JAN1960:08:03:31 0:00:18 39 95
259 10262 262 bottles of beer on the wall Option 1 0.4036777021 04AUG1962 01JAN1960:12:32:27 0:00:08 57 63
260 10263 263 bottles of beer on the wall Option 1 0.8931138603 07JAN1962 01JAN1960:09:04:24 0:00:32 6 27
261 10264 264 bottles of beer on the wall Option 1 0.528584433 06APR1962 01JAN1960:09:43:19 0:01:00 24 41
262 10265 265 bottles of beer on the wall Option 1 0.8267822945 29JUL1960 01JAN1960:00:48:11 0:00:01 81 78
263 10266 266 bottles of beer on the wall Option 1 0.7218411401 17FEB1960 01JAN1960:07:30:38 0:00:08 35 81
264 10267 267 bottles of beer on the wall Option 1 0.1475262773 11NOV1960 01JAN1960:13:44:20 0:00:57 36 68
265 10268 268 bottles of beer on the wall Option 1 0.9412727286 30DEC1960 01JAN1960:02:46:30 0:01:19 5 92
266 10269 269 bottles of beer on the wall Option 1 0.3038877548 27NOV1960 01JAN1960:10:50:10 0:01:21 43 95
267 10270 270 bottles of beer on the wall Option 1 0.2756435532 15APR1962 01JAN1960:09:05:28 0:01:34 11 14
268 10271 271 bottles of beer on the wall Option 1 0.7056001121 31AUG1960 01JAN1960:08:48:52 0:00:02 9 51
269 10272 272 bottles of beer on the wall Option 1 0.5273708508 21SEP1962 01JAN1960:12:58:13 0:00:28 97 69
270 10273 273 bottles of beer on the wall Option 1 0.6002807215 03MAY1960 01JAN1960:10:14:48 0:00:40 52 32
271 10274 274 bottles of beer on the wall Option 1 0.6100557971 20JUN1960 01JAN1960:08:11:55 0:00:27 90 14
272 10275 275 bottles of beer on the wall Option 1 0.4197408638 07JUN1961 01JAN1960:12:07:18 0:00:26 64 100
273 10276 276 bottles of beer on the wall Option 1 0.4903712498 19JAN1960 01JAN1960:01:06:26 0:00:03 35 24
274 10277 277 bottles of beer on the wall Option 1 0.6658435406 04NOV1960 01JAN1960:00:04:17 0:00:37 7 84
275 10278 278 bottles of beer on the wall Option 1 0.5491365942 14JAN1961 01JAN1960:04:12:49 0:00:27 99 47
276 10279 279 bottles of beer on the wall Option 1 0.4473488622 13MAY1961 01JAN1960:12:06:34 0:01:16 19 20
277 10280 280 bottles of beer on the wall Option 1 0.4511988663 06JUL1962 01JAN1960:10:05:51 0:00:56 76 34
278 10281 281 bottles of beer on the wall Option 1 0.0783031066 11JUN1961 01JAN1960:09:58:43 0:01:05 9 63
279 10282 282 bottles of beer on the wall Option 1 0.776985302 20JUL1962 01JAN1960:10:44:29 0:01:00 59 10
280 10283 283 bottles of beer on the wall Option 1 0.468099362 31AUG1962 01JAN1960:05:26:33 0:00:20 35 52
281 10284 284 bottles of beer on the wall Option 1 0.4040679696 20FEB1962 01JAN1960:06:27:25 0:00:04 76 30
282 10285 285 bottles of beer on the wall Option 1 0.4549995947 20FEB1962 01JAN1960:10:36:57 0:00:34 2 43
283 10286 286 bottles of beer on the wall Option 1 0.7455339361 16SEP1961 01JAN1960:08:39:35 0:01:00 42 44
284 10287 287 bottles of beer on the wall Option 1 0.0209561712 04JAN1960 01JAN1960:05:52:58 0:00:24 32 7
285 10288 288 bottles of beer on the wall Option 1 0.4955981842 04JAN1962 01JAN1960:02:56:03 0:00:30 85 31
286 10289 289 bottles of beer on the wall Option 1 0.4131368219 10FEB1960 01JAN1960:11:57:31 0:00:16 37 88
287 10290 290 bottles of beer on the wall Option 1 0.3282186721 17OCT1960 01JAN1960:10:54:04 0:00:56 72 28
288 10291 291 bottles of beer on the wall Option 1 0.2116929005 18JAN1962 01JAN1960:06:56:27 0:00:11 87 82
289 10292 292 bottles of beer on the wall Option 1 0.8483731937 12FEB1962 01JAN1960:05:05:41 0:01:36 12 83
290 10293 293 bottles of beer on the wall Option 1 0.1560111345 13NOV1960 01JAN1960:10:04:22 0:00:03 94 4
291 10294 294 bottles of beer on the wall Option 1 0.7046207808 12APR1962 01JAN1960:13:50:47 0:00:32 31 97
292 10295 295 bottles of beer on the wall Option 1 0.2716620403 04AUG1961 01JAN1960:01:52:29 0:00:57 99 44
293 10296 296 bottles of beer on the wall Option 1 0.5543203496 12SEP1960 01JAN1960:13:43:54 0:00:44 49 1
294 10297 297 bottles of beer on the wall Option 1 0.983109036 31JUL1962 01JAN1960:01:07:33 0:00:36 4 10
295 10298 298 bottles of beer on the wall Option 1 0.8123072115 14SEP1962 01JAN1960:06:16:12 0:01:25 88 96
296 10299 299 bottles of beer on the wall Option 1 0.4276896559 05OCT1960 01JAN1960:02:55:07 0:00:58 83 76
297 10300 300 bottles of beer on the wall Option 1 0.8921809042 19JAN1962 01JAN1960:02:05:38 0:00:12 80 13
298 10301 301 bottles of beer on the wall Option 1 0.6041374279 10DEC1961 01JAN1960:01:06:29 0:01:27 62 9
299 10302 302 bottles of beer on the wall Option 1 0.0460550185 31MAY1962 01JAN1960:03:03:56 0:00:05 33 88
300 10303 303 bottles of beer on the wall Option 1 0.1868385622 12APR1962 01JAN1960:12:42:44 0:01:05 65 18
301 10304 304 bottles of beer on the wall Option 1 0.3386632657 28SEP1961 01JAN1960:11:24:06 0:00:42 2 93
302 10305 305 bottles of beer on the wall Option 1 0.6400271019 01JUN1960 01JAN1960:13:33:07 0:01:30 60 72
303 10306 306 bottles of beer on the wall Option 1 0.9534907304 18NOV1961 01JAN1960:02:02:51 0:00:54 7 57
304 10307 307 bottles of beer on the wall Option 1 0.6663103745 06SEP1961 01JAN1960:05:36:49 0:00:43 88 2
305 10308 308 bottles of beer on the wall Option 1 0.5392553073 13FEB1962 01JAN1960:11:28:18 0:01:08 16 8
306 10309 309 bottles of beer on the wall Option 1 0.0747909025 17OCT1961 01JAN1960:08:36:12 0:00:41 49 42
307 10310 310 bottles of beer on the wall Option 1 0.3249381847 30SEP1960 01JAN1960:08:12:54 0:00:09 96 89
308 10311 311 bottles of beer on the wall Option 1 0.9231011951 19MAY1962 01JAN1960:05:10:33 0:00:50 30 9
309 10312 312 bottles of beer on the wall Option 1 0.4658221637 21MAY1961 01JAN1960:12:55:25 0:01:39 16 20
310 10313 313 bottles of beer on the wall Option 1 0.7215524673 21FEB1960 01JAN1960:02:00:07 0:01:40 95 94
311 10314 314 bottles of beer on the wall Option 1 0.7328679942 28OCT1961 01JAN1960:09:07:00 0:00:25 42 71
312 10315 315 bottles of beer on the wall Option 1 0.1276036776 12JUN1960 01JAN1960:01:54:08 0:00:56 57 42
313 10316 316 bottles of beer on the wall Option 1 0.1270824723 15SEP1960 01JAN1960:03:19:25 0:00:21 85 9
314 10317 317 bottles of beer on the wall Option 1 0.3750520117 13JUN1961 01JAN1960:04:33:09 0:01:15 24 20
315 10318 318 bottles of beer on the wall Option 1 0.5777822102 10DEC1960 01JAN1960:13:32:14 0:00:09 98 28
316 10319 319 bottles of beer on the wall Option 1 0.140476402 27AUG1962 01JAN1960:08:52:46 0:01:08 64 83
317 10320 320 bottles of beer on the wall Option 1 0.2589205551 31MAY1961 01JAN1960:08:33:06 0:00:53 28 98
318 10321 321 bottles of beer on the wall Option 1 0.7350722825 16SEP1962 01JAN1960:05:47:44 0:01:17 79 95
319 10322 322 bottles of beer on the wall Option 1 0.1476364542 15JAN1960 01JAN1960:12:21:20 0:00:20 86 62
320 10323 323 bottles of beer on the wall Option 1 0.8700561099 15MAY1962 01JAN1960:00:47:05 0:00:20 90 15
321 10324 324 bottles of beer on the wall Option 1 0.6408788802 12SEP1962 01JAN1960:11:50:31 0:00:53 41 72
322 10325 325 bottles of beer on the wall Option 1 0.6961101623 27NOV1960 01JAN1960:00:10:49 0:01:17 28 72
323 10326 326 bottles of beer on the wall Option 1 0.1467710059 24FEB1961 01JAN1960:01:13:38 0:00:33 14 5
324 10327 327 bottles of beer on the wall Option 1 0.0215573572 09JUN1961 01JAN1960:11:47:17 0:00:21 57 10
325 10328 328 bottles of beer on the wall Option 1 0.4173900054 25JUL1962 01JAN1960:12:28:20 0:00:23 73 90
326 10329 329 bottles of beer on the wall Option 1 0.6395625713 02NOV1961 01JAN1960:08:49:34 0:00:37 77 79
327 10330 330 bottles of beer on the wall Option 1 0.0091438908 18MAY1962 01JAN1960:05:10:05 0:00:41 15 31
328 10331 331 bottles of beer on the wall Option 1 0.1024675197 11DEC1960 01JAN1960:13:12:57 0:00:23 50 13
329 10332 332 bottles of beer on the wall Option 1 0.057470562 11MAY1961 01JAN1960:03:43:04 0:00:17 48 14
330 10333 333 bottles of beer on the wall Option 1 0.8478633872 21JUL1961 01JAN1960:03:45:42 0:01:31 22 40
331 10334 334 bottles of beer on the wall Option 1 0.3442252541 24JUN1960 01JAN1960:01:19:31 0:00:48 82 25
332 10335 335 bottles of beer on the wall Option 1 0.7338460184 06JUN1962 01JAN1960:03:32:34 0:01:04 6 31
333 10336 336 bottles of beer on the wall Option 1 0.6217917342 09MAR1961 01JAN1960:06:37:39 0:00:50 70 84
334 10337 337 bottles of beer on the wall Option 1 0.6684890807 10OCT1961 01JAN1960:05:34:24 0:01:20 66 18
335 10338 338 bottles of beer on the wall Option 1 0.3695247562 05SEP1962 01JAN1960:00:25:21 0:01:18 48 37
336 10339 339 bottles of beer on the wall Option 1 0.9429265987 06DEC1960 01JAN1960:07:11:17 0:00:38 59 1
337 10340 340 bottles of beer on the wall Option 1 0.9266307265 17JUL1960 01JAN1960:06:33:59 0:00:21 12 13
338 10341 341 bottles of beer on the wall Option 1 0.7280535543 23FEB1961 01JAN1960:05:01:10 0:00:34 73 25
339 10342 342 bottles of beer on the wall Option 1 0.488654495 15AUG1962 01JAN1960:01:24:33 0:00:56 59 25
340 10343 343 bottles of beer on the wall Option 1 0.9526806548 28DEC1960 01JAN1960:07:26:17 0:00:58 97 61
341 10344 344 bottles of beer on the wall Option 1 0.526025336 14JAN1960 01JAN1960:10:02:08 0:00:55 11 77
342 10345 345 bottles of beer on the wall Option 1 0.807215352 03JUL1961 01JAN1960:12:49:47 0:00:01 40 7
343 10346 346 bottles of beer on the wall Option 1 0.9305162979 28FEB1960 01JAN1960:09:46:40 0:00:59 56 28
344 10347 347 bottles of beer on the wall Option 1 0.7591318552 18FEB1962 01JAN1960:13:25:32 0:01:10 41 9
345 10348 348 bottles of beer on the wall Option 1 0.4177664911 11SEP1961 01JAN1960:09:55:17 0:01:39 76 82
346 10349 349 bottles of beer on the wall Option 1 0.4690050443 05DEC1961 01JAN1960:11:05:15 0:01:09 63 40
347 10350 350 bottles of beer on the wall Option 1 0.7541399979 31AUG1961 01JAN1960:12:30:45 0:00:33 57 12
348 10351 351 bottles of beer on the wall Option 1 0.1392844325 17MAR1962 01JAN1960:08:20:38 0:00:41 85 45
349 10352 352 bottles of beer on the wall Option 1 0.1020530235 23DEC1961 01JAN1960:09:46:20 0:00:01 55 56
350 10353 353 bottles of beer on the wall Option 1 0.0257998794 04DEC1961 01JAN1960:09:47:10 0:00:31 100 2
351 10354 354 bottles of beer on the wall Option 1 0.1238113316 20MAR1962 01JAN1960:09:15:30 0:00:01 74 11
352 10355 355 bottles of beer on the wall Option 1 0.4214245292 24NOV1960 01JAN1960:04:24:09 0:01:01 79 83
353 10356 356 bottles of beer on the wall Option 1 0.3243381057 12FEB1961 01JAN1960:00:55:59 0:00:50 30 52
354 10357 357 bottles of beer on the wall Option 1 0.9735697345 24NOV1960 01JAN1960:07:10:56 0:01:33 64 2
355 10358 358 bottles of beer on the wall Option 1 0.4796259461 28JAN1961 01JAN1960:11:51:29 0:01:03 19 29
356 10359 359 bottles of beer on the wall Option 1 0.003359966 01SEP1960 01JAN1960:13:24:25 0:00:09 66 60
357 10360 360 bottles of beer on the wall Option 1 0.5773700334 21JAN1960 01JAN1960:10:15:32 0:00:40 9 21
358 10361 361 bottles of beer on the wall Option 1 0.0792848342 25JAN1962 01JAN1960:06:00:35 0:01:25 73 73
359 10362 362 bottles of beer on the wall Option 1 0.9339359365 30MAY1961 01JAN1960:09:08:13 0:00:56 12 56
360 10363 363 bottles of beer on the wall Option 1 0.3517632132 12FEB1961 01JAN1960:12:07:19 0:00:01 74 69
361 10364 364 bottles of beer on the wall Option 1 0.4088954895 17MAR1961 01JAN1960:08:04:51 0:01:01 70 66
362 10365 365 bottles of beer on the wall Option 1 0.1254259623 30DEC1961 01JAN1960:06:47:12 0:00:01 79 43
363 10366 366 bottles of beer on the wall Option 1 0.9190132958 28MAY1961 01JAN1960:06:35:42 0:00:07 19 31
364 10367 367 bottles of beer on the wall Option 1 0.3033860015 17MAY1962 01JAN1960:05:32:03 0:00:32 57 73
365 10368 368 bottles of beer on the wall Option 1 0.1463442846 02SEP1962 01JAN1960:01:24:29 0:00:53 19 64
366 10369 369 bottles of beer on the wall Option 1 0.5516236343 18JUN1962 01JAN1960:10:52:23 0:00:11 51 40
367 10370 370 bottles of beer on the wall Option 1 0.5205378246 19JAN1960 01JAN1960:06:54:14 0:00:58 2 72
368 10371 371 bottles of beer on the wall Option 1 0.9941610768 28MAR1962 01JAN1960:04:55:58 0:00:22 46 65
369 10372 372 bottles of beer on the wall Option 1 0.065678 07MAY1961 01JAN1960:10:17:35 0:00:10 54 100
370 10373 373 bottles of beer on the wall Option 1 0.7222646138 17JUL1961 01JAN1960:01:47:32 0:00:51 26 96
371 10374 374 bottles of beer on the wall Option 1 0.8772228294 23JUL1960 01JAN1960:00:30:51 0:00:40 18 45
372 10375 375 bottles of beer on the wall Option 1 0.3651081847 11DEC1961 01JAN1960:00:46:15 0:00:15 14 90
373 10376 376 bottles of beer on the wall Option 1 0.3800431529 15AUG1960 01JAN1960:05:30:55 0:00:19 13 74
374 10377 377 bottles of beer on the wall Option 1 0.1077003503 26FEB1960 01JAN1960:04:48:40 0:00:08 51 53
375 10378 378 bottles of beer on the wall Option 1 0.7945664035 06MAR1961 01JAN1960:05:36:47 0:00:45 65 39
376 10379 379 bottles of beer on the wall Option 1 0.8754883054 06JUN1960 01JAN1960:06:09:33 0:00:04 3 3
377 10380 380 bottles of beer on the wall Option 1 0.9975561108 10AUG1960 01JAN1960:10:34:33 0:00:28 92 29
378 10381 381 bottles of beer on the wall Option 1 0.9817031599 07JUL1960 01JAN1960:01:40:00 0:00:39 63 45
379 10382 382 bottles of beer on the wall Option 1 0.4427802341 07JAN1962 01JAN1960:01:21:32 0:01:31 7 54
380 10383 383 bottles of beer on the wall Option 1 0.03180474 17JUL1962 01JAN1960:07:15:54 0:01:08 72 60
381 10384 384 bottles of beer on the wall Option 1 0.1031627707 10MAY1962 01JAN1960:02:52:58 0:01:31 6 64
382 10385 385 bottles of beer on the wall Option 1 0.911744344 01MAY1960 01JAN1960:03:51:16 0:01:04 1 54
383 10386 386 bottles of beer on the wall Option 1 0.4912374353 13FEB1961 01JAN1960:07:22:49 0:01:21 70 71
384 10387 387 bottles of beer on the wall Option 1 0.8803869509 04JUL1960 01JAN1960:12:14:05 0:00:18 78 88
385 10388 388 bottles of beer on the wall Option 1 0.0596609544 17DEC1960 01JAN1960:08:29:20 0:00:53 13 84
386 10389 389 bottles of beer on the wall Option 1 0.6625022132 12JAN1961 01JAN1960:11:15:39 0:01:05 10 31
387 10390 390 bottles of beer on the wall Option 1 0.2669677358 05OCT1961 01JAN1960:00:48:03 0:01:33 29 31
388 10391 391 bottles of beer on the wall Option 1 0.8095563454 04DEC1961 01JAN1960:07:54:13 0:01:15 95 2
389 10392 392 bottles of beer on the wall Option 1 0.6406704796 27JAN1961 01JAN1960:03:35:55 0:00:36 92 47
390 10393 393 bottles of beer on the wall Option 1 0.2188341847 02MAR1960 01JAN1960:04:05:21 0:00:11 28 34
391 10394 394 bottles of beer on the wall Option 1 0.7052043424 09JUN1962 01JAN1960:07:00:21 0:01:14 84 19
392 10395 395 bottles of beer on the wall Option 1 0.9843204464 18APR1960 01JAN1960:04:54:31 0:01:29 46 46
393 10396 396 bottles of beer on the wall Option 1 0.329249541 10SEP1961 01JAN1960:13:21:04 0:00:21 70 11
394 10397 397 bottles of beer on the wall Option 1 0.2073628675 13JUL1962 01JAN1960:11:16:26 0:01:23 17 97
395 10398 398 bottles of beer on the wall Option 1 0.7881676665 29JUN1962 01JAN1960:11:36:47 0:00:50 39 72
396 10399 399 bottles of beer on the wall Option 1 0.4347905537 31AUG1962 01JAN1960:02:30:30 0:00:21 86 61
397 10400 400 bottles of beer on the wall Option 1 0.2937454084 05APR1962 01JAN1960:02:44:06 0:00:32 27 81
398 10401 401 bottles of beer on the wall Option 1 0.6659902565 10APR1961 01JAN1960:11:53:59 0:00:01 80 17
399 10402 402 bottles of beer on the wall Option 1 0.7637229686 07APR1962 01JAN1960:05:45:25 0:01:28 11 17
400 10403 403 bottles of beer on the wall Option 1 0.3325480941 19MAR1961 01JAN1960:09:57:05 0:00:27 9 99
401 10404 404 bottles of beer on the wall Option 1 0.580015553 10AUG1960 01JAN1960:06:15:44 0:00:25 27 99
402 10405 405 bottles of beer on the wall Option 1 0.2514696071 08APR1961 01JAN1960:10:37:45 0:00:17 29 59
403 10406 406 bottles of beer on the wall Option 1 0.3792107284 19MAR1962 01JAN1960:04:49:30 0:00:07 44 38
404 10407 407 bottles of beer on the wall Option 1 0.4702913125 13DEC1961 01JAN1960:09:03:31 0:00:38 95 90
405 10408 408 bottles of beer on the wall Option 1 0.4207190659 03FEB1961 01JAN1960:00:37:47 0:00:38 37 65
406 10409 409 bottles of beer on the wall Option 1 0.2485069117 08DEC1961 01JAN1960:07:18:28 0:01:39 91 64
407 10410 410 bottles of beer on the wall Option 1 0.3257893502 27NOV1961 01JAN1960:11:33:02 0:00:54 41 7
408 10411 411 bottles of beer on the wall Option 1 0.0967283533 11AUG1962 01JAN1960:07:23:15 0:00:21 90 1
409 10412 412 bottles of beer on the wall Option 1 0.532676542 01SEP1960 01JAN1960:02:46:29 0:00:45 31 48
410 10413 413 bottles of beer on the wall Option 1 0.2564602151 15JAN1961 01JAN1960:01:09:11 0:01:21 16 89
411 10414 414 bottles of beer on the wall Option 1 0.0865634024 20MAR1962 01JAN1960:12:56:45 0:00:05 75 51
412 10415 415 bottles of beer on the wall Option 1 0.2926342321 07FEB1960 01JAN1960:06:48:55 0:00:36 66 33
413 10416 416 bottles of beer on the wall Option 1 0.0678594029 06OCT1961 01JAN1960:07:14:58 0:01:26 50 16
414 10417 417 bottles of beer on the wall Option 1 0.6376664767 09JUN1960 01JAN1960:05:00:13 0:01:37 7 92
415 10418 418 bottles of beer on the wall Option 1 0.4795483525 14JUN1962 01JAN1960:00:00:50 0:00:50 33 74
416 10419 419 bottles of beer on the wall Option 1 0.3492934342 22MAR1960 01JAN1960:08:54:52 0:01:26 61 72
417 10420 420 bottles of beer on the wall Option 1 0.5085071356 14MAR1960 01JAN1960:09:47:51 0:00:56 36 63
418 10421 421 bottles of beer on the wall Option 1 0.414953564 25MAR1961 01JAN1960:03:35:19 0:00:30 47 30
419 10422 422 bottles of beer on the wall Option 1 0.2431976652 25SEP1960 01JAN1960:08:47:25 0:00:17 6 42
420 10423 423 bottles of beer on the wall Option 1 0.2370444998 04MAR1962 01JAN1960:00:16:44 0:01:36 54 100
421 10424 424 bottles of beer on the wall Option 1 0.8687893324 12OCT1961 01JAN1960:03:16:33 0:00:56 97 25
422 10425 425 bottles of beer on the wall Option 1 0.0470510335 10MAY1960 01JAN1960:02:44:07 0:01:05 83 59
423 10426 426 bottles of beer on the wall Option 1 0.7114342887 12APR1960 01JAN1960:02:17:40 0:00:39 57 97
424 10427 427 bottles of beer on the wall Option 1 0.6176668283 05JUL1962 01JAN1960:09:09:12 0:01:03 92 38
425 10428 428 bottles of beer on the wall Option 1 0.0657907743 01JUL1962 01JAN1960:11:03:37 0:01:35 97 63
426 10429 429 bottles of beer on the wall Option 1 0.280104912 13MAR1962 01JAN1960:10:35:08 0:01:07 42 35
427 10430 430 bottles of beer on the wall Option 1 0.3639827088 02APR1960 01JAN1960:12:00:50 0:00:40 91 84
428 10431 431 bottles of beer on the wall Option 1 0.2130561137 25AUG1961 01JAN1960:12:07:22 0:01:37 70 9
429 10432 432 bottles of beer on the wall Option 1 0.9656705889 23DEC1960 01JAN1960:02:36:48 0:01:28 23 92
430 10433 433 bottles of beer on the wall Option 1 0.5824601052 22JAN1961 01JAN1960:01:19:48 0:00:32 73 6
431 10434 434 bottles of beer on the wall Option 1 0.5207124942 23APR1961 01JAN1960:10:45:16 0:01:08 45 85
432 10435 435 bottles of beer on the wall Option 1 0.9280530661 01NOV1960 01JAN1960:12:46:59 0:01:36 57 4
433 10436 436 bottles of beer on the wall Option 1 0.8982810149 29NOV1961 01JAN1960:09:23:13 0:01:15 100 68
434 10437 437 bottles of beer on the wall Option 1 0.3268259467 04MAR1960 01JAN1960:01:22:42 0:00:04 99 61
435 10438 438 bottles of beer on the wall Option 1 0.2038002704 12FEB1962 01JAN1960:09:03:24 0:00:40 97 78
436 10439 439 bottles of beer on the wall Option 1 0.0798850833 11MAY1960 01JAN1960:00:32:50 0:00:37 7 49
437 10440 440 bottles of beer on the wall Option 1 0.6697000566 01SEP1960 01JAN1960:08:04:03 0:00:30 7 68
438 10441 441 bottles of beer on the wall Option 1 0.550699767 11JAN1960 01JAN1960:06:33:15 0:01:09 19 10
439 10442 442 bottles of beer on the wall Option 1 0.9514032798 07FEB1962 01JAN1960:06:03:16 0:01:13 65 87
440 10443 443 bottles of beer on the wall Option 1 0.1182718324 08FEB1960 01JAN1960:11:28:52 0:00:46 61 59
441 10444 444 bottles of beer on the wall Option 1 0.5772118874 11FEB1962 01JAN1960:01:14:29 0:01:07 16 22
442 10445 445 bottles of beer on the wall Option 1 0.9828714463 06DEC1960 01JAN1960:05:47:08 0:01:40 66 4
443 10446 446 bottles of beer on the wall Option 1 0.1229167269 02JUN1960 01JAN1960:09:43:51 0:01:34 29 24
444 10447 447 bottles of beer on the wall Option 1 0.2102257522 09MAR1961 01JAN1960:04:06:09 0:01:27 53 78
445 10448 448 bottles of beer on the wall Option 1 0.9958884935 20FEB1962 01JAN1960:06:43:53 0:00:07 42 65
446 10449 449 bottles of beer on the wall Option 1 0.3530408085 04MAY1961 01JAN1960:11:26:59 0:01:37 76 35
447 10450 450 bottles of beer on the wall Option 1 0.2848354258 03SEP1960 01JAN1960:13:26:36 0:00:25 69 38
448 10451 451 bottles of beer on the wall Option 1 0.1461860818 10JUN1960 01JAN1960:01:24:25 0:00:02 61 23
449 10452 452 bottles of beer on the wall Option 1 0.2033735766 29AUG1960 01JAN1960:12:30:17 0:00:19 32 75
450 10453 453 bottles of beer on the wall Option 1 0.7973149958 26JUN1961 01JAN1960:04:46:06 0:00:21 94 14
451 10454 454 bottles of beer on the wall Option 1 0.6812086425 07NOV1961 01JAN1960:01:59:51 0:00:12 89 59
452 10455 455 bottles of beer on the wall Option 1 0.6111464974 24FEB1962 01JAN1960:07:42:56 0:00:15 23 92
453 10456 456 bottles of beer on the wall Option 1 0.9542484209 09APR1961 01JAN1960:09:09:17 0:01:26 87 93
454 10457 457 bottles of beer on the wall Option 1 0.9818577077 18AUG1960 01JAN1960:12:24:47 0:01:08 18 20
455 10458 458 bottles of beer on the wall Option 1 0.9331120881 06JUL1961 01JAN1960:10:10:02 0:01:40 78 36
456 10459 459 bottles of beer on the wall Option 1 0.8953045587 22APR1960 01JAN1960:04:47:34 0:00:37 13 46
457 10460 460 bottles of beer on the wall Option 1 0.1584420461 08JAN1961 01JAN1960:07:47:49 0:00:05 85 61
458 10461 461 bottles of beer on the wall Option 1 0.9324573814 24DEC1960 01JAN1960:02:13:36 0:00:22 55 94
459 10462 462 bottles of beer on the wall Option 1 0.0813189871 21AUG1961 01JAN1960:09:18:28 0:00:36 42 49
460 10463 463 bottles of beer on the wall Option 1 0.8736312594 17MAY1962 01JAN1960:12:52:58 0:00:30 45 7
461 10464 464 bottles of beer on the wall Option 1 0.1894013794 06NOV1960 01JAN1960:12:56:13 0:01:30 11 50
462 10465 465 bottles of beer on the wall Option 1 0.9149773772 07JUN1960 01JAN1960:08:53:28 0:00:22 23 17
463 10466 466 bottles of beer on the wall Option 1 0.6329479598 27JUN1962 01JAN1960:07:38:23 0:00:06 68 14
464 10467 467 bottles of beer on the wall Option 1 0.1897066413 21AUG1961 01JAN1960:10:15:28 0:01:12 59 46
465 10468 468 bottles of beer on the wall Option 1 0.8352205767 16MAR1961 01JAN1960:06:48:50 0:00:44 60 37
466 10469 469 bottles of beer on the wall Option 1 0.3339443432 09OCT1960 01JAN1960:00:52:53 0:00:27 70 44
467 10470 470 bottles of beer on the wall Option 1 0.1096137567 09JUL1962 01JAN1960:08:28:10 0:01:15 88 81
468 10471 471 bottles of beer on the wall Option 1 0.1481936733 10JAN1962 01JAN1960:06:38:41 0:00:56 21 77
469 10472 472 bottles of beer on the wall Option 1 0.5630390432 01MAR1960 01JAN1960:06:35:09 0:01:08 70 32
470 10473 473 bottles of beer on the wall Option 1 0.0604085713 12APR1962 01JAN1960:02:41:26 0:00:46 36 17
471 10474 474 bottles of beer on the wall Option 1 0.6011423606 25DEC1961 01JAN1960:02:57:45 0:01:11 36 18
472 10475 475 bottles of beer on the wall Option 1 0.7698335782 31JUL1962 01JAN1960:07:46:29 0:01:34 72 17
473 10476 476 bottles of beer on the wall Option 1 0.3823641224 17MAR1962 01JAN1960:03:34:37 0:00:09 27 86
474 10477 477 bottles of beer on the wall Option 1 0.9225378446 12JAN1962 01JAN1960:08:41:25 0:01:06 45 20
475 10478 478 bottles of beer on the wall Option 1 0.2979750453 23JUN1962 01JAN1960:10:06:36 0:01:28 88 7
476 10479 479 bottles of beer on the wall Option 1 0.4988665942 15JUL1961 01JAN1960:02:33:06 0:01:33 85 1
477 10480 480 bottles of beer on the wall Option 1 0.5243853585 29JUL1960 01JAN1960:00:26:18 0:00:31 48 77
478 10481 481 bottles of beer on the wall Option 1 0.6049248826 12JUL1962 01JAN1960:10:01:41 0:00:16 66 45
479 10482 482 bottles of beer on the wall Option 1 0.6312438024 01OCT1961 01JAN1960:05:18:12 0:00:04 43 27
480 10483 483 bottles of beer on the wall Option 1 0.3366865974 17MAY1962 01JAN1960:01:44:17 0:00:01 31 25
481 10484 484 bottles of beer on the wall Option 1 0.0813266188 08AUG1962 01JAN1960:06:18:27 0:00:08 71 63
482 10485 485 bottles of beer on the wall Option 1 0.3601163455 23JAN1962 01JAN1960:11:26:01 0:00:37 41 88
483 10486 486 bottles of beer on the wall Option 1 0.9033777345 13AUG1961 01JAN1960:00:16:14 0:00:56 82 33
484 10487 487 bottles of beer on the wall Option 1 0.1716986667 23DEC1960 01JAN1960:12:06:34 0:01:13 2 32
485 10488 488 bottles of beer on the wall Option 1 0.9734818912 11SEP1961 01JAN1960:00:34:41 0:00:28 17 7
486 10489 489 bottles of beer on the wall Option 1 0.0618140223 17DEC1961 01JAN1960:06:26:30 0:00:38 94 40
487 10490 490 bottles of beer on the wall Option 1 0.0204490144 22AUG1960 01JAN1960:01:50:18 0:00:19 40 56
488 10491 491 bottles of beer on the wall Option 1 0.8719323929 23MAY1960 01JAN1960:12:36:06 0:00:08 49 44
489 10492 492 bottles of beer on the wall Option 1 0.2656292181 28JUN1962 01JAN1960:05:32:50 0:01:35 15 39
490 10493 493 bottles of beer on the wall Option 1 0.37794835 23JUL1962 01JAN1960:13:15:43 0:00:56 6 86
491 10494 494 bottles of beer on the wall Option 1 0.6395865733 17JAN1961 01JAN1960:12:20:12 0:01:31 22 87
492 10495 495 bottles of beer on the wall Option 1 0.4408595098 05JUL1960 01JAN1960:12:21:53 0:01:40 25 27
493 10496 496 bottles of beer on the wall Option 1 0.4846261169 02DEC1961 01JAN1960:12:41:08 0:00:06 7 63
494 10497 497 bottles of beer on the wall Option 1 0.7903671478 14MAY1962 01JAN1960:05:09:21 0:01:03 98 25
495 10498 498 bottles of beer on the wall Option 1 0.60474184 13OCT1961 01JAN1960:03:52:59 0:01:22 98 98
496 10499 499 bottles of beer on the wall Option 1 0.1213259218 23APR1962 01JAN1960:06:29:47 0:01:35 38 70
497 10500 500 bottles of beer on the wall Option 1 0.7882370487 17FEB1962 01JAN1960:02:00:25 0:00:12 1 74

View File

@ -6,5 +6,8 @@
"lib": ["es2019", "dom"], "lib": ["es2019", "dom"],
"types": ["cypress", "cypress-real-events"] "types": ["cypress", "cypress-real-events"]
}, },
"include": ["**/*.ts"] "include": [
"**/*.ts",
"../cypress.config.ts"
]
} }

Binary file not shown.

Binary file not shown.

View File

@ -10,7 +10,7 @@ const check = (cwd) => {
onlyAllow: onlyAllow:
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;', 'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
excludePackages: excludePackages:
'@cds/city@1.1.0;@handsontable/angular@14.4.0;handsontable@14.4.0;hyperformula@2.7.0;jackspeak@2.2.0;path-scurry@1.7.0' '@cds/city@1.1.0;@handsontable/angular@15.3.0;handsontable@15.3.0;hyperformula@2.7.1;hyperformula@3.0.0;jackspeak@3.4.3;path-scurry@1.11.1;package-json-from-dist@1.0.1'
}, },
(error, json) => { (error, json) => {
if (error) { if (error) {

11050
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -44,23 +44,23 @@
"@angular/platform-browser": "^17.3.3", "@angular/platform-browser": "^17.3.3",
"@angular/platform-browser-dynamic": "^17.3.3", "@angular/platform-browser-dynamic": "^17.3.3",
"@angular/router": "^17.3.3", "@angular/router": "^17.3.3",
"@cds/core": "^6.10.0", "@cds/core": "^6.15.1",
"@clr/angular": "^17.0.1", "@clr/angular": "file:libraries/clr-angular-17.9.0.tgz",
"@clr/icons": "^13.0.2", "@clr/icons": "^13.0.2",
"@clr/ui": "^17.0.1", "@clr/ui": "file:libraries/clr-ui-17.9.0.tgz",
"@handsontable/angular": "^14.3.0", "@handsontable/angular": "^15.3.0",
"@sasjs/adapter": "4.10.2", "@sasjs/adapter": "^4.11.0",
"@sasjs/utils": "^3.4.0", "@sasjs/utils": "^3.4.0",
"@sheet/crypto": "file:libraries/sheet-crypto.tgz", "@sheet/crypto": "file:libraries/sheet-crypto.tgz",
"@types/d3-graphviz": "^2.6.7", "@types/d3-graphviz": "^2.6.7",
"@types/text-encoding": "0.0.35", "@types/text-encoding": "0.0.35",
"base64-arraybuffer": "^0.2.0", "base64-arraybuffer": "^0.2.0",
"buffer": "^5.4.3", "buffer": "^5.4.3",
"crypto-browserify": "3.12.0", "crypto-browserify": "^3.12.1",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"d3-graphviz": "^5.0.2", "d3-graphviz": "^5.0.2",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",
"handsontable": "^14.3.0", "handsontable": "^15.3.0",
"https-browserify": "1.0.0", "https-browserify": "1.0.0",
"hyperformula": "^2.5.0", "hyperformula": "^2.5.0",
"iconv-lite": "^0.5.0", "iconv-lite": "^0.5.0",
@ -80,6 +80,7 @@
"tslib": "^2.3.0", "tslib": "^2.3.0",
"vm": "^0.1.0", "vm": "^0.1.0",
"webpack": "^5.91.0", "webpack": "^5.91.0",
"xlsx": "^0.18.5",
"zone.js": "~0.14.4" "zone.js": "~0.14.4"
}, },
"devDependencies": { "devDependencies": {

View File

@ -148,5 +148,8 @@ export const globals: {
}, },
handsontable: { handsontable: {
darkTableHeaderClass: 'darkTH' darkTableHeaderClass: 'darkTH'
},
userDropdownConfig: {
closeOnDebugClick: false
} }
} }

View File

@ -139,10 +139,15 @@
[routerLink]="['/']" [routerLink]="['/']"
class="nav-link" class="nav-link"
> >
<img class="without-text d-block d-md-none" src="images/dc-logo.svg" /> <img
class="without-text d-block d-md-none"
src="images/dc-logo.svg"
alt="datacontroller logo without text"
/>
<img <img
class="with-text d-none d-md-block" class="with-text d-none d-md-block"
src="images/datacontroller.svg" src="images/datacontroller.svg"
alt="datacontroller logo"
/> />
</a> </a>
@ -283,7 +288,11 @@
<!-- App Loading Page --> <!-- App Loading Page -->
<div *ngIf="!startupDataLoaded" class="app-loading"> <div *ngIf="!startupDataLoaded" class="app-loading">
<img class="loading-logo" src="images/datacontroller.svg" /> <img
class="loading-logo"
src="images/datacontroller.svg"
alt="datacontroller logo"
/>
<div *ngIf="appActive === null" class="slider"> <div *ngIf="appActive === null" class="slider">
<div class="line"></div> <div class="line"></div>

View File

@ -1,447 +0,0 @@
@import '../colors.scss';
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
// This software is released under MIT license.
// The full license information can be found in LICENSE in the root directory of this project.
app-requests-modal {
z-index: 10000;
}
header.app-header {
background: $headerBackground !important;
color: #fff;
}
.logo img.without-text {
width: 30px;
}
.logo img.with-text {
width: 210px;
}
.header-hamburger-trigger {
display: block;
background: transparent;
border: 0;
margin-left: 10px;
}
.demo-expired-notice {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
top: 0;
height: 100vh !important;
width: 100vw !important;
z-index: 105;
background: rgba(33, 33, 33, .5);
.expired-details {
flex-direction: column;
align-items: center;
padding: 30px;
z-index: 110;
background: $headerBackground;
.expired-notice {
color: #e0e0e0;
font-size: 16px;
.mailto {
color: #8dc53e;
}
}
}
}
.main-container .update-key {
display: flex;
align-items: center;
color: white;
padding: 0px 10px;
background: #00000026;
}
.alert-icon-wrapper {
margin-top: 0 !important;
}
.nav-text {
margin-right: 20px;
}
.sidebar-toggle {
display: flex;
height: 100%;
align-items: center;
padding-left: 10px;
clr-icon {
cursor: pointer;
width: 30px;
height: 30px;
}
}
header {
.header-actions {
.dropdown {
position: unset; //without it, when opening user dropdown scrollbar was displaying without reason
}
}
.nav-link:hover {
color: #fafafa;
}
.nav-link.active {
background: #61717D;
}
}
.notf {
background: #16a57a;
color: #fffcfc;
font-size: 12px;
}
.toggle-switch input[type=checkbox]:checked+label:before {
border-color: #61717D;
background-color: #61717D;
transition: .15s ease-in;
transition-property: border-color,background-color;
}
.main-container {
min-height: 100vh !important;
}
.main-container .content-container .content-area {
padding: 0rem 1rem 1rem 1rem;
}
.content-container {
z-index: 0!important;
}
.navBarResp {
display: flex;
justify-content: center;
background: #495A67;
color: #fff;
}
::ng-deep {
.htInvalid {
background: black!important;
}
@media screen and (max-width:480px) {
h2 {
font-size: .7rem!important;
}
h3 {
font-size: .7rem;
}
}
.nav-link {
padding: 0rem 1rem 0rem 1rem;
}
body[cds-theme="light"] {
.btn-primary .btn, .btn.btn-primary {
border-color: $headerBackground;
background-color: $headerBackground;
color: #fff;
}
}
body[cds-theme="dark"] {
.btn-primary .btn, .btn.btn-primary {
border-color: #5e7382;
background-color: #5e7382;
color: #fff;
clr-icon, cds-icon {
color: #fff
}
}
}
.btn-primary .btn, .btn.btn-primary {
&:disabled {
opacity: 0.65;
}
}
.btn {
cursor: pointer;
display: inline-block;
-webkit-appearance: none!important;
border-radius: .125rem;
border: 1px solid;
min-width: 3rem;
max-width: 15rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
text-align: center;
text-transform: uppercase;
vertical-align: middle;
line-height: 1.5rem;
letter-spacing: .12em;
font-size: .5rem;
font-weight: 500;
height: 1.5rem;
padding: 0 .5rem;
}
.btn.btn-outline:hover {
border-color: $headerBackground;
background-color: #495A67;
color: #fff;
}
body[cds-theme="dark"] {
.btn.btn-icon.btn-dimmed {
color: #7295ae;
}
}
body[cds-theme="light"] {
.btn.btn-icon.btn-dimmed {
color: $headerBackground;
}
.btn.btn-outline {
border-color: $headerBackground;
background-color: transparent;
color: $headerBackground;
}
}
.htMobileEditorContainer .inputs textarea {
font-size: 13pt;
border: 2px solid #485967;
border-radius: 4px;
-webkit-appearance: none;
box-shadow: none;
position: absolute;
left: 14px;
right: 0px;
top: 0;
bottom: 0;
padding: 7pt;
width: 290px;
}
.htMobileEditorContainer .positionControls {
width: 333px;
position: absolute;
right: 5pt;
top: 50px;
bottom: 0;
display: flex;
justify-content: center;
}
.htMobileEditorContainer.active {
display: block;
height: 120px;
width: 350px;
}
/* Left and right */
/* Column headers */
body[cds-theme="light"] {
.wtBorder {
background-color: #495A67!important;
}
.ht_master tr:nth-of-type(odd) > td {
filter: brightness(0.95);
}
}
$darkBorderColor: #697c85;
body[cds-theme="dark"] {
.ht_master tr:nth-of-type(odd) > td {
filter: brightness(1.2);
}
.ht_master:not(.emptyColumns) ~ .handsontable tbody tr th, .ht_master:not(.emptyColumns) ~ .handsontable:not(.ht_clone_top) thead tr th:first-child {
background-color: #2d4048;
border-color: $darkBorderColor;
}
.handsontable td {
// border-right: 1px solid #697c85;
// border-bottom: 1px solid #697c85;
border-color: $darkBorderColor;
}
.handsontable tr:first-child th, .handsontable tr:first-child td {
border-color: $darkBorderColor;
}
.handsontable .handsontable.ht_clone_top .wtHider {
border-color: $darkBorderColor;
}
.handsontable .changeType {
background-color: #3c5662;
border-color: $darkBorderColor;
}
.handsontableInput {
background-color: #708b98;
}
}
.handsontable .handsontable.ht_clone_top .wtHider {
padding: 0 0 0px 0!important;
margin: 0px;
border-bottom: 3px solid #d6d3d3;
}
body[cds-theme="light"] {
.content-container {
// background: red;
background: #F5F6FF;
}
}
.datagrid-compact, .datagrid-history{
.datagrid {
border-collapse: separate;
border: 1px solid transparent;
border-radius: .125rem;
margin: 0;
margin-top: 1rem;
max-width: 100%;
width: 100%;
padding: 15px 15px 50px 15px;
}
.datagrid-foot {
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
height: 1.5rem;
padding: 0 .5rem;
line-height: calc(1.5rem - 3px);
font-size: .45833rem;
background-color: #fff;
border-top: 1px solid #ccc;
border-radius: 0px;
// border-radius: 0 0 .125rem .125rem;
}
.datagrid-footer {
position: absolute;
right: 30px;
top: 1px;
}
.datagrid .datagrid-head {
background-color: #fff;
border-bottom: 1px solid #ccc;
}
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
margin-top: .083333rem;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
padding: .5rem 0;
border: 1px solid #ccc;
box-shadow: 0 1px 0.125rem hsla(0,0%,45%,.25);
min-width: 5rem;
max-width: 15rem;
border-radius: .125rem;
visibility: hidden;
z-index: 1000;
}
.table {
border-collapse: separate;
border: 1px solid transparent;
border-radius: 0px;
margin: 0;
margin-top: 1rem;
max-width: 100%;
width: 100%;
}
.table th {
font-size: .45833rem;
font-weight: 600;
letter-spacing: .03em;
vertical-align: bottom;
border-bottom: 1px solid #ccc;
text-transform: uppercase;
}
.modal-header {
border-bottom: 2px solid #e4e4e4;
padding: 0 0 .5rem 0;
margin-bottom: 1rem;
}
.main-container .content-container {
min-height: 0px;
position: relative;
}
}
.app-loading {
.loading-logo {
max-width: 400px;
width: 100%;
}
}
@media screen and (max-width: 768px) {
.navBarResp {
display: flex;
justify-content: flex-start;
background: #495A67;
color: #fff;
}
.main-container .sub-nav.clr-nav-level-1 .nav .nav-link, .main-container .sub-nav.clr-nav-level-2 .nav .nav-link, .main-container .subnav.clr-nav-level-1 .nav .nav-link, .main-container .subnav.clr-nav-level-2 .nav .nav-link {
padding: 0 .5rem 0 1rem;
width: 100%;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
border-radius: .125rem 0 0 .125rem;
color: #95c84b;
}
.card-block, .card-footer {
padding: 10px 0px 0px 0px;
}
.main-container[_ngcontent-c0] .content-container[_ngcontent-c0] .content-area[_ngcontent-c0] {
padding: 0rem 0rem 0rem 0rem;
}
}

View File

@ -1,4 +1,9 @@
import { ChangeDetectorRef, Component, ElementRef } from '@angular/core' import {
ChangeDetectorRef,
Component,
ElementRef,
ViewEncapsulation
} from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { VERSION } from '../environments/version' import { VERSION } from '../environments/version'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
@ -18,6 +23,7 @@ import {
ClarityIcons, ClarityIcons,
exclamationTriangleIcon, exclamationTriangleIcon,
moonIcon, moonIcon,
processOnVmIcon,
sunIcon, sunIcon,
tableIcon, tableIcon,
trashIcon trashIcon
@ -28,13 +34,15 @@ ClarityIcons.addIcons(
sunIcon, sunIcon,
exclamationTriangleIcon, exclamationTriangleIcon,
tableIcon, tableIcon,
trashIcon trashIcon,
processOnVmIcon
) )
@Component({ @Component({
selector: 'my-app', selector: 'my-app',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class AppComponent { export class AppComponent {
private dcAdapterSettings: DcAdapterSettings | undefined private dcAdapterSettings: DcAdapterSettings | undefined

View File

@ -5,7 +5,7 @@
<div class="card-header">Terms and Conditions</div> <div class="card-header">Terms and Conditions</div>
<div class="card-block"> <div class="card-block">
<div class="card-text"> <div class="card-text">
<p> <p class="mt-0">
The Demo version of Data Controller is free for EVALUATION purposes The Demo version of Data Controller is free for EVALUATION purposes
only. Before proceeding with configuration, please confirm that you only. Before proceeding with configuration, please confirm that you
have read, understood, and agreed to the have read, understood, and agreed to the

View File

@ -1,50 +0,0 @@
.card {
margin-top: 0;
}
.btn {
margin-top: 10px;
}
.log-wrapper {
width: 100%;
background: #f0f0f0;
border: 1px solid #c9c9c9;
padding: 10px;
overflow: auto;
white-space: pre-wrap;
}
#contexts-btn {
padding: 0;
min-width: 30px;
margin-left: 10px;
height: 30px;
display: inline-flex;
justify-content: center;
align-items: center;
padding-top: 3px;
}
.validation-bar {
display: flex;
margin-top: 20px;
align-items: center;
clr-icon {
margin-right: 5px;
}
}
.autodeploy-section {
padding: 0px 15px;
.clr-checkbox-wrapper {
margin: 20px 0 20px 0;
}
.btn-autodeploy {
display: block;
margin: 15px 0 15px 0;
}
}

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { SasService } from '../services/sas.service' import { SasService } from '../services/sas.service'
import { SASjsConfig } from '@sasjs/adapter' import { SASjsConfig } from '@sasjs/adapter'
import { Router } from '@angular/router' import { Router } from '@angular/router'
@ -13,7 +13,8 @@ import { DcAdapterSettings } from '../models/DcAdapterSettings'
styleUrls: ['./deploy.component.scss'], styleUrls: ['./deploy.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class DeployComponent implements OnInit { export class DeployComponent implements OnInit {
public step: number = 0 public step: number = 0
@ -56,25 +57,6 @@ export class DeployComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
if (this.sasJsConfig.serverType === ServerType.SasViya) {
fetch('sasbuild/viya.json')
.then((res) => res.text())
.then((res) => {
let initJsonFile: any = null
try {
initJsonFile = JSON.parse(res)
} catch (err) {
console.error(err)
}
if (initJsonFile) {
this.jsonFile = initJsonFile
this.loggerService.log(this.jsonFile)
}
})
}
this.setDeployDefaults() this.setDeployDefaults()
} }

View File

@ -9,14 +9,17 @@
<p class="m-0 align-self-start">Done</p> <p class="m-0 align-self-start">Done</p>
<hr class="w-100" /> <hr class="w-100" />
<div class="deploy-status-row"> <div
*ngIf="autoDeployStatus.deployServicePack !== null"
class="deploy-status-row"
>
<clr-icon <clr-icon
*ngIf="autoDeployStatus.deployServicePack" *ngIf="autoDeployStatus.deployServicePack === true"
class="deploy-success" class="deploy-success"
shape="success-standard" shape="success-standard"
></clr-icon> ></clr-icon>
<clr-icon <clr-icon
*ngIf="!autoDeployStatus.deployServicePack" *ngIf="!autoDeployStatus.deployServicePack === false"
class="deploy-error" class="deploy-error"
shape="times-circle" shape="times-circle"
></clr-icon> ></clr-icon>
@ -52,7 +55,7 @@
class="deploy-error" class="deploy-error"
shape="times-circle" shape="times-circle"
></clr-icon> ></clr-icon>
LAUNCH / CONFIGURE LAUNCH
</button> </button>
<button <button
@ -94,20 +97,72 @@
</div> </div>
<label for="dcloc" class="mt-20 clr-control-label">DC Loc</label> <label for="dcloc" class="mt-20 clr-control-label">DC Loc</label>
<div class="mb-10 clr-control-container"> <div class="mb-10 clr-control-container dc-loc-input-wrapper">
<div class="clr-input-wrapper"> <div class="clr-input-wrapper small-mt">
<p class="mt-0">{{ dcPath }}</p> <input clrInput name="dcloc" [(ngModel)]="dcPath" />
</div> </div>
</div> </div>
<label for="dcloc" class="mt-20 clr-control-label">SAS Admin group</label> <label for="dcloc" class="mt-20 clr-control-label">SAS Admin group</label>
<div class="mb-10 clr-control-container"> <div class="mb-10 clr-control-container">
<div class="clr-input-wrapper"> <div class="clr-input-wrapper small-mt">
<p class="mt-0">{{ selectedAdminGroup }}</p> <select
*ngIf="!adminGroupsLoading"
clrSelect
name="options"
[(ngModel)]="selectedAdminGroup"
>
<option *ngFor="let adminGroup of adminGroups" [value]="adminGroup.id">
{{ adminGroup.name }}
</option>
</select>
<clr-spinner
clrInline
class="spinner-sm"
*ngIf="adminGroupsLoading"
></clr-spinner>
</div> </div>
</div> </div>
<clr-checkbox-wrapper> <label for="computeContext" class="mt-20 clr-control-label"
>Compute Context</label
>
<div class="mb-10 clr-control-container">
<div class="clr-input-wrapper small-mt">
<select
*ngIf="!computeContextsLoading"
clrSelect
name="options"
(ngModelChange)="onComputeContextChange($event)"
[(ngModel)]="selectedComputeContext"
>
<option
*ngFor="let computeContext of computeContexts"
[value]="computeContext.id"
>
{{ computeContext.name }}
</option>
</select>
<clr-spinner
clrInline
class="spinner-sm"
*ngIf="computeContextsLoading"
></clr-spinner>
</div>
</div>
<ng-container *ngIf="runningAsUser">
<label for="dcloc" class="mt-20 clr-control-label">Running as user:</label>
<div class="mb-10 clr-control-container">
<div class="clr-input-wrapper">
<p class="mt-0">{{ runningAsUser }}</p>
</div>
</div>
</ng-container>
<!-- Keeping this for a reference in case future VIYA changes and starts allowing separate backend and frontend) -->
<!-- <clr-checkbox-wrapper>
<input <input
clrCheckbox clrCheckbox
[(ngModel)]="recreateDatabase" [(ngModel)]="recreateDatabase"
@ -116,19 +171,28 @@
checked checked
/> />
<label>Recreate database</label> <label>Recreate database</label>
</clr-checkbox-wrapper> </clr-checkbox-wrapper> -->
<hr /> <hr />
<button <button
(click)="runAutoDeploy()"
class="btn-autodeploy btn btn-primary d-inline-block mr-10"
>
Deploy
</button>
<!-- Keeping this for a reference in case future VIYA changes and starts allowing separate backend and frontend) -->
<!-- <button
(click)="executeJson()" (click)="executeJson()"
class="btn-autodeploy btn btn-primary d-inline-block mr-10" class="btn-autodeploy btn btn-primary d-inline-block mr-10"
[disabled]="!jsonFile" [disabled]="!jsonFile"
> >
Deploy {{ !jsonFile ? '(json file is not available)' : '' }} Deploy {{ !jsonFile ? '(json file is not available)' : '' }}
</button> </button> -->
<button <!-- <button
(click)="uploadJsonAuto.click()" (click)="uploadJsonAuto.click()"
class="btn-autodeploy btn btn-primary d-inline-block mr-10" class="btn-autodeploy btn btn-primary d-inline-block mr-10"
> >
@ -140,7 +204,7 @@
hidden hidden
(click)="clearUploadInput($event)" (click)="clearUploadInput($event)"
(change)="onJsonFileChange($event)" (change)="onJsonFileChange($event)"
/> /> -->
<clr-modal [(clrModalOpen)]="recreateDatabaseModal" [clrModalClosable]="false"> <clr-modal [(clrModalOpen)]="recreateDatabaseModal" [clrModalClosable]="false">
<h3 class="modal-title">Warning</h3> <h3 class="modal-title">Warning</h3>

View File

@ -1,61 +0,0 @@
.auto-deploy {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 100;
}
.spinner-box {
width: 400px;
padding: 20px;
border-radius: 3px;
background: #fff;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
box-shadow: 1px 1px 8px 0px #00000082;
.buttons {
display: flex;
justify-content: space-between;
width: 100%;
}
}
.deploy-status-row {
display: flex;
align-items: center;
align-self: flex-start;
p {
margin: 0 0 0 10px;
}
}
.deploy-success {
color: #6ECF44;
}
.deploy-error {
color: #E74C3C;
// width: 20px;
// height: 20px;
}
.deploy-undeterminated {
color: #cacaca;
}
hr {
border: 0;
border-bottom: 1px solid #00000045;
}

View File

@ -1,15 +1,34 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core'
import SASjs, { SASjsConfig } from '@sasjs/adapter' import SASjs, { SASjsConfig } from '@sasjs/adapter'
import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings' import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings'
import { DeployService } from 'src/app/services/deploy.service' import { DeployService } from 'src/app/services/deploy.service'
import { EventService } from 'src/app/services/event.service' import { EventService } from 'src/app/services/event.service'
import { LoggerService } from 'src/app/services/logger.service' import { LoggerService } from 'src/app/services/logger.service'
import { SasViyaService } from 'src/app/services/sas-viya.service'
import { SasService } from 'src/app/services/sas.service' import { SasService } from 'src/app/services/sas.service'
import { ViyaApiCurrentUser } from 'src/app/viya-api-explorer/models/viya-api-current-user.model'
import {
Item,
ViyaApiIdentities
} from 'src/app/viya-api-explorer/models/viya-api-identities.model'
import { ComputeContextDetails } from 'src/app/viya-api-explorer/models/viya-compute-context-details.model'
import {
ViyaComputeContexts,
Item as ComputeContextItem
} from 'src/app/viya-api-explorer/models/viya-compute-contexts.model'
@Component({ @Component({
selector: 'app-automatic-deploy', selector: 'app-automatic-deploy',
templateUrl: './automatic.component.html', templateUrl: './automatic.component.html',
styleUrls: ['./automatic.component.scss'] styleUrls: ['./automatic.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class AutomaticComponent implements OnInit { export class AutomaticComponent implements OnInit {
@Input() sasJs!: SASjs @Input() sasJs!: SASjs
@ -21,6 +40,7 @@ export class AutomaticComponent implements OnInit {
@Output() onNavigateToHome: EventEmitter<any> = new EventEmitter<any>() @Output() onNavigateToHome: EventEmitter<any> = new EventEmitter<any>()
public selectedComputeContext: string = ''
public makeDataResponse: string = '' public makeDataResponse: string = ''
public jsonFile: any = null public jsonFile: any = null
public autodeploying: boolean = false public autodeploying: boolean = false
@ -28,8 +48,19 @@ export class AutomaticComponent implements OnInit {
public recreateDatabaseModal: boolean = false public recreateDatabaseModal: boolean = false
public isSubmittingJson: boolean = false public isSubmittingJson: boolean = false
public isJsonSubmitted: boolean = false public isJsonSubmitted: boolean = false
public recreateDatabase: boolean = false /**
* Default was `false` when deploy was done with frontend and backend separately.
* Now we are using only streaming app, so we always want to recreate database (makedata)
*/
public recreateDatabase: boolean = true
public createDatabaseLoading: boolean = false public createDatabaseLoading: boolean = false
public adminGroupsLoading: boolean = false
public currentUserInfoLoading: boolean = false
public computeContextsLoading: boolean = false
public adminGroups: { id: string; name: string }[] = []
public runningAsUser: string | undefined
public currentUserInfo: ViyaApiCurrentUser | null = null
public computeContexts: ComputeContextItem[] = []
/** autoDeployStatus /** autoDeployStatus
* This object presents the status for two steps that we have for deploy. * This object presents the status for two steps that we have for deploy.
@ -50,10 +81,114 @@ export class AutomaticComponent implements OnInit {
private eventService: EventService, private eventService: EventService,
private deployService: DeployService, private deployService: DeployService,
private sasService: SasService, private sasService: SasService,
private sasViyaService: SasViyaService,
private loggerService: LoggerService private loggerService: LoggerService
) {} ) {}
ngOnInit(): void {} ngOnInit(): void {
const promiseGetAadminGroups = this.getAdminGroups()
const getCurrentUser = this.getCurrentUser()
const getComputeContexts = this.getComputeContexts()
Promise.all([
promiseGetAadminGroups,
getCurrentUser,
getComputeContexts
]).then(() => {
if (this.selectedComputeContext) {
this.onComputeContextChange(this.selectedComputeContext)
}
})
}
public async getComputeContexts() {
return new Promise<void>((resolve, reject) => {
this.computeContextsLoading = true
this.sasViyaService.getComputeContexts().subscribe(
(res: ViyaComputeContexts) => {
this.computeContextsLoading = false
const defaultContext = res.items.find(
(item: ComputeContextItem) =>
item.name === 'SAS Job Execution compute context'
)
if (defaultContext) {
this.selectedComputeContext = defaultContext.id
}
this.computeContexts = res.items
resolve()
},
(err) => {
reject(err)
}
)
})
}
public async getCurrentUser() {
return new Promise<void>((resolve, reject) => {
this.currentUserInfoLoading = true
this.sasViyaService.getCurrentUser().subscribe(
(res: ViyaApiCurrentUser) => {
this.currentUserInfoLoading = false
this.currentUserInfo = res
this.dcPath = `/export/viya/homes/${res.id}`
resolve()
},
(err) => {
reject(err)
}
)
})
}
public async getAdminGroups() {
return new Promise<void>((resolve, reject) => {
this.adminGroupsLoading = true
this.sasViyaService
.getAdminGroups()
.subscribe((res: ViyaApiIdentities) => {
this.adminGroupsLoading = false
// Map admin groups with only needed fields
this.adminGroups = res.items.map((item: Item) => {
return {
id: item.id,
name: item.name
}
})
resolve()
}),
(err: any) => {
this.adminGroupsLoading = false
this.loggerService.error('Error while getting admin groups', err)
this.eventService.showAbortModal('admin groups', err)
reject(err)
}
})
}
public async onComputeContextChange(computeContextId: string) {
this.sasViyaService
.getComputeContextById(computeContextId)
.subscribe((res: ComputeContextDetails) => {
if (res.attributes && res.attributes.runServerAs) {
this.runningAsUser = res.attributes.runServerAs
} else {
this.runningAsUser = this.currentUserInfo?.id || 'unknown'
}
})
}
/** /**
* Executes sas.json file to deploy the backend * Executes sas.json file to deploy the backend
@ -63,7 +198,6 @@ export class AutomaticComponent implements OnInit {
* to create database if checkbox is toggled on * to create database if checkbox is toggled on
*/ */
public async executeJson() { public async executeJson() {
this.autodeploying = true
this.isSubmittingJson = true this.isSubmittingJson = true
try { try {
@ -98,6 +232,14 @@ export class AutomaticComponent implements OnInit {
} }
this.isSubmittingJson = false this.isSubmittingJson = false
}
public async runAutoDeploy(executeJson: boolean = false) {
this.autodeploying = true
if (executeJson) {
this.executeJson()
}
if (this.recreateDatabase) { if (this.recreateDatabase) {
this.createDatabase() this.createDatabase()
@ -119,21 +261,40 @@ export class AutomaticComponent implements OnInit {
] ]
} }
// Get and run service using the selected context name
let selectedComputeContextName = this.sasJsConfig.contextName
if (this.selectedComputeContext.length && this.computeContexts.length) {
const computeContext = this.computeContexts.find(
(context: ComputeContextItem) =>
context.id === this.selectedComputeContext
)
if (computeContext) {
selectedComputeContextName = computeContext.name
}
}
/** /**
* We are overriding default `sasjsConfig` object fields with this object fields. * We are overriding default `sasjsConfig` object fields with this object fields.
* Here we want to run this request using original WEB method. * Here we want to run this request using original WEB method.
* contextName: null is the MUST field for it. * contextName: null is the MUST field for it.
*/ */
let overrideConfig = { let overrideConfig = {
useComputeApi: false, useComputeApi: null,
contextName: this.sasJsConfig.contextName, contextName: selectedComputeContextName,
debug: true debug: true
} }
this.sasJs this.sasJs
.request(`services/admin/makedata`, data, overrideConfig, () => { .request(
this.sasService.shouldLogin.next(true) `services/admin/makedata&_contextname=${selectedComputeContextName}`,
}) data,
overrideConfig,
() => {
this.sasService.shouldLogin.next(true)
}
)
.then((res: any) => { .then((res: any) => {
this.autodeployDone = true this.autodeployDone = true
@ -148,8 +309,21 @@ export class AutomaticComponent implements OnInit {
} else { } else {
this.autoDeployStatus.runMakeData = false this.autoDeployStatus.runMakeData = false
} }
if (typeof res.sasjsAbort !== 'undefined') {
const abortRes = res
const abortMsg = abortRes.sasjsAbort[0].MSG
const macMsg = abortRes.sasjsAbort[0].MAC
this.eventService.showAbortModal('makedata', abortMsg, {
SYSWARNINGTEXT: abortRes.SYSWARNINGTEXT,
SYSERRORTEXT: abortRes.SYSERRORTEXT,
MAC: macMsg
})
}
}) })
.catch((err: any) => { .catch((err: any) => {
this.eventService.showAbortModal('makedata', JSON.stringify(err))
this.autoDeployStatus.runMakeData = false this.autoDeployStatus.runMakeData = false
this.autodeployDone = true this.autodeployDone = true

View File

@ -1,4 +0,0 @@
.clear-memory-button {
right: 10px;
top: 2px;
}

View File

@ -1,6 +1,14 @@
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core' import {
Component,
Input,
OnInit,
Output,
EventEmitter,
ViewEncapsulation
} from '@angular/core'
import SASjs, { SASjsConfig } from '@sasjs/adapter' import SASjs, { SASjsConfig } from '@sasjs/adapter'
import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings' import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings'
import { RequestWrapperResponse } from 'src/app/models/request-wrapper/RequestWrapperResponse'
import { DeployService } from 'src/app/services/deploy.service' import { DeployService } from 'src/app/services/deploy.service'
import { EventService } from 'src/app/services/event.service' import { EventService } from 'src/app/services/event.service'
import { LoggerService } from 'src/app/services/logger.service' import { LoggerService } from 'src/app/services/logger.service'
@ -9,7 +17,8 @@ import { SasService } from 'src/app/services/sas.service'
@Component({ @Component({
selector: 'app-manual-deploy', selector: 'app-manual-deploy',
templateUrl: './manual.component.html', templateUrl: './manual.component.html',
styleUrls: ['./manual.component.scss'] styleUrls: ['./manual.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class ManualComponent implements OnInit { export class ManualComponent implements OnInit {
@Input() sasJs!: SASjs @Input() sasJs!: SASjs
@ -265,7 +274,7 @@ export class ManualComponent implements OnInit {
* contextName: null is the MUST field for it. * contextName: null is the MUST field for it.
*/ */
let overrideConfig = { let overrideConfig = {
useComputeApi: false, useComputeApi: null,
contextName: this.sasJsConfig.contextName, contextName: this.sasJsConfig.contextName,
debug: true debug: true
} }
@ -303,10 +312,10 @@ export class ManualComponent implements OnInit {
this.sasService this.sasService
.request('public/startupservice', null) .request('public/startupservice', null)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
this.loggerService.log(res) this.loggerService.log(res.adapterResponse)
if (res.saslibs) { if (res.adapterResponse.saslibs) {
this.validationState = 'success' this.validationState = 'success'
} else { } else {
this.validationState = 'error' this.validationState = 'error'

View File

@ -10,11 +10,13 @@
</p> </p>
<p class="m-0 mt-10"> <p class="m-0 mt-10">
Please specify a physical directory below, to which user Please specify a physical directory (on the
<strong>{{ SYSUSERID }}</strong> can write, on behalf of Data Controller: <strong> {{ SYSHOSTNAME }}</strong>
compute server) below, to which user
<strong>{{ SYSUSERID }}</strong> can write, on behalf of Data Controller.
</p> </p>
<label class="mt-20 clr-control-label">DC Directory</label> <label class="mt-20 clr-control-label">DC Staging Directory</label>
<div class="mb-10 clr-control-container"> <div class="mb-10 clr-control-container">
<div class="clr-input-wrapper"> <div class="clr-input-wrapper">
<input <input

View File

@ -1,23 +0,0 @@
.clr-control-container {
width: 50vw;
}
.clr-input-wrapper {
width: 100%;
input {
width: 100%;
}
}
.thinProgress {
left: 0px;
right: 0;
width: unset;
height: 1px;
margin-top: 0 !important;
&::after {
top: 0;
}
}

View File

@ -1,8 +1,16 @@
import { Location } from '@angular/common' import { Location } from '@angular/common'
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core'
import SASjs, { SASjsConfig } from '@sasjs/adapter' import SASjs, { SASjsConfig } from '@sasjs/adapter'
import { ServerType } from '@sasjs/utils/types/serverType' import { ServerType } from '@sasjs/utils/types/serverType'
import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings' import { DcAdapterSettings } from 'src/app/models/DcAdapterSettings'
import { RequestWrapperResponse } from 'src/app/models/request-wrapper/RequestWrapperResponse'
import { SASGroup } from 'src/app/models/sas/public-getgroups.model' import { SASGroup } from 'src/app/models/sas/public-getgroups.model'
import { SASjsApiServerInfo } from 'src/app/models/sasjs-api/SASjsApiServerInfo.model' import { SASjsApiServerInfo } from 'src/app/models/sasjs-api/SASjsApiServerInfo.model'
import { SasService } from 'src/app/services/sas.service' import { SasService } from 'src/app/services/sas.service'
@ -11,7 +19,8 @@ import { SasjsService } from 'src/app/services/sasjs.service'
@Component({ @Component({
selector: 'app-sasjs-configurator', selector: 'app-sasjs-configurator',
templateUrl: './sasjs-configurator.component.html', templateUrl: './sasjs-configurator.component.html',
styleUrls: ['./sasjs-configurator.component.scss'] styleUrls: ['./sasjs-configurator.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class SasjsConfiguratorComponent implements OnInit { export class SasjsConfiguratorComponent implements OnInit {
@Input() sasJs!: SASjs @Input() sasJs!: SASjs
@ -68,11 +77,11 @@ export class SasjsConfiguratorComponent implements OnInit {
this.loading = true this.loading = true
this.sasService.request('usernav/usergroupsbymember', null).then( this.sasService.request('usernav/usergroupsbymember', null).then(
(res: any) => { (res: RequestWrapperResponse) => {
this.METAPERSON = res.MF_GETUSER this.METAPERSON = res.adapterResponse.MF_GETUSER
this.SYSUSERID = res.SYSUSERID this.SYSUSERID = res.adapterResponse.SYSUSERID
this.SYSHOSTNAME = res.SYSHOSTNAME this.SYSHOSTNAME = res.adapterResponse.SYSHOSTNAME
this.SYSVLONG = res.SYSVLONG this.SYSVLONG = res.adapterResponse.SYSVLONG
/* /*
We would like to present a default DCPATH (deployment path) to the We would like to present a default DCPATH (deployment path) to the
@ -88,12 +97,14 @@ export class SasjsConfiguratorComponent implements OnInit {
*/ */
this.dcDirectory = this.dcDirectory =
this.tmpDirectories[ this.tmpDirectories[
['L', 'H', 'A', 'S'].includes(res.SYSSCPL.substring(0, 1)) ['L', 'H', 'A', 'S'].includes(
res.adapterResponse.SYSSCPL.substring(0, 1)
)
? 'linux' ? 'linux'
: 'windows' : 'windows'
] ]
this.dcAdminGroupList = res.groups this.dcAdminGroupList = res.adapterResponse.groups
this.dcAdminGroup = this.dcAdminGroupList[0].GROUPNAME this.dcAdminGroup = this.dcAdminGroupList[0].GROUPNAME
this.loading = false this.loading = false

View File

@ -1,238 +0,0 @@
.record-edit-modal {
.column-entry {
display: flex;
justify-content: space-between;
.name-input-row {
width: 100%;
max-width: 260px;
.cell-desc {
margin-right: 30px;
margin-top: 10px;
}
}
.inputs-wrapper {
flex: 1;
display: flex;
align-items: center;
::ng-deep >*:not(.date-field):not(clr-select-container) {
flex: 1;
}
}
p {
margin-top: 0px;
}
::ng-deep {
.clr-textarea-wrapper {
margin-top: 0 !important;
}
.clr-form-control {
margin-top: 0px !important;
}
app-soft-select {
display: block;
width: 224px;
border: 1px solid #999;
color: #000;
padding: calc(.25rem + 2px) .5rem;
border-radius: .125rem;
font-size: .541667rem;
margin-right: 6px;
input {
width: 100%;
border: 0;
&:focus {
background: none;
border: 0 !important;
}
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
}
}
&:first-child p:first-child {
margin-top: 0;
}
}
.date-field {
position: relative;
display: inline-block;
textarea {
width: 230px;
}
.date-picker {
position: absolute;
right: 0;
top: 4px;
::ng-deep {
// clr-datepicker-view-manager {
// transform: unset !important;
// left: unset !important;
// right: 70px !important;
// }
.clr-input-group {
border: 0 !important;
}
}
}
}
.modal-body {
padding-bottom: 10px;
}
::ng-deep {
clr-select-container {
border: 1px solid #999;
color: #000;
border-radius: .125rem;
margin-right: 5px;
.clr-select-wrapper {
max-height: unset;
&::after {
top: 15px;
}
}
select {
height: auto;
padding: 10px;
padding-right: 20px;
border: 0 !important;
&:focus {
background: 0 0 !important;
}
&:hover {
background: transparent;
}
}
}
clr-input-container {
width: 224px;
border: 1px solid #999;
color: #000;
padding: calc(.25rem + 2px) .5rem;
border-radius: .125rem;
font-size: .541667rem;
margin-right: 6px;
input {
width: 100%;
border: 0;
&:focus {
background: none;
border: 0 !important;
}
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
&.invalid-data {
border-color: red;
}
}
.modal-dialog {
width: 80vw;
}
.clr-control-container {
width: 100%;
textarea {
width: 100%;
resize: none;
border-color: #999;
&.invalid-data {
border-color: red;
outline: 0;
}
&.not-char {
font-family: "Lucida Console", Monaco, monospace;
}
}
}
.generate-record-url {
right: 40px;
top: 40px;
font-size: 12px;
}
.generate-record-url-button {
right: 25px;
top: 5px;
}
.modal-header {
padding: 0 0 1rem 0;
}
.modal-footer {
display: flex;
align-items: center;
justify-content: space-between;
// height: 65px;
.alert {
margin: 0;
}
}
}
}
.prev-next {
display: flex;
align-items: center;
p {
margin: 0;
}
button {
margin: 0px 10px;
}
}
.focusable {
&:focus {
box-shadow: 0 0 3px 0px #5aa220;
}
}
.entry-input-left-offset {
left: -30px;
}
.validation-info-alert {
width: 310px
}

View File

@ -1,5 +1,12 @@
import { KeyValue } from '@angular/common' import { KeyValue } from '@angular/common'
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core'
import moment from 'moment' import moment from 'moment'
import { ValidateFilterSASResponse } from 'src/app/models/sas/validate-filter.model' import { ValidateFilterSASResponse } from 'src/app/models/sas/validate-filter.model'
import { QueryClause } from 'src/app/models/TableData' import { QueryClause } from 'src/app/models/TableData'
@ -16,7 +23,8 @@ import { EditRecordModal } from '../../models/EditRecordModal'
@Component({ @Component({
selector: 'app-edit-record', selector: 'app-edit-record',
templateUrl: './edit-record.component.html', templateUrl: './edit-record.component.html',
styleUrls: ['./edit-record.component.scss'] styleUrls: ['./edit-record.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class EditRecordComponent implements OnInit { export class EditRecordComponent implements OnInit {
@Input() currentRecord!: EditRecordModal @Input() currentRecord!: EditRecordModal

View File

@ -1,8 +0,0 @@
:host {
display: block;
}
p {
margin: 0;
text-align: center;
}

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
/** /**
* Goal of this component is to recieve array of strings where every element is one state * Goal of this component is to recieve array of strings where every element is one state
@ -10,7 +10,8 @@ import { Component, OnInit } from '@angular/core'
@Component({ @Component({
selector: 'app-upload-stater', selector: 'app-upload-stater',
templateUrl: './upload-stater.component.html', templateUrl: './upload-stater.component.html',
styleUrls: ['./upload-stater.component.scss'] styleUrls: ['./upload-stater.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class UploadStaterComponent implements OnInit { export class UploadStaterComponent implements OnInit {
public statesList: string[] = [] //States appended to be displayed public statesList: string[] = [] //States appended to be displayed

View File

@ -36,7 +36,7 @@
<div class="clr-row card-block mt-15 d-flex justify-content-between"> <div class="clr-row card-block mt-15 d-flex justify-content-between">
<div class="clr-col-md-auto"> <div class="clr-col-md-auto">
<div class="encoding-block"> <div class="encoding-block">
<clr-radio-container class="mt-0-i" clrInline> <clr-radio-container class="mt-0" clrInline>
<clr-radio-wrapper> <clr-radio-wrapper>
<input <input
type="radio" type="radio"
@ -193,13 +193,14 @@
libName: (libds?.split('.'))![0], libName: (libds?.split('.'))![0],
tableName: (libds?.split('.'))![1] tableName: (libds?.split('.'))![1]
} as libdsParsed" } as libdsParsed"
class="editor-title text-center mt-0-i" class="editor-title text-center mt-0"
> >
<clr-tooltip> <clr-tooltip>
<clr-icon <clr-icon
clrTooltipTrigger clrTooltipTrigger
(click)="datasetInfo = true" (click)="datasetInfo = true"
shape="info-circle" shape="info-circle"
aria-label="View dataset meta info"
class="is-highlight cursor-pointer" class="is-highlight cursor-pointer"
size="24" size="24"
></clr-icon> ></clr-icon>

View File

@ -1,246 +0,0 @@
.card {
margin-top: 0;
border: 0;
}
.buttonBar {
padding: 2px 10px 2px 10px;
align-items: center;
}
.testRed {
color: white;
background: rgba(255,0,0, 0.8) !important;
}
hot-table {
::ng-deep {
.firstColumnHeaderStyle button.changeType {
display: none;
}
.handsontable tbody th.ht__highlight, .handsontable thead th.ht__highlight {
&.primaryKeyHeaderStyle {
background-color: #306b00b0 !important;
}
}
.primaryKeyHeaderStyle {
background-color: #306b006e !important;
}
th.readonlyCell {
div {
opacity: 0.4;
}
}
td.readonlyCell {
opacity: 0.5
}
}
}
.submit-reason {
min-height: 120px;
max-height: 120px;
height: 120px;
}
.infoBar {
margin-top:14px;
background: #495967;
color: white;
text-align:center;
padding: 3px;
font-size: 16px;
height: 30px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
span {
width: 80%;
}
&:hover {
height: unset;
white-space: normal;
span {
width: unset;
}
}
}
.pkHeader {
background: #687682;
color: #fff;
margin: -1px -1px -1px -1px;
}
.headerBar {
// padding: 13px 0px 14px 0px;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background: var(--clr-vertical-nav-bg-color);
}
.error-icon {
width: 30px;
height: 30px;
color: red;
}
.btnCtrl {
display:flex;
justify-content:flex-end;
}
.card-header {
border-bottom: 1px solid transparent;
}
.hidden {
visibility: hidden;
}
.my-drop-zone {
border: solid 1px lightgray;
border-radius: 10px;
background: whitesmoke;
box-shadow: inset 0px 0px 4px 2px #a7a5a52b;
height: 50vh;
}
.nv-file-over {
border: solid 2px green;
} /* Default class applied to drop zones on over */
.file-drop-text{
text-align: center;
}
.nv-file-over {
border: solid 2px green;
} /* Default class applied to drop zones on over */
.file-drop-text{
text-align: center;
}
@media screen and (max-width: 768px) {
.progresStatic {
margin-top:9px!important;
}
.progress, .progress-static {
width: calc(100% - 14px);
}
}
.hotEditor {
position: relative;
}
.excel-parsing {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.details {
margin: 0;
position: absolute;
top: -45px;
}
}
.edit-record-spinner {
display: flex;
justify-content: center;
align-items: center;
background: rgba(255, 255, 255, 0.6);
position: absolute;
top: 0px;
bottom: 0px;
width: 100%;
z-index: 500;
}
@media screen and (max-width: 480px) {
.progresStatic {
margin-top:32px!important;
}
.card-block, .card-footer {
padding: 10px 0px 0px 0px;
}
}
.content-area {
padding: 0 0.8rem 0.8rem 0.8rem !important;
padding-top: 0;
// .card {
// min-height: calc(100vh - 160px);
// }
}
.drop-area {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
justify-content: center;
align-items: flex-start;
margin: 1px;
border: 2px dashed #fff;
z-index: -1;
span {
font-size: 20px;
margin-top: 20px;
padding: 10px;
background: #dbdbdb;
border-radius: 5px;
color: black;
}
}
#submitBtn, #cancelSubmitBtn {
width: 150px;
}
.view-table {
font-size: inherit !important;
}
// When width is smaller remove the text from the buttons
// keep only the icons
@media (max-width: 992px) {
.icon-collapse {
.text {
display: none;
}
}
}
// FIXME
// Let's leave it here for a reference if there
// is an issue with viewboxes/filter modal overlaying
// we will remove it if no issues found
// .filter-modal {
// z-index: 1210;
// }

View File

@ -57,10 +57,10 @@ import { LicenceService } from '../services/licence.service'
import * as numbro from 'numbro' import * as numbro from 'numbro'
import * as languages from 'numbro/dist/languages.min' import * as languages from 'numbro/dist/languages.min'
import { FileUploadEncoding } from '../models/FileUploadEncoding' import { FileUploadEncoding } from '../models/FileUploadEncoding'
import { import { SpreadsheetService } from '../services/spreadsheet.service'
ParseResult, import { UploadFileResponse } from '../models/UploadFile'
SpreadsheetService import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
} from '../services/spreadsheet.service' import { ParseResult } from '../models/ParseResult.interface'
@Component({ @Component({
selector: 'app-editor', selector: 'app-editor',
@ -69,7 +69,7 @@ import {
host: { host: {
class: 'content-container' class: 'content-container'
}, },
encapsulation: ViewEncapsulation.Emulated encapsulation: ViewEncapsulation.None
}) })
export class EditorComponent implements OnInit, AfterViewInit { export class EditorComponent implements OnInit, AfterViewInit {
@ViewChildren('uploadStater') @ViewChildren('uploadStater')
@ -81,11 +81,11 @@ export class EditorComponent implements OnInit, AfterViewInit {
@ViewChildren('fileUploadInput') @ViewChildren('fileUploadInput')
fileUploadInputCompList: QueryList<ElementRef> = new QueryList() fileUploadInputCompList: QueryList<ElementRef> = new QueryList()
public static cnt: number = 0 public static cnt = 0
public static nonPkCnt: number = 0 public static nonPkCnt = 0
public static lastCell: number = 0 public static lastCell = 0
private _tableSub: Subscription | undefined private _tableSub: Subscription | undefined
public message: string = '' public message = ''
public $dataFormats: $DataFormats | null = null public $dataFormats: $DataFormats | null = null
public submit: boolean | undefined public submit: boolean | undefined
public cols: Col[] = [] public cols: Col[] = []
@ -107,12 +107,12 @@ export class EditorComponent implements OnInit, AfterViewInit {
*/ */
restrictions: EditorRestrictions = {} restrictions: EditorRestrictions = {}
datasetInfo: boolean = false datasetInfo = false
dsmeta: DSMeta[] = [] dsmeta: DSMeta[] = []
versions: Version[] = [] versions: Version[] = []
dsNote = '' dsNote = ''
viewboxes: boolean = false viewboxes = false
Infinity = Infinity Infinity = Infinity
@ -154,7 +154,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
selection: any[], selection: any[],
clickEvent: MouseEvent clickEvent: MouseEvent
) => { ) => {
let firstSelection = selection[0] const firstSelection = selection[0]
if (firstSelection.start.row === firstSelection.end.row) { if (firstSelection.start.row === firstSelection.end.row) {
this.editRecord(null, firstSelection.start.row) this.editRecord(null, firstSelection.start.row)
@ -195,25 +195,25 @@ export class EditorComponent implements OnInit, AfterViewInit {
public hotCellsPropRow: number | null = null public hotCellsPropRow: number | null = null
public filter: boolean = false public filter = false
public submitLoading: boolean = false public submitLoading = false
public uploadLoading: boolean = false public uploadLoading = false
public rowsChanged: any = { public rowsChanged: any = {
rowsUpdated: 0, rowsUpdated: 0,
rowsDeleted: 0, rowsDeleted: 0,
rowsAdded: 0 rowsAdded: 0
} }
public modifedRowsIndexes: number[] = [] public modifedRowsIndexes: number[] = []
public queryErr: boolean = false public queryErr = false
public queryErrMessage: string | undefined public queryErrMessage: string | undefined
public successEnable: boolean = false public successEnable = false
public libTab: string | undefined public libTab: string | undefined
public queryFilter: any public queryFilter: any
public _query: Subscription | undefined public _query: Subscription | undefined
public whereString: string | undefined public whereString: string | undefined
public clauses: any public clauses: any
public nullVariables: boolean = false public nullVariables = false
public tableId: string | undefined public tableId: string | undefined
public pkFields: any = [] public pkFields: any = []
@ -221,53 +221,53 @@ export class EditorComponent implements OnInit, AfterViewInit {
public libds: string | undefined public libds: string | undefined
public filter_pk: string | undefined public filter_pk: string | undefined
public table: any public table: any
public filename: string = '' public filename = ''
public selectedColumn: any public selectedColumn: any
public hotSelection: Array<number> | null | undefined public hotSelection: Array<number> | null | undefined
public submitLimitNotice: boolean = false public submitLimitNotice = false
public badEdit: boolean = false public badEdit = false
public badEditCause: string | undefined public badEditCause: string | undefined
public badEditTitle: string | undefined public badEditTitle: string | undefined
public tableTrue: boolean | undefined public tableTrue: boolean | undefined
public saveLoading: boolean = false public saveLoading = false
public approvers: string[] = [] public approvers: string[] = []
public approver: any public approver: any
public readOnlyFields!: number public readOnlyFields!: number
public errValidation: boolean = false public errValidation = false
public dataObj: any public dataObj: any
public disableSubmit: boolean | undefined public disableSubmit: boolean | undefined
public pkNull: boolean = false public pkNull = false
public noPkNull: boolean = false public noPkNull = false
public tableData: Array<any> = [] public tableData: Array<any> = []
public queryText: string = '' public queryText = ''
public queryTextSaved: string = '' public queryTextSaved = ''
public showApprovers: boolean = false public showApprovers = false
public pkDups: boolean = false public pkDups = false
public validationDone: number = 0 public validationDone = 0
public duplicatePkIndexes: any = [] public duplicatePkIndexes: any = []
public columnHeader: string[] = [] public columnHeader: string[] = []
public specInfo: { col: string; len: number; type: number }[] = [] public specInfo: { col: string; len: number; type: number }[] = []
public tooLong: boolean = false public tooLong = false
public exceedCells: { public exceedCells: {
col: string col: string
len: number len: number
val: string val: string
}[] = [] }[] = []
public uploader: FileUploader = new FileUploader() public uploader: FileUploader = new FileUploader()
public uploadUrl: string = '' public uploadUrl = ''
public excelFileReady: boolean = false public excelFileReady = false
public uploadPreview: boolean = false public uploadPreview = false
public excelFileParsing: boolean = false public excelFileParsing = false
public excelUploadState: string | null = null public excelUploadState: string | null = null
public data: AOA = [] public data: AOA = []
public headerArray: string[] = [] public headerArray: string[] = []
public hotDataSchema: any = {} public hotDataSchema: any = {}
public headerShow: string[] = [] public headerShow: string[] = []
public headerVisible: boolean = false public headerVisible = false
public hasBaseDropZoneOver: boolean = false public hasBaseDropZoneOver = false
public hasAnotherDropZoneOver: boolean = false public hasAnotherDropZoneOver = false
public headerPks: string[] = [] public headerPks: string[] = []
public columnLevelSecurityFlag: boolean = false public columnLevelSecurityFlag = false
public dateTimeHeaders: string[] = [] public dateTimeHeaders: string[] = []
public timeHeaders: string[] = [] public timeHeaders: string[] = []
public dateHeaders: string[] = [] public dateHeaders: string[] = []
@ -286,12 +286,12 @@ export class EditorComponent implements OnInit, AfterViewInit {
dataModified!: any[] dataModified!: any[]
public filePasswordSubject: Subject<string | undefined> = new Subject() public filePasswordSubject: Subject<string | undefined> = new Subject()
public fileUnlockError: boolean = false public fileUnlockError = false
public filePasswordModal: boolean = false public filePasswordModal = false
public showUploadModal: boolean = false public showUploadModal = false
public discardSourceFile: boolean = false public discardSourceFile = false
public manualFileEditModal: boolean = false public manualFileEditModal = false
public recordAction: string | null = null public recordAction: string | null = null
@ -299,25 +299,25 @@ export class EditorComponent implements OnInit, AfterViewInit {
public currentEditRecordValidator: DcValidator | undefined public currentEditRecordValidator: DcValidator | undefined
public currentEditRecordLoadings: number[] = [] public currentEditRecordLoadings: number[] = []
public currentEditRecordErrors: number[] = [] public currentEditRecordErrors: number[] = []
public currentEditRecordIndex: number = -1 public currentEditRecordIndex = -1
public generateEditRecordUrlLoading: boolean = false public generateEditRecordUrlLoading = false
public generatedRecordUrl: string | null = null public generatedRecordUrl: string | null = null
public addRecordUrl: string | null = null public addRecordUrl: string | null = null
public recordNewOrPkModified: boolean = false public recordNewOrPkModified = false
public addRecordLoading: boolean = false public addRecordLoading = false
public singleRowSelected: boolean = false public singleRowSelected = false
public addingNewRow: boolean = false public addingNewRow = false
public getdataError: boolean = false public getdataError = false
public zeroFilterRows: boolean = false public zeroFilterRows = false
public tableFileDragOver: boolean = false public tableFileDragOver = false
/** /**
* Hash/values table used for dynamic cell validation * Hash/values table used for dynamic cell validation
*/ */
public cellValidationSource: CellValidationSource[] = [] public cellValidationSource: CellValidationSource[] = []
public validationTableLimit: number = 20 public validationTableLimit = 20
public extendedCellValidationFields: { public extendedCellValidationFields: {
DISPLAY_INDEX: number DISPLAY_INDEX: number
EXTRA_COL_NAME: number EXTRA_COL_NAME: number
@ -463,8 +463,8 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.hasBaseDropZoneOver = e this.hasBaseDropZoneOver = e
} }
public attachFile(event: any, dropped: boolean = false) { public attachFile(event: any, dropped = false) {
let file: File = dropped ? event[0] : event.target.files[0] const file: File = dropped ? event[0] : event.target.files[0]
this.excelUploadState = 'Loading' this.excelUploadState = 'Loading'
this.excelFileParsing = true this.excelFileParsing = true
@ -494,7 +494,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.eventService.showInfoModal('Table Found', tableFoundInfo) this.eventService.showInfoModal('Table Found', tableFoundInfo)
} }
) )
.then((parseResult: ParseResult | undefined) => { .then(async (parseResult: ParseResult | undefined) => {
if (parseResult) { if (parseResult) {
this.excelFileReady = true this.excelFileReady = true
@ -507,7 +507,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.getPendingExcelPreview() this.getPendingExcelPreview()
} else { } else {
// otherwise it's csv file, and we send them directly // otherwise it's csv file, and we send them directly
this.uploadParsedFiles() await this.uploadParsedFiles()
} }
} }
}) })
@ -542,7 +542,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
* This method will run validations and upload all of the pending files * This method will run validations and upload all of the pending files
* that are in the uploader queue. * that are in the uploader queue.
*/ */
public uploadParsedFiles() { public async uploadParsedFiles() {
if (this.checkInvalid()) { if (this.checkInvalid()) {
this.eventService.showAbortModal(null, 'Invalid values are present.') this.eventService.showAbortModal(null, 'Invalid values are present.')
return return
@ -559,7 +559,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
} }
this.uploadLoading = true this.uploadLoading = true
let filesToUpload: UploadFile[] = [] const filesToUpload: UploadFile[] = []
for (const file of this.uploader.queue) { for (const file of this.uploader.queue) {
filesToUpload.push({ filesToUpload.push({
@ -568,23 +568,23 @@ export class EditorComponent implements OnInit, AfterViewInit {
}) })
} }
this.sasService await this.sasService
.uploadFile(this.uploadUrl, filesToUpload, { table: this.libds }) .uploadFile(this.uploadUrl, filesToUpload, { table: this.libds })
.then( .then(
(res: any) => { (res: UploadFileResponse) => {
if (typeof res.sasjsAbort === 'undefined') { if (typeof res.adapterResponse.sasjsAbort === 'undefined') {
if (typeof res.sasparams === 'undefined') { if (typeof res.adapterResponse.sasparams === 'undefined') {
return return
} else { } else {
this.uploadLoading = false this.uploadLoading = false
let params = res.sasparams[0] const params = res.adapterResponse.sasparams[0]
this.successEnable = true this.successEnable = true
this.tableId = params.DSID this.tableId = params.DSID
this.router.navigateByUrl('/stage/' + this.tableId) this.router.navigateByUrl('/stage/' + this.tableId)
} }
} else { } else {
// handle succesfull response // handle succesfull response
const abortRes = res const abortRes = res.adapterResponse
const abortMsg = abortRes.sasjsAbort[0].MSG const abortMsg = abortRes.sasjsAbort[0].MSG
const macMsg = abortRes.sasjsAbort[0].MAC const macMsg = abortRes.sasjsAbort[0].MAC
@ -609,7 +609,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.fileUploadInputCompList.first.nativeElement.value = '' this.fileUploadInputCompList.first.nativeElement.value = ''
} }
this.uploader.queue = [] this.uploader.queue = []
this.eventService.catchResponseError('file upload', err) this.eventService.catchResponseError(
'file upload',
err.adapterResponse
)
} }
) )
} }
@ -625,10 +628,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.toggleHotPlugin('contextMenu', false) this.toggleHotPlugin('contextMenu', false)
let previewDatasource: any[] = [] const previewDatasource: any[] = []
this.data.map((item) => { this.data.map((item) => {
let itemObject: any = {} const itemObject: any = {}
this.headerShow.map((header: any, index: number) => { this.headerShow.map((header: any, index: number) => {
itemObject[header] = item[index] itemObject[header] = item[index]
@ -707,7 +710,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.editTable(true) this.editTable(true)
} }
private appendUploadState(state: string, replaceLast: boolean = false) { private appendUploadState(state: string, replaceLast = false) {
this.cdf.detectChanges() this.cdf.detectChanges()
if (this.uploadStaterCompList.first) { if (this.uploadStaterCompList.first) {
@ -740,7 +743,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
async sendClause() { async sendClause() {
this.submitLoading = true this.submitLoading = true
let nullVariableArr = [] let nullVariableArr = []
let emptyVariablesArr = [] const emptyVariablesArr = []
// to check number of empty clauses // to check number of empty clauses
if (typeof this.clauses === 'undefined') { if (typeof this.clauses === 'undefined') {
@ -748,7 +751,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.submitLoading = false this.submitLoading = false
return return
} else { } else {
let query = this.clauses.queryObj const query = this.clauses.queryObj
if (query[0].elements.length < 1) { if (query[0].elements.length < 1) {
// Clear cached filtering data // Clear cached filtering data
@ -863,9 +866,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
const hot = this.hotInstance const hot = this.hotInstance
let columnSorting = hot.getPlugin('multiColumnSorting') const columnSorting = hot.getPlugin('multiColumnSorting')
let columnSortConfig = columnSorting.getSortConfig() const columnSortConfig = columnSorting.getSortConfig()
let sortConfigs = Array.isArray(columnSortConfig) const sortConfigs = Array.isArray(columnSortConfig)
? columnSortConfig ? columnSortConfig
: [columnSortConfig] : [columnSortConfig]
@ -888,7 +891,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
hot.render() hot.render()
for (let sortConfig of sortConfigs) { for (const sortConfig of sortConfigs) {
columnSorting.sort(sortConfig) columnSorting.sort(sortConfig)
} }
@ -897,9 +900,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
} }
convertToCorrectTypes(dataSource: any) { convertToCorrectTypes(dataSource: any) {
for (let row of dataSource) { for (const row of dataSource) {
for (let colKey in row) { for (const colKey in row) {
let colSpecs = this.cols.find((x: any) => x.NAME === colKey) const colSpecs = this.cols.find((x: any) => x.NAME === colKey)
if (colSpecs) { if (colSpecs) {
if ( if (
@ -920,9 +923,9 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.cellValidationSource = [] this.cellValidationSource = []
const hot = this.hotInstance const hot = this.hotInstance
let columnSorting = hot.getPlugin('multiColumnSorting') const columnSorting = hot.getPlugin('multiColumnSorting')
let columnSortConfig = columnSorting.getSortConfig() const columnSortConfig = columnSorting.getSortConfig()
let sortConfigs = Array.isArray(columnSortConfig) const sortConfigs = Array.isArray(columnSortConfig)
? columnSortConfig ? columnSortConfig
: [columnSortConfig] : [columnSortConfig]
@ -943,14 +946,14 @@ export class EditorComponent implements OnInit, AfterViewInit {
hot.validateRows(this.modifedRowsIndexes) hot.validateRows(this.modifedRowsIndexes)
// this.editRecordListeners(); // this.editRecordListeners();
for (let sortConfig of sortConfigs) { for (const sortConfig of sortConfigs) {
columnSorting.sort(sortConfig) columnSorting.sort(sortConfig)
} }
this.checkRowLimit() this.checkRowLimit()
} }
timesClicked: number = 0 timesClicked = 0
public hotClicked() { public hotClicked() {
if (this.timesClicked === 1 && this.hotTable.readOnly) { if (this.timesClicked === 1 && this.hotTable.readOnly) {
this.editTable() this.editTable()
@ -979,7 +982,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
setTimeout(() => { setTimeout(() => {
const hot = this.hotInstance const hot = this.hotInstance
let dsInsertIndex = this.dataSource.length const dsInsertIndex = this.dataSource.length
hot.alter('insert_row_below', dsInsertIndex, 1) hot.alter('insert_row_below', dsInsertIndex, 1)
hot.updateSettings({ data: this.dataSource }, false) hot.updateSettings({ data: this.dataSource }, false)
hot.selectCell(this.dataSource.length - 1, 0) hot.selectCell(this.dataSource.length - 1, 0)
@ -1037,14 +1040,14 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.dataModified = [] this.dataModified = []
for (let i = 0; i < this.dataSource.length; i++) { for (let i = 0; i < this.dataSource.length; i++) {
let dataRow = this.helperService.deepClone(this.dataSource[i]) const dataRow = this.helperService.deepClone(this.dataSource[i])
if (dataRow._____DELETE__THIS__RECORD_____ === 'Yes') { if (dataRow._____DELETE__THIS__RECORD_____ === 'Yes') {
this.dataModified.push(dataRow) this.dataModified.push(dataRow)
rowsDeleted++ rowsDeleted++
} else { } else {
let dataRowUnchanged = this.dataSourceUnchanged.find((row: any) => { const dataRowUnchanged = this.dataSourceUnchanged.find((row: any) => {
for (let pkCol of this.headerPks) { for (const pkCol of this.headerPks) {
if (row[pkCol] !== dataRow[pkCol]) { if (row[pkCol] !== dataRow[pkCol]) {
return false return false
} }
@ -1077,7 +1080,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
public validatePrimaryKeys() { public validatePrimaryKeys() {
const hot = this.hotInstance const hot = this.hotInstance
let myTable = hot.getData() const myTable = hot.getData()
this.pkFields = [] this.pkFields = []
for (let index = 0; index < myTable.length; index++) { for (let index = 0; index < myTable.length; index++) {
let pkRow = '' let pkRow = ''
@ -1087,8 +1090,8 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.pkFields.push(pkRow) this.pkFields.push(pkRow)
} }
let results = [] const results = []
let rows = this.dataSource.length const rows = this.dataSource.length
for (let j = 0; j < this.pkFields.length; j++) { for (let j = 0; j < this.pkFields.length; j++) {
for (let i = 0; i < this.pkFields.length; i++) { for (let i = 0; i < this.pkFields.length; i++) {
@ -1133,12 +1136,12 @@ export class EditorComponent implements OnInit, AfterViewInit {
* when we don't want to re-set force values of every row in the table * when we don't want to re-set force values of every row in the table
*/ */
public reSetCellValidationValues( public reSetCellValidationValues(
setForcedValues: boolean = false, setForcedValues = false,
specificRowForceValue?: number specificRowForceValue?: number
) { ) {
const hot = this.hotInstance const hot = this.hotInstance
for (let entry of this.cellValidationSource) { for (const entry of this.cellValidationSource) {
const colSource = entry.values.map( const colSource = entry.values.map(
(el: any) => el[this.cellValidationFields.RAW_VALUE] (el: any) => el[this.cellValidationFields.RAW_VALUE]
) )
@ -1193,7 +1196,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
public reSetExtendedCellValidationValues( public reSetExtendedCellValidationValues(
cellValidationEntry?: CellValidationSource, cellValidationEntry?: CellValidationSource,
row?: number, row?: number,
setForcedValues: boolean = false, setForcedValues = false,
specificRowForceValue?: number specificRowForceValue?: number
) { ) {
const hot = this.hotInstance const hot = this.hotInstance
@ -1215,7 +1218,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
return return
} }
for (let entry of this.cellValidationSource) { for (const entry of this.cellValidationSource) {
const extendedValuesObject = this.getExtendedValuesByCellValue(entry) const extendedValuesObject = this.getExtendedValuesByCellValue(entry)
this.setExtendedValuesToCells( this.setExtendedValuesToCells(
@ -1232,19 +1235,19 @@ export class EditorComponent implements OnInit, AfterViewInit {
cellValidationEntry: CellValidationSource, cellValidationEntry: CellValidationSource,
row: number, row: number,
extendedValues: DynamicExtendedCellValidation[], extendedValues: DynamicExtendedCellValidation[],
setForcedValues: boolean = false, setForcedValues = false,
specificRowForceValue?: number specificRowForceValue?: number
) { ) {
const hot = this.hotInstance const hot = this.hotInstance
let uniqueCells: any[] = [] const uniqueCells: any[] = []
for (let element of extendedValues) { for (const element of extendedValues) {
if (uniqueCells.indexOf(element.EXTRA_COL_NAME) < 0) if (uniqueCells.indexOf(element.EXTRA_COL_NAME) < 0)
uniqueCells.push(element.EXTRA_COL_NAME) uniqueCells.push(element.EXTRA_COL_NAME)
} }
for (let cell of uniqueCells) { for (const cell of uniqueCells) {
const valuesForCol = extendedValues.filter( const valuesForCol = extendedValues.filter(
(x) => x.EXTRA_COL_NAME === cell (x) => x.EXTRA_COL_NAME === cell
) )
@ -1382,10 +1385,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
const extendedValuesObject = [] const extendedValuesObject = []
for (let extendedValue of filteredValues || []) { for (const extendedValue of filteredValues || []) {
let tempObj: any = {} const tempObj: any = {}
for (let key of Object.keys(prepObj)) { for (const key of Object.keys(prepObj)) {
tempObj[key] = extendedValue[prepObj[key]] tempObj[key] = extendedValue[prepObj[key]]
} }
@ -1439,7 +1442,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
/** /**
* Below code should be analized, not sure what is the purpose of exceedCells * Below code should be analized, not sure what is the purpose of exceedCells
*/ */
let myTableData = hot.getData() const myTableData = hot.getData()
// If the last row is empty, remove it before validation // If the last row is empty, remove it before validation
if (myTableData.length > 1 && hot.isEmptyRow(myTableData.length - 1)) { if (myTableData.length > 1 && hot.isEmptyRow(myTableData.length - 1)) {
@ -1459,7 +1462,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
hot.validateRows(this.modifedRowsIndexes, () => { hot.validateRows(this.modifedRowsIndexes, () => {
if (this.checkInvalid()) { if (this.checkInvalid()) {
let abortMsg = 'Invalid Values are Present' const abortMsg = 'Invalid Values are Present'
this.eventService.showInfoModal('Validation error', abortMsg) this.eventService.showInfoModal('Validation error', abortMsg)
@ -1470,7 +1473,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.validationDone = 1 this.validationDone = 1
setTimeout(() => { setTimeout(() => {
let txt: any = document.getElementById('formFields_8') const txt: any = document.getElementById('formFields_8')
if (txt) txt.focus() if (txt) txt.focus()
}, 200) }, 200)
}) })
@ -1487,11 +1490,11 @@ export class EditorComponent implements OnInit, AfterViewInit {
public async saveTable(data: any) { public async saveTable(data: any) {
const hot = this.hotInstance const hot = this.hotInstance
let hotData = hot.getData() const hotData = hot.getData()
data = data.filter((dataRow: any) => { data = data.filter((dataRow: any) => {
let elModified = this.dataModified.find((row) => { const elModified = this.dataModified.find((row) => {
for (let pkCol of this.headerPks) { for (const pkCol of this.headerPks) {
if (row[pkCol] !== dataRow[pkCol]) { if (row[pkCol] !== dataRow[pkCol]) {
return false return false
} }
@ -1504,7 +1507,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
}) })
data = data.map((row: any) => { data = data.map((row: any) => {
let deleteColValue = row['_____DELETE__THIS__RECORD_____'] const deleteColValue = row['_____DELETE__THIS__RECORD_____']
delete row['_____DELETE__THIS__RECORD_____'] delete row['_____DELETE__THIS__RECORD_____']
row['_____DELETE__THIS__RECORD_____'] = deleteColValue row['_____DELETE__THIS__RECORD_____'] = deleteColValue
@ -1522,7 +1525,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.loggerService.log('Data submitted', data) this.loggerService.log('Data submitted', data)
if (this.checkInvalid()) { if (this.checkInvalid()) {
let abortMsg = 'Invalid Values are Present' const abortMsg = 'Invalid Values are Present'
this.eventService.showInfoModal('Validation error', abortMsg) this.eventService.showInfoModal('Validation error', abortMsg)
this.cancelSubmit() this.cancelSubmit()
this.submit = false this.submit = false
@ -1540,7 +1543,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.saveLoading = true this.saveLoading = true
this.disableSubmit = false this.disableSubmit = false
this.submit = true this.submit = true
let updateParams: any = {} const updateParams: any = {}
updateParams.ACTION = 'LOAD' updateParams.ACTION = 'LOAD'
this.message = this.message.replace(/\n/g, '. ') this.message = this.message.replace(/\n/g, '. ')
updateParams.MESSAGE = this.message updateParams.MESSAGE = this.message
@ -1561,9 +1564,11 @@ export class EditorComponent implements OnInit, AfterViewInit {
'editors/stagedata', 'editors/stagedata',
this.$dataFormats this.$dataFormats
) )
.then((res: any) => { .then((res: RequestWrapperResponse) => {
if (typeof res.sasparams !== 'undefined') { if (typeof res.adapterResponse.sasparams !== 'undefined') {
this.router.navigateByUrl('/stage/' + res.sasparams[0].DSID) this.router.navigateByUrl(
'/stage/' + res.adapterResponse.sasparams[0].DSID
)
return true return true
} }
@ -1571,7 +1576,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
let error = `Submit request failed` let error = `Submit request failed`
if (res) { if (res) {
let errorText = const errorText =
typeof res === 'string' ? res : JSON.stringify(res) typeof res === 'string' ? res : JSON.stringify(res)
error += `\n${errorText}` error += `\n${errorText}`
@ -1591,7 +1596,10 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.disableSubmit = true this.disableSubmit = true
this.submit = false this.submit = false
let errorText = typeof err === 'string' ? err : JSON.stringify(err) const errorText =
typeof err.adapterRespnse === 'string'
? err.adapterRespnse
: JSON.stringify(err.adapterRespnse)
this.eventService.showAbortModal( this.eventService.showAbortModal(
'editors/stagedata', 'editors/stagedata',
@ -1661,7 +1669,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
} }
} }
confirmRecordEdit(close: boolean = true) { confirmRecordEdit(close = true) {
const closingRecordIndex = this.currentEditRecordIndex const closingRecordIndex = this.currentEditRecordIndex
if (close) this.currentEditRecordIndex = -1 if (close) this.currentEditRecordIndex = -1
@ -1763,7 +1771,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
) )
// Update that copy with current cells meta (dynamic validation data) // Update that copy with current cells meta (dynamic validation data)
for (let cellMeta of currentEditRecordCellsMeta) { for (const cellMeta of currentEditRecordCellsMeta) {
if (cellMeta) { if (cellMeta) {
const data = cellMeta.prop?.toString() //------------ const data = cellMeta.prop?.toString() //------------
delete cellMeta.prop // We convert to be able to update dcValidator rule by using CellProperties delete cellMeta.prop // We convert to be able to update dcValidator rule by using CellProperties
@ -1787,7 +1795,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
const hot = this.hotInstance const hot = this.hotInstance
hot.batch(() => { hot.batch(() => {
let contextMenuPlugin = hot.getPlugin<any>(pluginName) const contextMenuPlugin = hot.getPlugin<any>(pluginName)
if (!contextMenuPlugin) { if (!contextMenuPlugin) {
console.warn( console.warn(
@ -1983,8 +1991,8 @@ export class EditorComponent implements OnInit, AfterViewInit {
suppressSuccessAbortModal: true, suppressSuccessAbortModal: true,
suppressErrorAbortModal: true suppressErrorAbortModal: true
}) })
.then((res: any) => { .then((res: RequestWrapperResponse) => {
const colSource = res.dynamic_values.map( const colSource = res.adapterResponse.dynamic_values.map(
(el: any) => el[this.cellValidationFields.RAW_VALUE] (el: any) => el[this.cellValidationFields.RAW_VALUE]
) )
@ -1998,8 +2006,8 @@ export class EditorComponent implements OnInit, AfterViewInit {
...this.cellValidationSource[validationSourceIndex], ...this.cellValidationSource[validationSourceIndex],
row: row, row: row,
col: column, col: column,
values: res.dynamic_values, values: res.adapterResponse.dynamic_values,
extended_values: res.dynamic_extended_values extended_values: res.adapterResponse.dynamic_extended_values
} }
} }
@ -2214,7 +2222,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
}) })
// recover lib and table parameters from url; filter pk is optional - if filter is applied // recover lib and table parameters from url; filter pk is optional - if filter is applied
let myParams: any = {} const myParams: any = {}
if (typeof this.route.snapshot.params['libMem'] !== 'undefined') { if (typeof this.route.snapshot.params['libMem'] !== 'undefined') {
this.libds = this.route.snapshot.params['libMem'] this.libds = this.route.snapshot.params['libMem']
this.filter_pk = this.route.snapshot.params['filterId'] this.filter_pk = this.route.snapshot.params['filterId']
@ -2495,7 +2503,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
this.columnHeader[0] = 'Delete?' this.columnHeader[0] = 'Delete?'
this.readOnlyFields = response.data.sasparams[0].PKCNT this.readOnlyFields = response.data.sasparams[0].PKCNT
let hotInstaceEl = document.getElementById('hotInstance') const hotInstaceEl = document.getElementById('hotInstance')
if (hotInstaceEl) { if (hotInstaceEl) {
hotInstaceEl.addEventListener('mousedown', (event) => { hotInstaceEl.addEventListener('mousedown', (event) => {
@ -2504,7 +2512,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
} }
setTimeout(() => { setTimeout(() => {
let menuDebugItem: any = const menuDebugItem: any =
document.querySelector('.debug-switch-item') || undefined document.querySelector('.debug-switch-item') || undefined
if (menuDebugItem) menuDebugItem.click() if (menuDebugItem) menuDebugItem.click()
}, 100) }, 100)
@ -2594,7 +2602,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
hot.addHook('afterCreateRow', (source: any, change: any) => { hot.addHook('afterCreateRow', (source: any, change: any) => {
if (source > this.dataSource.length) { if (source > this.dataSource.length) {
// don't scroll if row is not added to the end (bottom) // don't scroll if row is not added to the end (bottom)
let wtHolder = document.querySelector('.wtHolder') const wtHolder = document.querySelector('.wtHolder')
setTimeout(() => { setTimeout(() => {
if (wtHolder) wtHolder.scrollTop = wtHolder.scrollHeight if (wtHolder) wtHolder.scrollTop = wtHolder.scrollHeight

View File

@ -1,85 +0,0 @@
@import '../../colors.scss';
::ng-deep body[cds-theme="dark"] {
.group-info {
background-color: $headerBackground;
border-color: $headerBackground;
}
.group-data {
background-color: $headerBackground;
border-color: $headerBackground;
}
.member-table tbody{
tr:hover{
background-color: #29404b;
}
}
}
::ng-deep body[cds-theme="light"] {
.group-info{
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.group-data {
background-color: #f9f9f9;
border-color: #a7a7a7;
box-shadow: 0px 2px 5px #dad7d7;
}
.member-table tbody{
tr:hover{
background-color: #e6e6e6;
}
}
}
.sidebar-height{
height: 100%;
}
.group-info-text{
display: inline;
font-size: 20px;
}
.group-info{
border: 1px solid;
border-radius: 3px;
}
.group-info td{
text-align: center;
}
.group-data{
border: 1px solid;
border-radius: 3px;
}
.group-data{
min-height: auto;
h3, h5{
text-align: center;
}
.member-table{
width: 100%;
}
.member-table tbody{
tr:hover{
cursor: pointer;
}
}
}
.table-container{
overflow: auto;
}
@media screen and (max-width: 768px){
.group-data{
min-height: unset !important;
}
}

View File

@ -1,11 +1,12 @@
import { Location } from '@angular/common' import { Location } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { SASjsConfig } from '@sasjs/adapter' import { SASjsConfig } from '@sasjs/adapter'
import { ServerType } from '@sasjs/utils/types/serverType' import { ServerType } from '@sasjs/utils/types/serverType'
import { HelperService } from '../services/helper.service' import { HelperService } from '../services/helper.service'
import { SasService } from '../services/sas.service' import { SasService } from '../services/sas.service'
import { globals } from '../_globals' import { globals } from '../_globals'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
@Component({ @Component({
selector: 'app-group', selector: 'app-group',
@ -13,7 +14,8 @@ import { globals } from '../_globals'
styleUrls: ['./group.component.scss'], styleUrls: ['./group.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class GroupComponent implements OnInit { export class GroupComponent implements OnInit {
public groups: Array<any> | undefined public groups: Array<any> | undefined
@ -82,11 +84,13 @@ export class GroupComponent implements OnInit {
globals.usernav.groupList = groups globals.usernav.groupList = groups
}) })
} else { } else {
this.sasService.request('public/getgroups', null).then((res: any) => { this.sasService
this.loading = false .request('public/getgroups', null)
this.groups = res.groups .then((res: RequestWrapperResponse) => {
globals.usernav.groupList = res.groups this.loading = false
}) this.groups = res.adapterResponse.groups
globals.usernav.groupList = res.adapterResponse.groups
})
} }
} else { } else {
this.groups = globals.usernav.groupList this.groups = globals.usernav.groupList
@ -128,14 +132,15 @@ export class GroupComponent implements OnInit {
let data = { iwant: [{ groupid: this.paramURI }] } let data = { iwant: [{ groupid: this.paramURI }] }
this.sasService this.sasService
.request('usernav/usermembersbygroup', data) .request('usernav/usermembersbygroup', data)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
this.groupMembers = res.sasmembers this.groupMembers = res.adapterResponse.sasmembers
this.groupMemberCount = res.sasmembers.length this.groupMemberCount = res.adapterResponse.sasmembers.length
if (res.sasmembers[0] !== undefined) { if (res.adapterResponse.sasmembers[0] !== undefined) {
this.loading = false this.loading = false
this.groupUri = res.sasmembers[0].URIMEM || this.paramURI this.groupUri =
this.groupName = res.sasmembers[0].GROUPNAME res.adapterResponse.sasmembers[0].URIMEM || this.paramURI
this.groupDesc = res.sasmembers[0].GROUPDESC this.groupName = res.adapterResponse.sasmembers[0].GROUPNAME
this.groupDesc = res.adapterResponse.sasmembers[0].GROUPDESC
if (!this.groupName) { if (!this.groupName) {
this.groupName = this.paramURI this.groupName = this.paramURI
@ -202,13 +207,13 @@ export class GroupComponent implements OnInit {
this.sasService this.sasService
.request('usernav/usermembersbygroup', data) .request('usernav/usermembersbygroup', data)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
this.loading = false this.loading = false
this.groupUri = group.GROUPURI this.groupUri = group.GROUPURI
this.groupName = group.GROUPNAME this.groupName = group.GROUPNAME
this.groupDesc = group.GROUPDESC this.groupDesc = group.GROUPDESC
this.groupMembers = res.sasmembers this.groupMembers = res.adapterResponse.sasmembers
this.groupMemberCount = res.sasmembers.length this.groupMemberCount = res.adapterResponse.sasmembers.length
}) })
} }
} }

View File

@ -123,11 +123,11 @@
</div> </div>
<div *ngIf="!loading" class="no-table-selected"> <div *ngIf="!loading" class="no-table-selected">
<clr-icon <img
shape="warning-standard" src="images/select-table.png"
size="60" class="select-table-icon"
class="is-info icon-dc-fill" alt="select table icon"
></clr-icon> />
<p <p
*ngIf="treeNodeLibraries?.length! > 0" *ngIf="treeNodeLibraries?.length! > 0"
class="text-center color-gray mt-10" class="text-center color-gray mt-10"

View File

@ -1,32 +0,0 @@
clr-tree-node button {
white-space: nowrap;
}
.card-block {
height: 100%;
padding: 0;
}
.no-table-selected {
position: relative;
height: 100%;
}
::ng-deep {
// .badge.badge-info {
// background: #6a9235!important;
// color: #fff;
// }
clr-icon.is-blue, clr-icon.is-info {
fill: #6a9235;
}
}
.spinner-wrapper-fullpage {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}

View File

@ -3,7 +3,7 @@
* This software is released under MIT license. * This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project. * The full license information can be found in LICENSE in the root directory of this project.
*/ */
import { Component, AfterContentInit } from '@angular/core' import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { globals } from '../_globals' import { globals } from '../_globals'
@ -18,7 +18,8 @@ import { LicenceService } from '../services/licence.service'
templateUrl: './home.component.html', templateUrl: './home.component.html',
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class HomeComponent implements AfterContentInit { export class HomeComponent implements AfterContentInit {
public treeNodeLibraries: Array<any> | null = null public treeNodeLibraries: Array<any> | null = null

View File

@ -1,51 +0,0 @@
:host {
height: calc(100% - 96px);
padding: 20px 20px;
}
.card {
margin-top: 0;
}
.key-error {
font-size: 16px;
}
.misskey {
color: #E74C3C;
}
.license-key-form, .activation-key-form {
padding: 0;
.clr-control-container {
width: 100%;
textarea {
width: 100%;
height: 170px;
max-height: 170px;
min-height: 170px;
resize: none;
}
}
}
.apply-keys {
height: 40px;
}
.drop-area {
display: flex;
justify-content: center;
align-items: center;
padding: 15px;
border: 2px dashed #b2b2b2;
border-radius: 4px;
cursor: pointer;
margin: 10px 0;
}
clr-tabs button {
box-shadow: none !important
}

View File

@ -1,7 +1,8 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { AppService, LicenceService, SasService } from '../services' import { AppService, LicenceService, SasService } from '../services'
import { LicenseKeyData } from '../models/LicenseKeyData' import { LicenseKeyData } from '../models/LicenseKeyData'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
enum LicenseActions { enum LicenseActions {
key = 'key', key = 'key',
@ -13,7 +14,8 @@ enum LicenseActions {
@Component({ @Component({
selector: 'app-licensing', selector: 'app-licensing',
templateUrl: './licensing.component.html', templateUrl: './licensing.component.html',
styleUrls: ['./licensing.component.scss'] styleUrls: ['./licensing.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class LicensingComponent implements OnInit { export class LicensingComponent implements OnInit {
public action: LicenseActions | null = null public action: LicenseActions | null = null
@ -116,8 +118,12 @@ export class LicensingComponent implements OnInit {
this.sasService this.sasService
.request('admin/registerkey', table) .request('admin/registerkey', table)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
if (res.return && res.return[0] && res.return[0].MSG === 'SUCCESS') { if (
res.adapterResponse.return &&
res.adapterResponse.return[0] &&
res.adapterResponse.return[0].MSG === 'SUCCESS'
) {
location.replace(location.href.split('#')[0]) location.replace(location.href.split('#')[0])
} }
}) })

View File

@ -1,85 +0,0 @@
@import '../../colors.scss';
.toggle-switch input[type=checkbox]:checked+label:before {
border-color: $headerBackground;
background-color: $headerBackground !important;
transition: .15s ease-in;
transition-property: border-color,background-color;
}
#graph{
height: calc(100vh - 195px);
overflow: hidden;
text-align: center;
display: block;
width: 100%;
border: 1px solid #e4e4e4;
margin-top: 10px;
}
.selection-wrapper {
width: 100%;
max-width: 670px;
}
.column-active {
background: #d8e3e9;
color: black;
}
.content-area {
padding: 0.5rem !important;
.card {
min-height: calc(100vh - 120px);
.card-block {
padding: 0.5rem 0.35rem !important;
}
}
}
clr-tree-node button {
white-space: nowrap;
}
.btn-group.direction {
margin-left: var(--cds-global-space-6);
}
.graph-render-spinner {
position: absolute;
top: 0;
width: 100%;
display: flex;
justify-content: center;
margin-top: 10px;
}
.biglineage-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-footer {
p {
margin: 0;
}
}
.lineage-title-wrapper {
left: 12px;
}
.max-depth-input {
width: 100%;
}
@media (max-width: 768px) {
.toggle-switch-container {
margin-bottom: 20px;
}
}

View File

@ -1,4 +1,4 @@
import { Component } from '@angular/core' import { Component, ViewEncapsulation } from '@angular/core'
import { Location } from '@angular/common' import { Location } from '@angular/common'
import { globals } from '../_globals' import { globals } from '../_globals'
import * as d3Viz from 'd3-graphviz' import * as d3Viz from 'd3-graphviz'
@ -9,6 +9,7 @@ import { SasService } from '../services/sas.service'
import * as saveSvg from 'save-svg-as-png' import * as saveSvg from 'save-svg-as-png'
import { LoggerService } from '../services/logger.service' import { LoggerService } from '../services/logger.service'
import { LicenceService } from '../services/licence.service' import { LicenceService } from '../services/licence.service'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
const moment = require('moment') const moment = require('moment')
@Component({ @Component({
@ -17,7 +18,8 @@ const moment = require('moment')
templateUrl: './lineage.component.html', templateUrl: './lineage.component.html',
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class LineageComponent { export class LineageComponent {
public switchFlag: boolean = false public switchFlag: boolean = false
@ -115,8 +117,8 @@ export class LineageComponent {
await this.sasService await this.sasService
.request('lineage/getmetacols', libTable) .request('lineage/getmetacols', libTable)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
this.columnsList = res.metacols this.columnsList = res.adapterResponse.metacols
if (this.columnsList && this.columnsList.length > 0) { if (this.columnsList && this.columnsList.length > 0) {
// this.column = this.columnsList[0]['COLURI'] // this.column = this.columnsList[0]['COLURI']
@ -174,8 +176,8 @@ export class LineageComponent {
let libTable = { SASControlTable: [{ liburi: $event }] } let libTable = { SASControlTable: [{ liburi: $event }] }
await this.sasService await this.sasService
.request('lineage/getmetatables', libTable) .request('lineage/getmetatables', libTable)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
this.tablesList = res.metatables this.tablesList = res.adapterResponse.metatables
if (this.tablesList && this.tablesList.length > 0) { if (this.tablesList && this.tablesList.length > 0) {
library['tables'] = this.tablesList library['tables'] = this.tablesList
@ -295,8 +297,8 @@ export class LineageComponent {
} else { } else {
await this.sasService await this.sasService
.request('public/viewlibs', null) .request('public/viewlibs', null)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
this.libraryList = res.saslibs this.libraryList = res.adapterResponse.saslibs
this.helperService.displayLibraries(this.libraryList) this.helperService.displayLibraries(this.libraryList)
if (this.libraryList) { if (this.libraryList) {
@ -402,8 +404,8 @@ export class LineageComponent {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
this.sasService this.sasService
.request('lineage/fetchtablelineage', libTable) .request('lineage/fetchtablelineage', libTable)
.then(async (res: any) => { .then(async (res: RequestWrapperResponse) => {
if (res.flatdata.length > 0) { if (res.adapterResponse.flatdata.length > 0) {
if (this.licenceService.checkLineageLimit()) { if (this.licenceService.checkLineageLimit()) {
this.eventService.showInfoModal( this.eventService.showInfoModal(
'Notice', 'Notice',
@ -421,20 +423,22 @@ export class LineageComponent {
} }
this.lineageTableName = this.lineageTableName =
res.info[0].LIBREF + '.' + res.info[0].TABLENAME res.adapterResponse.info[0].LIBREF +
'.' +
res.adapterResponse.info[0].TABLENAME
let dotArray = res.finalfinal let dotArray = res.adapterResponse.finalfinal
let vizTmp: string = '' let vizTmp: string = ''
for (let i = 0; i < dotArray.length; i++) { for (let i = 0; i < dotArray.length; i++) {
vizTmp += unescape(dotArray[i].LINE) + '\n' vizTmp += unescape(dotArray[i].LINE) + '\n'
} }
this.flatdata = res.flatdata this.flatdata = res.adapterResponse.flatdata
if (this.libraryList) { if (this.libraryList) {
let libraryToSelect = this.libraryList.find((library: any) => let libraryToSelect = this.libraryList.find((library: any) =>
res.info[0].LIBURI.toUpperCase().includes( res.adapterResponse.info[0].LIBURI.toUpperCase().includes(
library.LIBRARYID.toUpperCase() library.LIBRARYID.toUpperCase()
) )
) )
@ -450,7 +454,7 @@ export class LineageComponent {
if (libraryToSelect['tables']) { if (libraryToSelect['tables']) {
tableToSelect = libraryToSelect['tables'].find((table: any) => tableToSelect = libraryToSelect['tables'].find((table: any) =>
table.TABLEURI.toUpperCase().includes( table.TABLEURI.toUpperCase().includes(
res.info[0].TABLEID.toUpperCase() res.adapterResponse.info[0].TABLEID.toUpperCase()
) )
) )
@ -495,10 +499,10 @@ export class LineageComponent {
.replace(/\sds:/g, '\nds:') .replace(/\sds:/g, '\nds:')
.replace(/\s\n/g, '\n') .replace(/\s\n/g, '\n')
this.idlookup = res.idlookup this.idlookup = res.adapterResponse.idlookup
if (res.finalfinal.length > this.largeDotFileLimit) { if (res.adapterResponse.finalfinal.length > this.largeDotFileLimit) {
this.largeDotFileLines = res.finalfinal.length this.largeDotFileLines = res.adapterResponse.finalfinal.length
} else { } else {
this.buildGraph() this.buildGraph()
} }
@ -619,8 +623,8 @@ export class LineageComponent {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
this.sasService this.sasService
.request('lineage/fetchcollineage', libTable) .request('lineage/fetchcollineage', libTable)
.then(async (res: any) => { .then(async (res: RequestWrapperResponse) => {
if (res.flatdata.length > 0) { if (res.adapterResponse.flatdata.length > 0) {
if (this.licenceService.checkLineageLimit()) { if (this.licenceService.checkLineageLimit()) {
this.eventService.showInfoModal( this.eventService.showInfoModal(
'Notice', 'Notice',
@ -631,18 +635,21 @@ export class LineageComponent {
} }
} }
if (typeof res === 'string') { if (typeof res.adapterResponse === 'string') {
this.vizInput = 'digraph G {SAS Error}' this.vizInput = 'digraph G {SAS Error}'
this.buildGraph() this.buildGraph()
return return
} }
this.lineageTableName = res.info[0].LIBREF + '.' + res.info[0].TABNAME this.lineageTableName =
this.lineageColumnName = res.info[0].COLNAME res.adapterResponse.info[0].LIBREF +
'.' +
res.adapterResponse.info[0].TABNAME
this.lineageColumnName = res.adapterResponse.info[0].COLNAME
this.idlookup = res.idlookup this.idlookup = res.adapterResponse.idlookup
let dotArray = res.fromsas let dotArray = res.adapterResponse.fromsas
let vizTmp: string = '' let vizTmp: string = ''
for (let i = 0; i < dotArray.length; i++) { for (let i = 0; i < dotArray.length; i++) {
vizTmp += unescape(dotArray[i].STRING) + '\n' vizTmp += unescape(dotArray[i].STRING) + '\n'
@ -653,11 +660,11 @@ export class LineageComponent {
.replace(/\sds:/g, '\nds:') .replace(/\sds:/g, '\nds:')
.replace(/\s\n/g, '\n') .replace(/\s\n/g, '\n')
this.flatdata = res.flatdata this.flatdata = res.adapterResponse.flatdata
if (this.libraryList) { if (this.libraryList) {
let libraryToSelect = this.libraryList.find((library: any) => let libraryToSelect = this.libraryList.find((library: any) =>
res.info[0]?.LIBURI?.toUpperCase()?.includes( res.adapterResponse.info[0]?.LIBURI?.toUpperCase()?.includes(
library?.LIBRARYID?.toUpperCase() library?.LIBRARYID?.toUpperCase()
) )
) )
@ -672,7 +679,8 @@ export class LineageComponent {
if (libraryToSelect['tables']) { if (libraryToSelect['tables']) {
tableToSelect = libraryToSelect['tables'].find( tableToSelect = libraryToSelect['tables'].find(
(table: any) => table.TABLEURI === res.info[0].TABURI (table: any) =>
table.TABLEURI === res.adapterResponse.info[0].TABURI
) )
if (tableToSelect) { if (tableToSelect) {
@ -714,8 +722,8 @@ export class LineageComponent {
} }
} }
if (res.fromsas.length > this.largeDotFileLimit) { if (res.adapterResponse.fromsas.length > this.largeDotFileLimit) {
this.largeDotFileLines = res.fromsas.length this.largeDotFileLines = res.adapterResponse.fromsas.length
} else { } else {
this.buildGraph() this.buildGraph()
} }

View File

@ -1,82 +0,0 @@
::ng-deep body[cds-theme="dark"] {
.object-header:hover {
background-color: #405560;
}
}
::ng-deep body[cds-theme="light"] {
.objects-col {
background: white;
}
.object-header:hover {
background-color: #d8e3e9;
}
}
.objects-col{
height: 75vh;
overflow: scroll;
border: 1px solid #cccccc;
border-radius: 4px;
}
.cols-head {
border: 1px solid #cccccc;
padding: 10px;
display: flex;
}
.object-text {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-left: 10px;
flex: 1;
}
.repo-dropdown{
margin-right: 15px;
margin-left: 15px;
margin-bottom: 10px;
}
.clr-accordion-title{
width: 100%;
}
.float-right{
margin: 0px;
float: right;
}
.full-width{
width: 100%;
}
.object-uri{
margin: 0px;
margin-top: 5px;
}
.object-header{
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 3px;
padding-right: 3px;
}
.object-header:hover{
border-radius: 3px;
}
.datagrid-host{
display: unset !important;
}
.card {
margin-top: 0;
flex: 1;
display: flex;
flex-direction: column;
}
.content-area {
padding: 0.5rem !important;
display: flex;
flex-direction: column;
}

View File

@ -1,5 +1,5 @@
import { Location } from '@angular/common' import { Location } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { ClrDatagridStringFilterInterface } from '@clr/angular' import { ClrDatagridStringFilterInterface } from '@clr/angular'
import { Observable, of } from 'rxjs' import { Observable, of } from 'rxjs'
@ -9,6 +9,7 @@ import { SasService } from '../services/sas.service'
import { globals } from '../_globals' import { globals } from '../_globals'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
interface MetaData { interface MetaData {
NAME: any NAME: any
@ -49,7 +50,8 @@ class ValueFilter implements ClrDatagridStringFilterInterface<MetaData> {
styleUrls: ['./metadata.component.scss'], styleUrls: ['./metadata.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class MetadataComponent implements OnInit { export class MetadataComponent implements OnInit {
metaDataList: Array<any> | undefined metaDataList: Array<any> | undefined
@ -109,44 +111,52 @@ export class MetadataComponent implements OnInit {
this.metatypesLoading = false this.metatypesLoading = false
this.metaDataSearch = globals.metadata.metaDataSearch this.metaDataSearch = globals.metadata.metaDataSearch
} else { } else {
this.sasService.request('metanav/metatypes', null).then((res: any) => { this.sasService
this.metaDataList = res.types .request('metanav/metatypes', null)
globals.metadata.metaDataList = this.metaDataList .then((res: RequestWrapperResponse) => {
this.loading = false this.metaDataList = res.adapterResponse.types
this.metatypesLoading = false globals.metadata.metaDataList = this.metaDataList
}) this.loading = false
this.metatypesLoading = false
})
this.sasService.request('metanav/metarepos', null).then((res: any) => { this.sasService
let foundation = false .request('metanav/metarepos', null)
this.repositories = [] .then((res: RequestWrapperResponse) => {
for (let index = 0; index < res.outrepos.length; index++) { let foundation = false
this.repositories.push(res.outrepos[index].NAME) this.repositories = []
if (res.outrepos[index].NAME === 'Foundation') { for (
foundation = true let index = 0;
index < res.adapterResponse.outrepos.length;
index++
) {
this.repositories.push(res.adapterResponse.outrepos[index].NAME)
if (res.adapterResponse.outrepos[index].NAME === 'Foundation') {
foundation = true
}
} }
} if (foundation) {
if (foundation) { this.repository = 'Foundation'
this.repository = 'Foundation' } else {
} else { this.repository = res.adapterResponse.outrepos[0].NAME
this.repository = res.outrepos[0].NAME }
} globals.metadata.metaRepositories = this.repositories
globals.metadata.metaRepositories = this.repositories globals.metadata.selectedRepository = this.repository
globals.metadata.selectedRepository = this.repository if (this.objectRoute) {
if (this.objectRoute) { this.eventService.closeSidebar()
this.eventService.closeSidebar() this.showData = true
this.showData = true let name = ''
let name = '' let id = this.route.snapshot.params['objectID']
let id = this.route.snapshot.params['objectID'] // let temp = this.router.url.split("%20").join(" ").split("/").reverse();
// let temp = this.router.url.split("%20").join(" ").split("/").reverse(); this.metaObjectList = []
this.metaObjectList = [] this.metaObjectList.push({ ID: id, NAME: name })
this.metaObjectList.push({ ID: id, NAME: name }) this.metaObjectShowList = this.metaObjectList
this.metaObjectShowList = this.metaObjectList this.metaObjectOnClick(
this.metaObjectOnClick( this.metaObjectShowList[0].ID,
this.metaObjectShowList[0].ID, this.metaObjectShowList[0]
this.metaObjectShowList[0] )
) }
} })
})
} }
} }
@ -183,56 +193,64 @@ export class MetadataComponent implements OnInit {
const data: any = { const data: any = {
SASControlTable: [{ metatype: $event, repo: this.repository }] SASControlTable: [{ metatype: $event, repo: this.repository }]
} }
this.sasService.request('metanav/metaobjects', data).then((res: any) => { this.sasService
this.metaObjectList = res.objects .request('metanav/metaobjects', data)
this.getMetaObjectAttributes(this.metaObjectSize) .then((res: RequestWrapperResponse) => {
this.loading = false this.metaObjectList = res.adapterResponse.objects
this.assoTypeSelected = $event this.getMetaObjectAttributes(this.metaObjectSize)
this.eventService.closeSidebar() this.loading = false
this.showData = true this.assoTypeSelected = $event
}) this.eventService.closeSidebar()
this.showData = true
})
} }
public async selectmetaObject($event: any, metaData?: any) { public async selectmetaObject($event: any, metaData?: any) {
let data: any = { let data: any = {
SASControlTable: [{ objecturi: $event }] SASControlTable: [{ objecturi: $event }]
} }
this.sasService.request('metanav/metadetails', data).then((res: any) => { this.sasService
this.metaObjectAssociations = res.associations .request('metanav/metadetails', data)
this.root$ = of(this.getAssosiationsCount(res.associations)) .then((res: RequestWrapperResponse) => {
this.showAcc = true this.metaObjectAssociations = res.adapterResponse.associations
this.showTable = true this.root$ = of(
let metaObjectName = res.attributes.find( this.getAssosiationsCount(res.adapterResponse.associations)
(x: any) => x.NAME === 'Name'
).VALUE
this.assoObjectSelected = metaObjectName
metaData.NAME = metaObjectName
let url = this.router.url
if (this.objectRoute) {
// this.location.replaceState(url.slice(0, url.lastIndexOf("object")) + "object/" + $event.slice(1 + $event.indexOf("\\")) + "/" + escape(metaData.NAME));
this.location.replaceState(
url.slice(0, url.lastIndexOf('object')) +
'object/' +
$event.slice(1 + $event.indexOf('\\'))
) )
} else { this.showAcc = true
// this.location.replaceState(url + "/object/" + $event.slice(1 + $event.indexOf("\\")) + "/" + escape(metaData.NAME)); this.showTable = true
this.location.replaceState( let metaObjectName = res.adapterResponse.attributes.find(
url + '/object/' + $event.slice(1 + $event.indexOf('\\')) (x: any) => x.NAME === 'Name'
) ).VALUE
} this.assoObjectSelected = metaObjectName
this.metaObjectAttributes = res.attributes metaData.NAME = metaObjectName
}) let url = this.router.url
if (this.objectRoute) {
// this.location.replaceState(url.slice(0, url.lastIndexOf("object")) + "object/" + $event.slice(1 + $event.indexOf("\\")) + "/" + escape(metaData.NAME));
this.location.replaceState(
url.slice(0, url.lastIndexOf('object')) +
'object/' +
$event.slice(1 + $event.indexOf('\\'))
)
} else {
// this.location.replaceState(url + "/object/" + $event.slice(1 + $event.indexOf("\\")) + "/" + escape(metaData.NAME));
this.location.replaceState(
url + '/object/' + $event.slice(1 + $event.indexOf('\\'))
)
}
this.metaObjectAttributes = res.adapterResponse.attributes
})
} }
public async selectAssosiationsDetails($event: any, metaData?: any) { public async selectAssosiationsDetails($event: any, metaData?: any) {
let data: any = { let data: any = {
SASControlTable: [{ objecturi: $event }] SASControlTable: [{ objecturi: $event }]
} }
this.sasService.request('metanav/metadetails', data).then((res: any) => { this.sasService
this.metaObjectAttributes = res.attributes .request('metanav/metadetails', data)
this.showTable = true .then((res: RequestWrapperResponse) => {
}) this.metaObjectAttributes = res.adapterResponse.attributes
this.showTable = true
})
} }
public getAssosiationsCount(assosiationList: Array<any>) { public getAssosiationsCount(assosiationList: Array<any>) {
@ -244,7 +262,7 @@ export class MetadataComponent implements OnInit {
details: [] details: []
}) })
} }
let assocObj = assosiationsHash.get(assosiation.ASSOC) let assocObj: any = assosiationsHash.get(assosiation.ASSOC)
assocObj.count++ assocObj.count++
assocObj.details.push({ assocObj.details.push({
ASSOCURI: assosiation.ASSOCURI, ASSOCURI: assosiation.ASSOCURI,
@ -254,7 +272,7 @@ export class MetadataComponent implements OnInit {
}) })
} }
let assocGrouped: Array<any> = [] let assocGrouped: Array<any> = []
assosiationsHash.forEach(function (val, key) { assosiationsHash.forEach(function (val: any, key) {
assocGrouped.push({ assocGrouped.push({
ASSOC: key, ASSOC: key,
count: val.count, count: val.count,
@ -294,9 +312,9 @@ export class MetadataComponent implements OnInit {
} }
return this.sasService return this.sasService
.request('metanav/metadetails', data) .request('metanav/metadetails', data)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
this.showTable = true this.showTable = true
this.metaObjectAttributes = res.attributes this.metaObjectAttributes = res.adapterResponse.attributes
this.assoObjectSelected = asso.NAME this.assoObjectSelected = asso.NAME
let url = this.router.url let url = this.router.url
if (this.objectRoute) { if (this.objectRoute) {
@ -314,7 +332,7 @@ export class MetadataComponent implements OnInit {
asso.ASSOCURI.slice(1 + asso.ASSOCURI.indexOf('\\')) asso.ASSOCURI.slice(1 + asso.ASSOCURI.indexOf('\\'))
) )
} }
return this.getAssosiationsCount(res.associations) return this.getAssosiationsCount(res.adapterResponse.associations)
}) })
} }

View File

@ -0,0 +1,29 @@
import { DcValidator } from '../shared/dc-validator/dc-validator'
import { FileUploadEncoding } from './FileUploadEncoding'
import { FileUploader } from './FileUploader.class'
import { ExcelRule } from './TableData'
import XLSX from 'xlsx'
export interface ParseParams {
file: File
password?: string
dcValidator: DcValidator
/**
* If workbook is provided, parse function will not run a XLSX.read()
* it will use this property instead. So the client must do a file read beforehand
*/
workbook?: XLSX.WorkBook
/**
* Parse function will manipulate and return the uploader array which can be provided with files already in the queue
* Otherwise new empty instance will be created.
*/
uploader?: FileUploader
headerPks: string[]
headerArray: string[]
headerShow: string[]
timeHeaders: string[]
dateHeaders: string[]
dateTimeHeaders: string[]
xlRules: ExcelRule[]
encoding?: FileUploadEncoding
}

View File

@ -0,0 +1,15 @@
import { FileUploader } from './FileUploader.class'
import FoundRangeInfo from './RangeInfo'
export interface ParseResult {
/**
* In case of CSV file, won't be returned
*/
data?: any[]
/**
* In case of CSV file, won't be returned
*/
headerShow?: string[]
rangeSheetRes?: FoundRangeInfo
uploader: FileUploader
}

View File

@ -0,0 +1,13 @@
export default interface FoundRangeInfo {
found: boolean
sheetName: string
rangeStartAddress: string
rangeEndAddress: string
rangeAddress: string
missingHeaders: MissingHeaders[]
}
export interface MissingHeaders {
sheetName: string
missingHeaders: string[]
}

View File

@ -0,0 +1,13 @@
import { MissingHeaders } from './RangeInfo'
export interface SearchDataExcelResult {
missing?: MissingHeaders[]
found?: {
data: any
arrayData: any[]
sheetName: string
headers: string[]
startAddress?: string
endAddress?: string
}
}

View File

@ -0,0 +1,4 @@
export interface UploadFileResponse {
adapterResponse: any
log?: string
}

View File

@ -0,0 +1,4 @@
export interface RequestWrapperResponse<responseType = any> {
adapterResponse: responseType
log?: string
}

View File

@ -22,8 +22,8 @@
/> />
</div> </div>
<ng-container *ngIf="parsedDatasets.length && !submittedDatasets.length"> <ng-container *ngIf="parsedDatasets.length && !submittedCsvDatasets.length">
<div class="text-center mb-10"> <div *ngIf="!excelsSubmitted" class="text-center mb-10">
<button (click)="onDiscard()" class="btn btn-danger btn-sm mr-10"> <button (click)="onDiscard()" class="btn btn-danger btn-sm mr-10">
Discard Discard
</button> </button>
@ -41,30 +41,54 @@
<clr-tree-node *ngFor="let dataset of parsedDatasets"> <clr-tree-node *ngFor="let dataset of parsedDatasets">
<button <button
(click)="onParsedDatasetClick(dataset)" (click)="onParsedDatasetClick(dataset)"
class="clr-treenode-link whitespace-nowrap" class="clr-treenode-link whitespace-nowrap d-flex clr-align-items-center"
[class.active]="dataset.active" [class.active]="dataset.active"
> >
<cds-icon <ng-container *ngIf="dataset.submitResult">
*ngIf="!(dataset.datasource && dataset.parseResult)" <cds-icon
status="danger" *ngIf="dataset.submitResult.error"
shape="exclamation-circle" status="danger"
></cds-icon> shape="exclamation-circle"
<cds-icon ></cds-icon>
*ngIf="dataset.datasource && dataset.parseResult" <cds-icon
shape="table" *ngIf="dataset.submitResult.success"
></cds-icon> status="success"
{{ dataset.libds }} shape="check-circle"
></cds-icon>
</ng-container>
<ng-container *ngIf="!dataset.submitResult">
<ng-container *ngIf="dataset.datasource">
<cds-icon
*ngIf="!(dataset.datasource.length && dataset.parseResult)"
status="danger"
shape="exclamation-circle"
></cds-icon>
<cds-icon
*ngIf="dataset.datasource.length && dataset.parseResult"
shape="table"
></cds-icon>
</ng-container>
<ng-container *ngIf="!dataset.datasource">
<cds-icon *ngIf="!dataset.parsingTable" shape="table"></cds-icon>
<clr-spinner *ngIf="dataset.parsingTable" clrSmall></clr-spinner>
</ng-container>
</ng-container>
<span class="ml-5"> {{ dataset.libds }} </span>
</button> </button>
</clr-tree-node> </clr-tree-node>
</clr-tree> </clr-tree>
</ng-container> </ng-container>
<ng-container *ngIf="submittedDatasets.length"> <ng-container *ngIf="submittedCsvDatasets.length">
<p cds-text="caption" class="ml-10 mb-10 mt-10">Submitted tables:</p> <p cds-text="caption" class="ml-10 mb-10 mt-10">Submitted tables:</p>
<clr-tree> <clr-tree>
<clr-tree-node *ngFor="let dataset of submittedDatasets"> <clr-tree-node *ngFor="let dataset of submittedCsvDatasets">
<button <button
(click)="onSubmittedDatasetClick(dataset)" (click)="onSubmittedCsvDatasetClick(dataset)"
class="clr-treenode-link whitespace-nowrap" class="clr-treenode-link whitespace-nowrap"
[class.active]="dataset.active" [class.active]="dataset.active"
> >
@ -90,7 +114,7 @@
</div> --> </div> -->
</app-sidebar> </app-sidebar>
<div class="content-area"> <div #contentArea class="content-area">
<div class="card no-borders h-100 d-flex clr-flex-column"> <div class="card no-borders h-100 d-flex clr-flex-column">
<div <div
class="header-row clr-row justify-content-between clr-justify-content-center w-100 m-0" class="header-row clr-row justify-content-between clr-justify-content-center w-100 m-0"
@ -103,7 +127,7 @@
class="no-table-selected pointer-events-none" class="no-table-selected pointer-events-none"
> >
<clr-icon <clr-icon
shape="warning-standard" shape="upload-cloud"
size="40" size="40"
class="is-info icon-dc-fill" class="is-info icon-dc-fill"
></clr-icon> ></clr-icon>
@ -116,7 +140,7 @@
<ng-container *ngIf="!parsedDatasets.length && selectedFile !== null"> <ng-container *ngIf="!parsedDatasets.length && selectedFile !== null">
<div class="d-flex clr-justify-content-center mt-15"> <div class="d-flex clr-justify-content-center mt-15">
<div class="dataset-input-wrapper"> <div class="dataset-input-wrapper">
<p cds-text="secondary regular" class="mb-20"> <p cds-text="secondary regular" class="mb-5">
Selected file: <strong>{{ selectedFile.name }}</strong> Selected file: <strong>{{ selectedFile.name }}</strong>
<clr-tooltip> <clr-tooltip>
<cds-icon <cds-icon
@ -129,6 +153,9 @@
<clr-tooltip-content> Discard the file </clr-tooltip-content> <clr-tooltip-content> Discard the file </clr-tooltip-content>
</clr-tooltip> </clr-tooltip>
</p> </p>
<p cds-text="secondary regular" class="mb-20">
File size: <strong>{{ selectedFile.sizeMB }} MB</strong>
</p>
<p cds-text="secondary regular" class="mb-15"> <p cds-text="secondary regular" class="mb-15">
Paste or type the list of datasets to upload: Paste or type the list of datasets to upload:
</p> </p>
@ -151,7 +178,7 @@
<div class="dataset-selection-actions text-right mt-10"> <div class="dataset-selection-actions text-right mt-10">
<button <button
(click)="onUploadFile()" (click)="onStartParsingFile()"
id="continue-btn" id="continue-btn"
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
[disabled]="!matchedDatasets.length" [disabled]="!matchedDatasets.length"
@ -164,23 +191,100 @@
</div> </div>
</ng-container> </ng-container>
<ng-container *ngIf="parsedDatasets.length && !submittedDatasets.length"> <ng-container
*ngIf="parsedDatasets.length && !submittedCsvDatasets.length"
>
<div <div
*ngIf="!activeParsedDataset" *ngIf="!activeParsedDataset"
class="no-table-selected pointer-events-none" class="no-table-selected pointer-events-none"
> >
<clr-icon <ng-container *ngIf="fileLoadingState !== FileLoadingState.parsed">
shape="warning-standard" <clr-icon
size="40" shape="process-on-vm"
class="is-info icon-dc-fill" size="40"
></clr-icon> class="is-info icon-dc-fill"
<p class="text-center color-gray mt-10" cds-text="section"> ></clr-icon>
Please select a dataset on the left to review the data
</p> <p class="text-center color-gray mt-10" cds-text="section">
{{ fileLoadingState }}...
</p>
</ng-container>
<ng-container *ngIf="fileLoadingState === FileLoadingState.parsed">
<clr-icon
shape="warning-standard"
size="40"
class="is-info icon-dc-fill"
></clr-icon>
<p class="text-center color-gray mt-10" cds-text="section">
Please select a dataset on the left to review the data
</p>
</ng-container>
</div> </div>
<ng-container *ngIf="activeParsedDataset"> <ng-container *ngIf="activeParsedDataset">
<div class="d-flex clr-justify-content-between p-10"> <div
*ngIf="activeParsedDataset.submitResult"
class="d-flex clr-justify-content-between p-10 mt-15 submission-results"
>
<div>
<p cds-text="secondary regular" class="mb-10">
Submit Status:
<span
*ngIf="activeParsedDataset.submitResult?.success"
class="color-green"
><strong>SUCCESS</strong></span
>
<span
*ngIf="activeParsedDataset.submitResult?.error"
class="color-red"
><strong>ERROR</strong></span
>
</p>
<p
*ngIf="activeParsedDataset.submitResult?.error"
cds-text="secondary regular"
>
Error details:
</p>
</div>
<div>
<button
*ngIf="
!submittingCsv && activeParsedDataset.submitResult?.error
"
(click)="reSubmitTable(activeParsedDataset)"
class="btn btn-primary mt-10"
[clrLoading]="submitLoading"
>
Resubmit
</button>
<button
(click)="
downloadFile(
activeParsedDataset.submitResult.log ||
activeParsedDataset.submitResult.success ||
activeParsedDataset.submitResult.error
)
"
class="btn btn-primary-outline mt-10"
>
Download log
</button>
</div>
</div>
<div
*ngIf="activeParsedDataset.submitResult?.error"
class="error-field mt-15"
>
<div class="log-wrapper">
{{ activeParsedDataset.submitResult?.error | json }}
</div>
</div>
<div class="d-flex clr-justify-content-between p-10 mt-15">
<div> <div>
<p cds-text="secondary regular" class="mb-10"> <p cds-text="secondary regular" class="mb-10">
Found in range: Found in range:
@ -197,7 +301,17 @@
</ng-container> </ng-container>
<ng-container *ngIf="!activeParsedDataset.parseResult"> <ng-container *ngIf="!activeParsedDataset.parseResult">
<strong>No data found</strong> <strong *ngIf="!activeParsedDataset.parsingTable"
>No data found</strong
>
<span
*ngIf="activeParsedDataset.parsingTable"
class="d-flex clr-align-items-center"
>
<strong>Searching for the data...</strong>
<clr-spinner class="ml-5" clrSmall></clr-spinner>
</span>
</ng-container> </ng-container>
</p> </p>
<p cds-text="secondary regular"> <p cds-text="secondary regular">
@ -262,9 +376,9 @@
</ng-container> </ng-container>
</ng-container> </ng-container>
<ng-container *ngIf="submittedDatasets.length"> <ng-container *ngIf="submittedCsvDatasets.length">
<div <div
*ngIf="!activeSubmittedDataset" *ngIf="!activeSubmittedCsvDataset"
class="no-table-selected pointer-events-none" class="no-table-selected pointer-events-none"
> >
<clr-icon <clr-icon
@ -278,31 +392,17 @@
</div> </div>
</ng-container> </ng-container>
<ng-container *ngIf="activeSubmittedDataset"> <ng-container *ngIf="activeSubmittedCsvDataset">
<div class="d-flex clr-justify-content-between p-10"> <div class="d-flex clr-justify-content-between p-10">
<div> <div>
<p
*ngIf="activeSubmittedDataset.parseResult"
cds-text="secondary regular"
class="mb-10"
>
Found in range:
<strong
>"{{
activeSubmittedDataset.parseResult.rangeSheetRes?.sheetName
}}"!{{
activeSubmittedDataset.parseResult.rangeSheetRes?.rangeAddress
}}</strong
>
</p>
<p cds-text="secondary regular" class="mb-10"> <p cds-text="secondary regular" class="mb-10">
Matched with dataset: Matched with dataset:
<strong> <strong>
<clr-tooltip> <clr-tooltip>
<a <a
clrTooltipTrigger clrTooltipTrigger
[routerLink]="'/editor/' + activeSubmittedDataset.libds" [routerLink]="'/editor/' + activeSubmittedCsvDataset.libds"
>{{ activeSubmittedDataset.libds }}</a >{{ activeSubmittedCsvDataset.libds }}</a
> >
<clr-tooltip-content <clr-tooltip-content
[clrPosition]="'top-right'" [clrPosition]="'top-right'"
@ -315,15 +415,17 @@
</p> </p>
<p cds-text="secondary regular" class="mb-10"> <p cds-text="secondary regular" class="mb-10">
Status: Status:
<span *ngIf="activeSubmittedDataset.success" class="color-green" <span
*ngIf="activeSubmittedCsvDataset.success"
class="color-green"
><strong>SUCCESS</strong></span ><strong>SUCCESS</strong></span
> >
<span *ngIf="activeSubmittedDataset.error" class="color-red" <span *ngIf="activeSubmittedCsvDataset.error" class="color-red"
><strong>ERROR</strong></span ><strong>ERROR</strong></span
> >
</p> </p>
<p <p
*ngIf="activeSubmittedDataset.error" *ngIf="activeSubmittedCsvDataset.error"
cds-text="secondary regular" cds-text="secondary regular"
> >
Error details: Error details:
@ -331,18 +433,11 @@
</div> </div>
<div> <div>
<button
*ngIf="!submittingCsv && activeSubmittedDataset.error"
(click)="reSubmitTable(activeSubmittedDataset)"
class="btn btn-primary mt-10"
[clrLoading]="submitLoading"
>
Resubmit
</button>
<button <button
(click)=" (click)="
downloadFile( downloadFile(
activeSubmittedDataset.success || activeSubmittedDataset.error activeSubmittedCsvDataset.success ||
activeSubmittedCsvDataset.error
) )
" "
class="btn btn-primary-outline mt-10" class="btn btn-primary-outline mt-10"
@ -352,9 +447,9 @@
</div> </div>
</div> </div>
<div *ngIf="activeSubmittedDataset.error" class="error-field mt-15"> <div *ngIf="activeSubmittedCsvDataset.error" class="error-field mt-15">
<div class="log-wrapper"> <div class="log-wrapper">
{{ activeSubmittedDataset.error | json }} {{ activeSubmittedCsvDataset.error | json }}
</div> </div>
</div> </div>
</ng-container> </ng-container>

View File

@ -1,50 +0,0 @@
.no-table-selected {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
position: absolute;
background: var(--clr-vertical-nav-bg-color);
z-index: 10;
width: 100%;
height: 100%;
top: 0;
}
.header-row {
padding: 15px 0;
border-bottom: 1px solid #d3d3d3;
}
.dataset-input-wrapper {
max-width: 500px;
width: 100%;
textarea {
min-height: 200px;
height: 200px;
}
}
.submit-reason {
min-height: 70px;
max-height: 70px;
height: 70px;
}
.log-wrapper {
margin: 0 10px;
height: auto;
}
::ng-deep td.not-matched {
background-color: #ff000054;
}
.dataset-selection-actions {
border-top: 1px solid #d3d3d3;
}
.licence-limit-notice {
color: var(--cds-alias-status-warning-dark);
}

View File

@ -1,8 +1,11 @@
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ElementRef,
HostBinding, HostBinding,
OnInit OnInit,
ViewChild,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { import {
EventService, EventService,
@ -11,17 +14,13 @@ import {
SasService, SasService,
SasStoreService SasStoreService
} from '../services' } from '../services'
import * as XLSX from '@sheet/crypto'
import { globals } from '../_globals' import { globals } from '../_globals'
import { EditorsGetDataServiceResponse } from '../models/sas/editors-getdata.model' import { EditorsGetDataServiceResponse } from '../models/sas/editors-getdata.model'
import { DcValidator } from '../shared/dc-validator/dc-validator' import { DcValidator } from '../shared/dc-validator/dc-validator'
import { ExcelRule } from '../models/TableData' import { ExcelRule } from '../models/TableData'
import { HotTableInterface } from '../models/HotTable.interface' import { HotTableInterface } from '../models/HotTable.interface'
import { Col } from '../shared/dc-validator/models/col.model' import { Col } from '../shared/dc-validator/models/col.model'
import { import { SpreadsheetService } from '../services/spreadsheet.service'
ParseResult,
SpreadsheetService
} from '../services/spreadsheet.service'
import Handsontable from 'handsontable' import Handsontable from 'handsontable'
import { HotTableRegisterer } from '@handsontable/angular' import { HotTableRegisterer } from '@handsontable/angular'
import { EditorsStageDataSASResponse } from '../models/sas/editors-stagedata.model' import { EditorsStageDataSASResponse } from '../models/sas/editors-stagedata.model'
@ -29,18 +28,39 @@ import { CellChange, ChangeSource } from 'handsontable/common'
import { baseAfterGetColHeader } from '../shared/utils/hot.utils' import { baseAfterGetColHeader } from '../shared/utils/hot.utils'
import { ColumnSettings } from 'handsontable/settings' import { ColumnSettings } from 'handsontable/settings'
import { UploadFile } from '@sasjs/adapter' import { UploadFile } from '@sasjs/adapter'
import { UploadFileResponse } from '../models/UploadFile'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
import { ParseResult } from '../models/ParseResult.interface'
import XLSX from 'xlsx'
enum FileLoadingState {
reading = 'Reading the file',
parsing = 'Searching for the data in the file',
parsed = 'Searching for the data finished',
/**
* Defualt value
*/
notSelected = 'File not selected'
}
@Component({ @Component({
selector: 'app-multi-dataset', selector: 'app-multi-dataset',
templateUrl: './multi-dataset.component.html', templateUrl: './multi-dataset.component.html',
styleUrls: ['./multi-dataset.component.scss'] styleUrls: ['./multi-dataset.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class MultiDatasetComponent implements OnInit { export class MultiDatasetComponent implements OnInit {
@HostBinding('class.content-container') contentContainerClass = true @HostBinding('class.content-container') contentContainerClass = true
@ViewChild('contentArea', { static: true }) contentAreaRef!: ElementRef
public licenceState = this.licenceService.licenceState public licenceState = this.licenceService.licenceState
public Infinity = Infinity public Infinity = Infinity
public workbookInterval: any
public fileLoadingState: FileLoadingState = FileLoadingState.notSelected
public FileLoadingState = FileLoadingState
public hotTableLicenseKey: string | undefined = undefined public hotTableLicenseKey: string | undefined = undefined
public hotTableMaxRows = public hotTableMaxRows =
this.licenceState.value.viewer_rows_allowed || Infinity this.licenceState.value.viewer_rows_allowed || Infinity
@ -48,15 +68,19 @@ export class MultiDatasetComponent implements OnInit {
public csvFiles: UploadFile[] = [] public csvFiles: UploadFile[] = []
public csvSubmitting: boolean = false public csvSubmitting: boolean = false
public selectedFile: File | null = null public autoDetectingColumns: boolean = false
public selectedFile: SelectedFile | null = null
public parsedDatasets: ParsedDataset[] = [] public parsedDatasets: ParsedDataset[] = []
public submittedDatasets: SubmittedDatasetResult[] = [] public submittedCsvDatasets: SubmittedCsvDatasetResult[] = []
public datasetsLoading: boolean = false public datasetsLoading: boolean = false
public uploadLoading: boolean = false public uploadLoading: boolean = false
public submitLoading: boolean = false public submitLoading: boolean = false
public matchedDatasets: string[] = [] public matchedDatasets: string[] = []
public sheetNames: string[] = []
public userInputDatasets: string = '' public userInputDatasets: string = ''
public libsAndTables: { public libsAndTables: {
@ -157,7 +181,7 @@ export class MultiDatasetComponent implements OnInit {
} }
} }
onFileChange(event: any) { async onFileChange(event: any) {
const files = event?.target?.files || [] const files = event?.target?.files || []
if (files.length < 1) { if (files.length < 1) {
@ -194,6 +218,20 @@ export class MultiDatasetComponent implements OnInit {
// For EXCEL if multiple files, we only take one (the first one) // For EXCEL if multiple files, we only take one (the first one)
this.selectedFile = event.target.files[0] this.selectedFile = event.target.files[0]
if (this.selectedFile) {
this.fileLoadingState = FileLoadingState.reading
this.selectedFile.sizeMB = this.spreadsheetService.bytesToMB(
this.selectedFile.size
)
// Read the excel file to be ready
this.spreadsheetService.xlsxReadFile(this.selectedFile!).then((wb) => {
this.fileLoadingState = FileLoadingState.parsing
this.selectedFile!.workbook = wb
})
}
this.initUserInputHot() this.initUserInputHot()
this.onAutoDetectColumns() this.onAutoDetectColumns()
} else if (matchedExtension === 'csv') { } else if (matchedExtension === 'csv') {
@ -246,7 +284,7 @@ export class MultiDatasetComponent implements OnInit {
this.userInputDatasets = '' this.userInputDatasets = ''
} }
async onUploadFile() { async onStartParsingFile() {
this.uploadLoading = true this.uploadLoading = true
const datasetFetchingPromises: Promise< const datasetFetchingPromises: Promise<
@ -271,58 +309,65 @@ export class MultiDatasetComponent implements OnInit {
const datasetObjects = this.buildDatasetsObjects(datasets) const datasetObjects = this.buildDatasetsObjects(datasets)
for (let datasetObject of datasetObjects) { datasetObjects.forEach((datasetObject) => {
this.spreadsheetService this.parsedDatasets.push({
.parseExcelFile({ libds: datasetObject.libds,
file: this.selectedFile!, includeInSubmission: true,
dcValidator: datasetObject.dcValidator!, datasetInfo: datasetObject,
headerPks: datasetObject.headerPks, parsingTable: true
headerArray: datasetObject.headerArray, })
headerShow: [], })
timeHeaders: datasetObject.timeHeaders,
dateHeaders: datasetObject.dateHeaders,
dateTimeHeaders: datasetObject.dateTimeHeaders,
xlRules: datasetObject.xlRules
})
.then((parseResult: ParseResult | undefined) => {
console.log('parseResult', parseResult)
if (parseResult && parseResult.data) { this.workbookLoaded().then((workbook) => {
let datasource: any[] = [] for (let parsedDataset of this.parsedDatasets) {
this.spreadsheetService
.parseExcelFile({
file: this.selectedFile!,
workbook: workbook,
password: this.selectedFile!.password || undefined,
dcValidator: parsedDataset.datasetInfo.dcValidator!,
headerPks: parsedDataset.datasetInfo.headerPks,
headerArray: parsedDataset.datasetInfo.headerArray,
headerShow: [],
timeHeaders: parsedDataset.datasetInfo.timeHeaders,
dateHeaders: parsedDataset.datasetInfo.dateHeaders,
dateTimeHeaders: parsedDataset.datasetInfo.dateTimeHeaders,
xlRules: parsedDataset.datasetInfo.xlRules
})
.then((parseResult: ParseResult | undefined) => {
this.fileLoadingState = FileLoadingState.parsed
parseResult.data.map((item) => { if (parseResult && parseResult.data) {
let itemObject: any = {} let datasource: any[] = []
parseResult.headerShow!.map((header: any, index: number) => { parseResult.data.map((item) => {
itemObject[header] = item[index] let itemObject: any = {}
parseResult.headerShow!.map((header: any, index: number) => {
itemObject[header] = item[index]
})
// If Delete? column is not set in the file, we set it to NO
if (!itemObject['_____DELETE__THIS__RECORD_____'])
itemObject['_____DELETE__THIS__RECORD_____'] = 'No'
datasource.push(itemObject)
}) })
// If Delete? column is not set in the file, we set it to NO parsedDataset.datasource = datasource
if (!itemObject['_____DELETE__THIS__RECORD_____']) parsedDataset.parseResult = parseResult
itemObject['_____DELETE__THIS__RECORD_____'] = 'No' parsedDataset.parsingTable = false
}
datasource.push(itemObject)
})
this.parsedDatasets.push({
libds: datasetObject.libds,
parseResult: parseResult,
includeInSubmission: true,
datasetInfo: datasetObject,
datasource: datasource
})
}
})
.catch((error: string) => {
console.warn('Parsing excel file error.', error)
this.parsedDatasets.push({
libds: datasetObject.libds,
includeInSubmission: false,
datasetInfo: datasetObject
}) })
}) .catch((error: string) => {
} console.warn('Parsing excel file error.', error)
parsedDataset.datasource = []
parsedDataset.includeInSubmission = false
parsedDataset.parsingTable = false
})
}
})
}) })
} }
@ -349,13 +394,17 @@ export class MultiDatasetComponent implements OnInit {
setTimeout(() => { setTimeout(() => {
this.hotInstance = this.hotRegisterer.getInstance('hotInstance') this.hotInstance = this.hotRegisterer.getInstance('hotInstance')
// Set height of parsed data to full height of the page content area
const contentAreaHeight = this.contentAreaRef.nativeElement.clientHeight
const hotHeight = `${contentAreaHeight - 160}px`
if (this.activeParsedDataset) { if (this.activeParsedDataset) {
this.hotInstance.updateSettings({ this.hotInstance.updateSettings({
data: this.activeParsedDataset.datasource || [], data: this.activeParsedDataset.datasource || [],
colHeaders: this.activeParsedDataset.datasetInfo.headerColumns, colHeaders: this.activeParsedDataset.datasetInfo.headerColumns,
columns: this.activeParsedDataset.datasetInfo.dcValidator?.getRules(), columns: this.activeParsedDataset.datasetInfo.dcValidator?.getRules(),
readOnly: true, readOnly: true,
height: '300px', height: hotHeight || '300px',
className: 'htDark' className: 'htDark'
}) })
} }
@ -507,13 +556,32 @@ export class MultiDatasetComponent implements OnInit {
* convention. {@link isValidDatasetFormat} * convention. {@link isValidDatasetFormat}
*/ */
async onAutoDetectColumns() { async onAutoDetectColumns() {
const sheetNames = await this.parseExcelSheetNames() let passwordError = false
if (sheetNames) { await this.parseExcelSheetNames()
.then((sheetNames) => {
this.sheetNames = sheetNames
})
.catch((err) => {
if (err.includes('password')) {
passwordError = true
}
})
if (passwordError) {
this.onDiscardFile()
this.eventService.showInfoModal(
'Locked file',
'We failed to unlock the file.'
)
return
}
if (this.sheetNames) {
this.matchedDatasets = [] this.matchedDatasets = []
this.userInputDatasets = '' this.userInputDatasets = ''
sheetNames.forEach((sheetName: string, index: number) => { this.sheetNames.forEach((sheetName: string, index: number) => {
const trimmedSheetname = sheetName.trim() const trimmedSheetname = sheetName.trim()
if ( if (
@ -561,8 +629,8 @@ export class MultiDatasetComponent implements OnInit {
this.initHot() this.initHot()
} }
onSubmittedDatasetClick(submittedDataset: SubmittedDatasetResult) { onSubmittedCsvDatasetClick(submittedDataset: SubmittedCsvDatasetResult) {
this.deselectAllSubmittedDatasets() this.deselectAllSubmittedCsvDatasets()
submittedDataset.active = true submittedDataset.active = true
} }
@ -571,8 +639,10 @@ export class MultiDatasetComponent implements OnInit {
return this.parsedDatasets.find((dataset) => dataset.active) return this.parsedDatasets.find((dataset) => dataset.active)
} }
public get activeSubmittedDataset(): SubmittedDatasetResult | undefined { public get activeSubmittedCsvDataset():
return this.submittedDatasets.find((dataset) => dataset.active) | SubmittedCsvDatasetResult
| undefined {
return this.submittedCsvDatasets.find((dataset) => dataset.active)
} }
public get notFoundDatasets(): string[] { public get notFoundDatasets(): string[] {
@ -606,8 +676,12 @@ export class MultiDatasetComponent implements OnInit {
return this.csvFiles.length > 0 return this.csvFiles.length > 0
} }
public get excelsSubmitted(): boolean {
return !!this.parsedDatasets.filter((ds) => ds.submitResult).length
}
public downloadFile(response: any) { public downloadFile(response: any) {
const filename = `stagedata-${this.activeSubmittedDataset?.libds}-log` const filename = `stagedata-${this.activeSubmittedCsvDataset?.libds}-log`
this.helperService.downloadTextFile(filename, JSON.stringify(response)) this.helperService.downloadTextFile(filename, JSON.stringify(response))
} }
@ -615,7 +689,7 @@ export class MultiDatasetComponent implements OnInit {
* Submits attached CSVs which are matched with existing datasets * Submits attached CSVs which are matched with existing datasets
*/ */
async submitCsvFiles() { async submitCsvFiles() {
let requestsResults: SubmittedDatasetResult[] = [] let requestsResults: SubmittedCsvDatasetResult[] = []
for (let file of this.csvFiles) { for (let file of this.csvFiles) {
const libds = this.parseDatasetFromCsvName(file.fileName) const libds = this.parseDatasetFromCsvName(file.fileName)
@ -626,17 +700,17 @@ export class MultiDatasetComponent implements OnInit {
await this.sasService await this.sasService
.uploadFile('services/editors/loadfile', [file], { table: libds }) .uploadFile('services/editors/loadfile', [file], { table: libds })
.then( .then(
(res: any) => { (res: UploadFileResponse) => {
if (typeof res.sasjsAbort !== 'undefined') { if (typeof res.adapterResponse.sasjsAbort !== 'undefined') {
error = res.sasjsAbort error = res.adapterResponse.sasjsAbort
} else { } else {
success = res success = res.adapterResponse
} }
}, },
(err: any) => { (err: any) => {
console.error('err', err) console.error('err', err)
error = err error = err.adapterResponse
} }
) )
@ -647,7 +721,7 @@ export class MultiDatasetComponent implements OnInit {
}) })
} }
this.submittedDatasets = requestsResults this.submittedCsvDatasets = requestsResults
} }
/** /**
@ -678,7 +752,7 @@ export class MultiDatasetComponent implements OnInit {
} }
/** /**
* Sends tables to the SAS sequentially * Sends tables found in an excel to the SAS sequentially
* *
* @param explicitDatasets if empty all datasets will be sent, otherwise only datasets * @param explicitDatasets if empty all datasets will be sent, otherwise only datasets
* in the array will be sent. eg. ['lib1.table_1', 'lib1.table_2'] * in the array will be sent. eg. ['lib1.table_1', 'lib1.table_2']
@ -688,10 +762,6 @@ export class MultiDatasetComponent implements OnInit {
this.submitLoading = true this.submitLoading = true
let requestsResults: SubmittedDatasetResult[] = explicitDatasets
? this.submittedDatasets
: []
for (let table of this.parsedDatasets) { for (let table of this.parsedDatasets) {
// Skip the table if no data inside // Skip the table if no data inside
if (!table.parseResult || !table.datasource) continue if (!table.parseResult || !table.datasource) continue
@ -733,6 +803,7 @@ export class MultiDatasetComponent implements OnInit {
let error let error
let success let success
let log
await this.sasStoreService await this.sasStoreService
.updateTable( .updateTable(
@ -741,59 +812,67 @@ export class MultiDatasetComponent implements OnInit {
'SASControlTable', 'SASControlTable',
'editors/stagedata', 'editors/stagedata',
table.datasetInfo.data.$sasdata, table.datasetInfo.data.$sasdata,
true true,
{
debug: true
}
) )
.then((res: EditorsStageDataSASResponse) => { .then((res: RequestWrapperResponse<EditorsStageDataSASResponse>) => {
success = res success = res.adapterResponse
log = res.log
}) })
.catch((err: any) => { .catch((err: any) => {
console.error('err', err) console.error('err', err)
error = err error = err.adapterResponse
log = err.log
}) })
const requestResult = { const requestResult = {
success, success,
error, error,
log,
parseResult: table.parseResult, parseResult: table.parseResult,
libds: table.libds libds: table.libds
} }
// If explicit datasets are set don't just push to th array table.submitResult = requestResult
// instead replace if result already exist from before (this might be re-submit)
if (explicitDatasets) {
const existingResultIndex = requestsResults.findIndex(
(result) => result.libds === table.libds
)
if (existingResultIndex > -1) {
requestsResults[existingResultIndex] = requestResult
} else {
requestsResults.push(requestResult)
}
} else {
requestsResults.push(requestResult)
}
} }
} }
this.submittedDatasets = requestsResults
this.showSubmitReasonModal = false this.showSubmitReasonModal = false
this.submitLoading = false this.submitLoading = false
this.deselectAllParsedDatasets() this.deselectAllParsedDatasets()
} }
async reSubmitTable(activeSubmittedDataset: SubmittedDatasetResult) { async reSubmitTable(activeSubmittedDataset: ParsedDataset) {
// Submit only particular table // Submit only particular table
await this.submitTables([activeSubmittedDataset.libds]) await this.submitTables([activeSubmittedDataset.libds])
// Activate new resubmitted table // Activate new resubmitted table
const newSubmittedDataset = this.submittedDatasets.find( const newSubmittedDataset = this.parsedDatasets.find(
(sd) => sd.libds === activeSubmittedDataset.libds (sd) => sd.libds === activeSubmittedDataset.libds
) )
if (newSubmittedDataset) newSubmittedDataset.active = true if (newSubmittedDataset) newSubmittedDataset.active = true
} }
/**
*
* @returns Promise once workbook is loaded because use XLSX.read in the background
*/
private workbookLoaded(): Promise<XLSX.WorkBook> {
return new Promise((resolve, reject) => {
if (!this.selectedFile) reject('No file selected')
this.workbookInterval = setInterval(() => {
if (this.selectedFile!.workbook) {
clearInterval(this.workbookInterval)
resolve(this.selectedFile!.workbook)
}
}, 500)
})
}
private parseDatasetFromCsvName(fileName: string) { private parseDatasetFromCsvName(fileName: string) {
const fileNameArr = fileName.split('.') const fileNameArr = fileName.split('.')
fileNameArr.pop() fileNameArr.pop()
@ -815,40 +894,26 @@ export class MultiDatasetComponent implements OnInit {
.map((row) => (row ? `${row[0]}.${row[1]}` : '')) .map((row) => (row ? `${row[0]}.${row[1]}` : ''))
} }
private parseExcelSheetNames(): Promise<string[]> { /**
* Read the file minimally just to get the sheet names, not reading full file
* to help boost the performance
*
* @returns sheet names in string array
*/
private async parseExcelSheetNames(): Promise<string[]> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader() if (!this.selectedFile) return resolve([])
if (!this.selectedFile) { this.spreadsheetService
console.warn('selectedFile is missing') .parseExcelSheetNames(this.selectedFile)
return resolve([]) .then((parsed) => {
} if (parsed.password) this.selectedFile!.password = parsed.password
reader.onload = (event: ProgressEvent<FileReader>) => { return resolve(parsed.sheetNames)
if (!event?.target) { })
console.warn('File reader event.target is missing') .catch((err) => {
return return reject(err)
}
const data = event.target.result
const workbook = XLSX.read(data, {
type: 'binary'
}) })
try {
const sheet_names_list = workbook.SheetNames
return resolve(sheet_names_list)
} catch (e) {
console.error(e)
}
}
reader.onerror = function (ex) {
console.log(ex)
}
reader.readAsBinaryString(this.selectedFile)
}) })
} }
@ -977,8 +1042,8 @@ export class MultiDatasetComponent implements OnInit {
} }
} }
private deselectAllSubmittedDatasets() { private deselectAllSubmittedCsvDatasets() {
for (let submittedDataset of this.submittedDatasets) { for (let submittedDataset of this.submittedCsvDatasets) {
submittedDataset.active = false submittedDataset.active = false
} }
} }
@ -1002,16 +1067,29 @@ export interface ParsedDataset {
libds: string libds: string
parseResult?: ParseResult parseResult?: ParseResult
datasetInfo: DatasetsObject datasetInfo: DatasetsObject
submitResult?: SubmittedDatasetResult
datasource?: any[] datasource?: any[]
includeInSubmission: boolean includeInSubmission: boolean
status?: 'success' | 'error' status?: 'success' | 'error'
active?: boolean active?: boolean
parsingTable?: boolean
} }
export interface SubmittedDatasetResult { export interface SubmittedDatasetResult {
success: EditorsStageDataSASResponse | undefined
error: any
log?: string
}
export interface SubmittedCsvDatasetResult {
libds: string libds: string
success: EditorsStageDataSASResponse | undefined success: EditorsStageDataSASResponse | undefined
error: any error: any
parseResult?: ParseResult
active?: boolean active?: boolean
} }
export interface SelectedFile extends File {
sizeMB?: number
password?: string
workbook?: XLSX.WorkBook
}

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-not-found', selector: 'app-not-found',
@ -6,7 +6,8 @@ import { Component, OnInit } from '@angular/core'
styleUrls: ['./not-found.component.scss'], styleUrls: ['./not-found.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class NotFoundComponent implements OnInit { export class NotFoundComponent implements OnInit {
constructor() {} constructor() {}

View File

@ -1,307 +0,0 @@
::ng-deep {
body[cds-theme="dark"] {
.clause-logic {
background: #192a30;
}
.clause-query {
background: #263e48;
}
}
body[cds-theme="light"] {
.clause-logic {
background: #e9e9e9;
}
.clause-query {
background: #fbf8f8;
}
}
}
.content {
display: flex;
.clauses-container {
display: flex;
flex-direction: column;
.clause-logic {
display: flex;
justify-content: center;
flex-direction: column;
padding: 15px;
}
.clause-query {
padding: 30px 0px 20px 20px;
display: flex;
justify-content: center;
flex-direction: column;
position: relative;
& > .clr-row {
justify-content: space-between;
&:not(:last-child) {
padding-bottom: 15px;
margin-bottom: 15px;
border-bottom: 1px solid rgba(0, 0, 0, 0.16)
}
}
.remove-group-clause-button {
position: absolute;
top: 0px;
right: 10px;
color: gray;
}
.variable-col {
display: flex;
align-items: flex-start;
padding-bottom: 1px;
.datalist-wrapper {
width: 100%;
input {
width: 100%;
}
}
}
.operator-col {
display: flex;
align-items: flex-start;
clr-select-container {
height: 45px;
margin-top: 0;
width: 100%;
}
}
.value-col {
display: flex;
align-items: flex-start;
padding-bottom: 1px;
.checkbox-vals {
width: 100%;
padding: 0px 5px;
border-bottom: 1px solid rgba(0, 0, 0, 0.3);
clr-checkbox-container {
margin-top: 0;
}
section {
max-height: 120px;
overflow-y: scroll;
}
}
.single-field-vals {
width: 100%;
::ng-deep {
.clr-control-container {
width: 100%;
.clr-input-wrapper {
max-width: none;
.clr-input-group {
width: 100%;
}
}
}
}
& > input {
width: 100%;
}
input[type=time] {
width: 100%;
padding-right: 17px;
}
}
.range-vals {
width: 100%;
::ng-deep {
.clr-control-container {
width: 100%;
.clr-input-wrapper {
max-width: none;
.clr-input-group {
width: 100%;
}
}
}
}
.from {
margin-bottom: 10px;
& > input {
width: 100%;
}
input[type=time] {
width: 100%;
padding-right: 17px;
}
}
.from, .to {
min-width: 100px;
& > input {
width: 100%;
}
input[type=time] {
width: 100%;
padding-right: 17px;
}
}
}
.contains-vals {
width: 100%;
::ng-deep {
.clr-control-container {
width: 100%;
.clr-input-wrapper {
max-width: none;
.clr-input-group {
width: 100%;
}
}
}
}
& > input {
width: 100%;
}
input[type=time] {
width: 100%;
padding-right: 17px;
}
}
}
.clause-buttons {
display: flex;
justify-content: space-around;
align-items: center;
flex-direction: row;
align-items: center;
button {
min-width: auto;
}
}
}
}
}
.invalid-clause {
border-left: 2px solid #d94b31;
}
.clause-row {
clr-icon {
margin: 0;
}
}
.clause-row:after {
position: relative;
content: "";
height: .41667rem;
width: .41667rem;
top: .29167rem;
right: .25rem;
background-image: url(data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org…%2C9.84%2C3.24a0.68%2C0.68%2C0%2C1%2C1%2C1%2C1Z%22%2F%3E%0A%3C%2Fsvg%3E%0A);
background-repeat: no-repeat;
background-size: contain;
vertical-align: middle;
margin: 0;
}
::ng-deep body[cds-theme="dark"] {
.line-numbers {
border-color: #989797 !important;
}
}
pre[class*="language-"] {
padding: 8px;
margin: 0;
border-radius: 1px;
display: flex;
justify-content: center;
align-items: center;
min-height: 66px;
position: relative;
span.spinner {
position: absolute;
left: 10px;
top: 10px;
}
code {
white-space: pre-wrap;
word-break: break-word;
}
}
.input-val {
border: 0px;
background: #fbf8f8;
border-bottom: 1px solid #999999;
}
clr-date-container {
margin-top: 2px !important;
}
input[type="time"] {
border: 0;
background: transparent;
border-bottom: 1px solid #b3b3b3;
&:focus {
outline: none;
}
}
.in-values-modal {
.modal-footer {
border-top: 1px solid #d8d8d8;
margin-top: 10px;
}
}
.progress, .progress-static {
background-color: transparent;
width: 100%;
height: 4px;
top: 3px;
}

View File

@ -6,7 +6,8 @@ import {
OnDestroy, OnDestroy,
ChangeDetectorRef, ChangeDetectorRef,
LOCALE_ID, LOCALE_ID,
Input Input,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { SasStoreService } from '../services/sas-store.service' import { SasStoreService } from '../services/sas-store.service'
import { globals } from '../_globals' import { globals } from '../_globals'
@ -27,7 +28,8 @@ registerLocaleData(localeEnGB)
selector: 'app-query', selector: 'app-query',
templateUrl: './query.component.html', templateUrl: './query.component.html',
styleUrls: ['./query.component.scss'], styleUrls: ['./query.component.scss'],
providers: [{ provide: LOCALE_ID, useValue: 'en-GB' }] providers: [{ provide: LOCALE_ID, useValue: 'en-GB' }],
encapsulation: ViewEncapsulation.None
}) })
export class QueryComponent export class QueryComponent
implements AfterViewInit, AfterContentInit, OnDestroy implements AfterViewInit, AfterContentInit, OnDestroy

View File

@ -1,210 +0,0 @@
@import '../../../colors.scss';
.loader {
display:flex;
justify-content: center;
height:75vh;
align-items:center;
flex-direction:column
}
.modalLarge {
width: 50rem!important;
}
.addedRow {
border: 1px solid rgba(9, 77, 117, 0.2);
border-radius: 5px;
}
.deletedRow {
border: 1px solid rgba(70, 71, 70, 0.2);
border-radius: 5px;
}
::ng-deep body[cds-theme="dark"] {
table {
.updatedRow {
background: #93971e;
}
.addedRow {
background: rgb(86 153 95);
}
.deletedRow {
background: rgb(138 90 90);
}
}
}
::ng-deep body[cds-theme="light"] {
table {
.updatedRow {
background: #fafda8;
}
.addedRow {
background: rgb(146, 208, 154);
}
.deletedRow {
background: rgb(230, 179, 179);
}
}
}
.updatedRow {
border: 1px solid rgba(9, 117, 9, 0.2);
border-radius: 5px;
}
.table {
border: 0px solid;
}
.ch {
background: rgba(0,0,0,.1);
border: 1px solid rgba(104, 100, 0, 0.4);
border-radius: 5px;
}
.ch:hover {
background: rgba(252, 135, 120, 0.4);
}
.tooltip .tooltip-content.tooltip-top-right, .tooltip.tooltip-top-right>.tooltip-content, .tooltip>.tooltip-content {
font-size: .54167rem;
font-weight: 400;
letter-spacing: normal;
background: $headerBackground;
border-radius: .125rem;
color: #f0f1ec;;
line-height: .75rem;
margin: 0;
padding: .375rem .5rem;
width: 235px;
position: absolute;
top: auto;
bottom: 100%;
left: 12px;
right: auto;
border-bottom-left-radius: 0;
margin-bottom: .66667rem;
}
.tooltip .tooltip-content.tooltip-top-right:before, .tooltip.tooltip-top-right>.tooltip-content:before, .tooltip>.tooltip-content:before {
position: absolute;
bottom: -.375rem;
left: 0;
top: auto;
right: auto;
content: "";
border-left: .25rem solid $headerBackground;
border-top: .20833rem solid $headerBackground;
border-right: .25rem solid transparent;
border-bottom: .20833rem solid transparent;
}
.table {
border: 0px solid;
}
.toggle-switch input[type=checkbox]:checked+label:before {
border-color: $headerBackground;
background-color: $headerBackground !important;
transition: .15s ease-in;
transition-property: border-color,background-color;
}
.tableCont {
overflow:auto;
margin: 15px 10px 10px 10px;
td {
word-break: break-word;
}
}
.approvalInfo {
display: flex;
justify-content: flex-end
}
.approvalBack {
display: flex;
justify-content: flex-start;
}
@media screen and (max-width:768px) {
.approvalInfo {
display: flex;
justify-content: center;
margin-top: 15px;
}
.approvalBack {
display: flex;
justify-content: center;
margin-bottom: 15px;
}
.card {
margin-top:0rem!important;
min-height: calc(100vh - 0px)!important;
}
.table td.left, .table th.left {
text-align: left;
width: 150px!important;
flex: 0
}
}
.table td.left, .table th.left {
text-align: left;
flex: 1;
width: 300px!important;
}
.tooll {
position: absolute;
background: #e6b3b3;
color: $headerBackground;
top: 0px;
height: 36px;
width: 100%;
left: 0px;
justify-content: center;
align-items: center;
display: flex;
}
#acceptBtn, #rejectBtn {
width: 175px
}
.formatted-values-toggle {
min-width: 75px
}
clr-modal {
::ng-deep {
.modal-body-wrapper {
overflow: auto;
}
}
}
.rows-notice {
display: flex;
align-items: center;
margin-right: 10px;
color: #6a6a6a;
font-size: 15px;
clr-icon {
margin: 0;
}
}

View File

@ -1,6 +1,11 @@
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { SasStoreService } from '../../services/sas-store.service' import { SasStoreService } from '../../services/sas-store.service'
import { Component, AfterViewInit, OnDestroy } from '@angular/core' import {
Component,
AfterViewInit,
OnDestroy,
ViewEncapsulation
} from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { EventService } from '../../services/event.service' import { EventService } from '../../services/event.service'
@ -22,7 +27,8 @@ interface ChangesObj {
styleUrls: ['./approve-details.component.scss'], styleUrls: ['./approve-details.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class ApproveDetailsComponent implements AfterViewInit, OnDestroy { export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
private _detailsSub: Subscription | undefined private _detailsSub: Subscription | undefined
@ -349,13 +355,12 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
this.params = param this.params = param
this.response = res this.response = res
this.calcDiff() this.calcDiff()
this.callChangesInfo(this.tableId)
}) })
.catch((err: any) => err) .catch((err: any) => err)
.finally(() => { .finally(() => {
this.loadingTable = true this.loadingTable = true
}) })
this.callChangesInfo(this.tableId)
} }
) )
if (typeof this.router.snapshot.params['tableId'] === 'undefined') { if (typeof this.router.snapshot.params['tableId'] === 'undefined') {
@ -378,6 +383,7 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
this.params = param this.params = param
this.response = res this.response = res
this.calcDiff() this.calcDiff()
this.callChangesInfo(this.tableId)
}) })
.catch((err: any) => { .catch((err: any) => {
this.acceptLoading = false this.acceptLoading = false
@ -386,8 +392,6 @@ export class ApproveDetailsComponent implements AfterViewInit, OnDestroy {
this.loadingTable = true this.loadingTable = true
this.setFocus() this.setFocus()
}) })
this.callChangesInfo(this.tableId)
} }
ngOnDestroy() { ngOnDestroy() {

View File

@ -89,6 +89,7 @@
<clr-dg-cell class="p-0 d-flex justify-content-center"> <clr-dg-cell class="p-0 d-flex justify-content-center">
<button <button
class="btn btn-success" class="btn btn-success"
aria-label="Download audit file"
[id]="approveItem.tableId" [id]="approveItem.tableId"
(click)=" (click)="
download(approveItem.tableId); $event.stopPropagation() download(approveItem.tableId); $event.stopPropagation()

View File

@ -1,43 +0,0 @@
@import '../../../colors.scss';
.column-center {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.datagrid .datagrid-column .datagrid-column-title{
outline: none!important;
}
.links {
font-weight: 700;cursor: pointer;
}
.tooltip.tooltip-bottom-left>.tooltip-content, .tooltip .tooltip-content.tooltip-bottom-left {
background: $headerBackground !important;
}
.tooltip.tooltip-bottom-left>.tooltip-content:before, .tooltip .tooltip-content.tooltip-bottom-left:before {
border-right: .25rem solid $headerBackground;
border-bottom: .20833rem solid $headerBackground;
}
.noBorder {
border-bottom: 1px solid transparent!important;
}
.approvals-list-wrapper {
height: 70vh;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
.noapprovals-info-wrapper {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
height: calc(100vh - 200px);
}

View File

@ -1,4 +1,9 @@
import { Component, OnInit, ChangeDetectorRef } from '@angular/core' import {
Component,
OnInit,
ChangeDetectorRef,
ViewEncapsulation
} from '@angular/core'
import { SasStoreService } from '../../services/sas-store.service' import { SasStoreService } from '../../services/sas-store.service'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { SasService } from '../../services/sas.service' import { SasService } from '../../services/sas.service'
@ -20,7 +25,8 @@ interface ApproveData {
styleUrls: ['./approve.component.scss'], styleUrls: ['./approve.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class ApproveComponent implements OnInit { export class ApproveComponent implements OnInit {
public approveList: Array<ApproveData> | undefined public approveList: Array<ApproveData> | undefined

View File

@ -24,19 +24,19 @@
<a <a
*ngIf="ind < 1" *ngIf="ind < 1"
(click)="getTable(approveData[col])" (click)="getTable(approveData[col])"
class="cursor-pointer" class="cursor-pointer table-link"
>{{ approveData[col] }}</a >{{ approveData[col] }}</a
> >
<div *ngIf="ind < 2 && ind >= 1"> <div *ngIf="ind < 2 && ind >= 1">
<a <a
(click)="getBaseTable(approveData[col])" (click)="getBaseTable(approveData[col])"
class="cursor-pointer" class="cursor-pointer table-link"
>VIEW</a >VIEW</a
> >
<span> / </span> <span> / </span>
<a <a
(click)="getEditTable(approveData[col])" (click)="getEditTable(approveData[col])"
class="cursor-pointer" class="cursor-pointer table-link"
>EDIT</a >EDIT</a
> >
</div> </div>
@ -47,7 +47,12 @@
</table> </table>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline" (click)="openModal = false"> <button
type="button"
aria-label="Close modal"
class="btn btn-outline"
(click)="openModal = false"
>
OK OK
</button> </button>
</div> </div>
@ -119,6 +124,7 @@
<clr-dg-cell class="verCenter">{{ historyItem.reviewed }}</clr-dg-cell> <clr-dg-cell class="verCenter">{{ historyItem.reviewed }}</clr-dg-cell>
<clr-dg-cell class="verCenter p-0 d-flex justify-content-center"> <clr-dg-cell class="verCenter p-0 d-flex justify-content-center">
<button <button
aria-label="Download audit file"
class="btn btn-success" class="btn btn-success"
(click)="download(historyItem.tableId); $event.stopPropagation()" (click)="download(historyItem.tableId); $event.stopPropagation()"
> >

View File

@ -1,37 +0,0 @@
.rejected {
color: #f83126;
font-weight: bold
}
.accepted {
color: #3fc424;
font-weight: bold
}
.hsCell {
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
padding: 7px;
}
.btCell {
display: flex !important;
justify-content: center !important;
}
.verCenter {
display: flex;
align-items: center;
word-break: break-all;
}
.load-more {
input {
width: 90px;
}
}
#noDataContainer {
height: calc(100vh - 200px);
}

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { SASjsConfig } from '@sasjs/adapter' import { SASjsConfig } from '@sasjs/adapter'
@ -15,7 +15,8 @@ import {
styleUrls: ['./history.component.scss'], styleUrls: ['./history.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class HistoryComponent implements OnInit { export class HistoryComponent implements OnInit {
public history: Array<any> = [] public history: Array<any> = []

View File

@ -64,7 +64,11 @@
<!-- <clr-dg-cell>{{sub.approver}}</clr-dg-cell> --> <!-- <clr-dg-cell>{{sub.approver}}</clr-dg-cell> -->
<clr-dg-cell>{{ sub.submitReason }}</clr-dg-cell> <clr-dg-cell>{{ sub.submitReason }}</clr-dg-cell>
<clr-dg-cell> <clr-dg-cell>
<div class="row justify-content-around" role="tooltip"> <div
class="row justify-content-around"
role="tooltip"
aria-label="Go to staged data screen"
>
<a <a
class="column-center links tooltip tooltip-md tooltip-bottom-left color-blue" class="column-center links tooltip tooltip-md tooltip-bottom-left color-blue"
(click)="goToStage(sub.tableId)" (click)="goToStage(sub.tableId)"
@ -79,6 +83,7 @@
<clr-dg-cell class="p-0 d-flex justify-content-center"> <clr-dg-cell class="p-0 d-flex justify-content-center">
<button <button
class="btn btn-success" class="btn btn-success"
aria-label="Download audit file for table record"
(click)="download(sub.tableId); $event.stopPropagation()" (click)="download(sub.tableId); $event.stopPropagation()"
> >
<clr-icon shape="download"></clr-icon> <clr-icon shape="download"></clr-icon>

View File

@ -1,17 +0,0 @@
@import '../../../colors.scss';
.noBorder {
border-bottom: 1px solid transparent!important;
}
.tooltip.tooltip-bottom-left>.tooltip-content, .tooltip .tooltip-content.tooltip-bottom-left {
background: $headerBackground !important;
}
.tooltip.tooltip-bottom-left>.tooltip-content:before, .tooltip .tooltip-content.tooltip-bottom-left:before {
border-right: .25rem solid $headerBackground;
border-bottom: .20833rem solid $headerBackground;
}
.no-submitted-tables {
height: calc(100vh - 200px);
}

View File

@ -1,4 +1,9 @@
import { Component, AfterViewInit, OnInit } from '@angular/core' import {
Component,
AfterViewInit,
OnInit,
ViewEncapsulation
} from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { SasStoreService, EventService, SasService } from '../../services' import { SasStoreService, EventService, SasService } from '../../services'
@ -17,7 +22,8 @@ interface SubmitterData {
styleUrls: ['./submitter.component.scss'], styleUrls: ['./submitter.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class SubmitterComponent implements OnInit, AfterViewInit { export class SubmitterComponent implements OnInit, AfterViewInit {
public remained: number = 0 public remained: number = 0

View File

@ -1,9 +1,10 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { globals } from '../_globals' import { globals } from '../_globals'
import { HelperService } from '../services/helper.service' import { HelperService } from '../services/helper.service'
import { Location } from '@angular/common' import { Location } from '@angular/common'
import { Router, ActivatedRoute } from '@angular/router' import { Router, ActivatedRoute } from '@angular/router'
import { SasService } from '../services/sas.service' import { SasService } from '../services/sas.service'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
@Component({ @Component({
selector: 'app-role', selector: 'app-role',
@ -11,7 +12,8 @@ import { SasService } from '../services/sas.service'
styleUrls: ['./role.component.scss'], styleUrls: ['./role.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class RoleComponent implements OnInit { export class RoleComponent implements OnInit {
public roles: Array<any> | undefined public roles: Array<any> | undefined
@ -47,61 +49,69 @@ export class RoleComponent implements OnInit {
} else { } else {
if (globals.usernav.roleList === undefined) { if (globals.usernav.roleList === undefined) {
this.loading = true this.loading = true
this.sasService.request('usernav/userroles', null).then((res: any) => { this.sasService
this.loading = false .request('usernav/userroles', null)
this.roles = res.roles .then((res: RequestWrapperResponse) => {
globals.usernav.roleList = res.roles this.loading = false
if (this.paramPresent) { this.roles = res.adapterResponse.roles
if (this.roles !== undefined) { globals.usernav.roleList = res.adapterResponse.roles
let validRole = this.findRole(this.roles, this.paramURI) if (this.paramPresent) {
if (validRole !== false) { if (this.roles !== undefined) {
this.loading = true let validRole = this.findRole(this.roles, this.paramURI)
let data = { iwant: [{ roleid: this.paramURI }] } if (validRole !== false) {
this.sasService this.loading = true
.request('usernav/usermembersbyrole', data) let data = { iwant: [{ roleid: this.paramURI }] }
.then((res: any) => { this.sasService
this.loading = false .request('usernav/usermembersbyrole', data)
this.roleMembers = res.sasmembers .then((res: RequestWrapperResponse) => {
this.roleMembersCount = res.sasmembers.length this.loading = false
this.roleGroups = res.sasgroups this.roleMembers = res.adapterResponse.sasmembers
this.roleGroupsCount = res.sasgroups.length this.roleMembersCount =
this.roleUri = validRole.ROLEURI res.adapterResponse.sasmembers.length
this.roleName = validRole.ROLENAME this.roleGroups = res.adapterResponse.sasgroups
this.roleDesc = validRole.ROLEDESC this.roleGroupsCount =
}) res.adapterResponse.sasgroups.length
this.roleUri = validRole.ROLEURI
this.roleName = validRole.ROLENAME
this.roleDesc = validRole.ROLEDESC
})
}
} }
} }
} })
})
} else { } else {
this.roles = globals.usernav.roleList this.roles = globals.usernav.roleList
this.roleSearch = globals.usernav.roleSearch this.roleSearch = globals.usernav.roleSearch
this.sasService.request('usernav/userroles', null).then((res: any) => { this.sasService
this.roles = res.roles .request('usernav/userroles', null)
globals.usernav.roleList = res.roles .then((res: RequestWrapperResponse) => {
this.roles = res.adapterResponse.roles
globals.usernav.roleList = res.adapterResponse.roles
if (this.paramPresent) { if (this.paramPresent) {
if (this.roles !== undefined) { if (this.roles !== undefined) {
let validRole = this.findRole(this.roles, this.paramURI) let validRole = this.findRole(this.roles, this.paramURI)
if (validRole !== false) { if (validRole !== false) {
this.loading = true this.loading = true
let data = { iwant: [{ roleid: this.paramURI }] } let data = { iwant: [{ roleid: this.paramURI }] }
this.sasService this.sasService
.request('usernav/usermembersbyrole', data) .request('usernav/usermembersbyrole', data)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
this.loading = false this.loading = false
this.roleMembers = res.sasmembers this.roleMembers = res.adapterResponse.sasmembers
this.roleMembersCount = res.sasmembers.length this.roleMembersCount =
this.roleGroups = res.sasgroups res.adapterResponse.sasmembers.length
this.roleGroupsCount = res.sasgroups.length this.roleGroups = res.adapterResponse.sasgroups
this.roleUri = validRole.ROLEURI this.roleGroupsCount =
this.roleName = validRole.ROLENAME res.adapterResponse.sasgroups.length
this.roleDesc = validRole.ROLEDESC this.roleUri = validRole.ROLEURI
}) this.roleName = validRole.ROLENAME
this.roleDesc = validRole.ROLEDESC
})
}
} }
} }
} })
})
} }
} }
} }
@ -125,12 +135,12 @@ export class RoleComponent implements OnInit {
let data = { iwant: [{ roleid: role.ROLEURI }] } let data = { iwant: [{ roleid: role.ROLEURI }] }
this.sasService this.sasService
.request('usernav/usermembersbyrole', data) .request('usernav/usermembersbyrole', data)
.then((res: any) => { .then((res: RequestWrapperResponse) => {
this.loading = false this.loading = false
this.roleMembers = res.sasmembers this.roleMembers = res.adapterResponse.sasmembers
this.roleMembersCount = res.sasmembers.length this.roleMembersCount = res.adapterResponse.sasmembers.length
this.roleGroups = res.sasgroups this.roleGroups = res.adapterResponse.sasgroups
this.roleGroupsCount = res.sasgroups.length this.roleGroupsCount = res.adapterResponse.sasgroups.length
this.roleUri = role.ROLEURI this.roleUri = role.ROLEURI
this.roleName = role.ROLENAME this.roleName = role.ROLENAME
this.roleDesc = role.ROLEDESC this.roleDesc = role.ROLEDESC

View File

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core' import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-home-route', selector: 'app-home-route',
@ -6,7 +6,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core'
styleUrls: ['./home-route.component.scss'], styleUrls: ['./home-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class HomeRouteComponent implements OnInit, OnDestroy { export class HomeRouteComponent implements OnInit, OnDestroy {
constructor() {} constructor() {}

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-review-route', selector: 'app-review-route',
@ -6,7 +6,8 @@ import { Component, OnInit } from '@angular/core'
styleUrls: ['./review-route.component.scss'], styleUrls: ['./review-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class ReviewRouteComponent implements OnInit { export class ReviewRouteComponent implements OnInit {
constructor() {} constructor() {}

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-usernav-route', selector: 'app-usernav-route',
@ -6,7 +6,8 @@ import { Component, OnInit } from '@angular/core'
styleUrls: ['./usernav-route.component.scss'], styleUrls: ['./usernav-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class UsernavRouteComponent implements OnInit { export class UsernavRouteComponent implements OnInit {
constructor() {} constructor() {}

View File

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core' import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-view-route', selector: 'app-view-route',
@ -6,7 +6,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core'
styleUrls: ['./view-route.component.scss'], styleUrls: ['./view-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class ViewRouteComponent implements OnInit, OnDestroy { export class ViewRouteComponent implements OnInit, OnDestroy {
constructor() {} constructor() {}

View File

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core' import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'app-xlmap-route', selector: 'app-xlmap-route',
@ -6,7 +6,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core'
styleUrls: ['./xlmap-route.component.scss'], styleUrls: ['./xlmap-route.component.scss'],
host: { host: {
class: 'content-container' class: 'content-container'
} },
encapsulation: ViewEncapsulation.None
}) })
export class XLMapRouteComponent implements OnInit, OnDestroy { export class XLMapRouteComponent implements OnInit, OnDestroy {
constructor() {} constructor() {}

View File

@ -9,11 +9,13 @@ import { EnvironmentInfo } from '../system/models/environment-info.model'
import { LicenceService } from './licence.service' import { LicenceService } from './licence.service'
import { AppSettingsService } from './app-settings.service' import { AppSettingsService } from './app-settings.service'
import { AppThemes } from '../models/AppSettings' import { AppThemes } from '../models/AppSettings'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
import { AppStoreService } from './app-store.service'
@Injectable() @Injectable()
export class AppService { export class AppService {
public syssite = new BehaviorSubject<string[] | null>(null) public syssite = new BehaviorSubject<string[] | null>(null)
private environmentInfo: EnvironmentInfo | null = null private environmentInfo: EnvironmentInfo = {}
constructor( constructor(
private licenceService: LicenceService, private licenceService: LicenceService,
@ -21,7 +23,8 @@ export class AppService {
private sasService: SasService, private sasService: SasService,
private loggerService: LoggerService, private loggerService: LoggerService,
private appSettingsService: AppSettingsService, private appSettingsService: AppSettingsService,
private router: Router private router: Router,
private appStoreService: AppStoreService
) { ) {
this.subscribe() this.subscribe()
@ -81,16 +84,19 @@ export class AppService {
await this.sasService await this.sasService
.request('public/startupservice', null) .request('public/startupservice', null)
.then(async (res: any) => { .then(async (res: RequestWrapperResponse) => {
this.syssite.next([res.SYSSITE]) this.syssite.next([res.adapterResponse.SYSSITE])
let missingProps: string[] = [] let missingProps: string[] = []
if (!res.globvars || (res.globvars && !res.globvars[0])) if (
!res.adapterResponse.globvars ||
(res.adapterResponse.globvars && !res.adapterResponse.globvars[0])
)
missingProps.push('Globvars') missingProps.push('Globvars')
if (!res.sasdatasets) missingProps.push('Sasdatasets') if (!res.adapterResponse.sasdatasets) missingProps.push('Sasdatasets')
if (!res.saslibs) missingProps.push('Saslibs') if (!res.adapterResponse.saslibs) missingProps.push('Saslibs')
if (!res.xlmaps) missingProps.push('XLMaps') if (!res.adapterResponse.xlmaps) missingProps.push('XLMaps')
if (missingProps.length > 0) { if (missingProps.length > 0) {
startupServiceError = true startupServiceError = true
@ -103,23 +109,26 @@ export class AppService {
return return
} }
const dcAdapterSettings = this.appStoreService.getDcAdapterSettings()
this.environmentInfo = { this.environmentInfo = {
SYSSITE: res.SYSSITE, SYSSITE: res.adapterResponse.SYSSITE,
SYSSCPL: res.SYSSCPL, SYSSCPL: res.adapterResponse.SYSSCPL,
SYSTCPIPHOSTNAME: res.SYSTCPIPHOSTNAME, SYSTCPIPHOSTNAME: res.adapterResponse.SYSTCPIPHOSTNAME,
SYSVLONG: res.SYSVLONG, SYSVLONG: res.adapterResponse.SYSVLONG,
MEMSIZE: res.MEMSIZE, MEMSIZE: res.adapterResponse.MEMSIZE,
SYSPROCESSMODE: res.SYSPROCESSMODE, SYSPROCESSMODE: res.adapterResponse.SYSPROCESSMODE,
SYSHOSTNAME: res.SYSHOSTNAME, SYSHOSTNAME: res.adapterResponse.SYSHOSTNAME,
SYSUSERID: res.SYSUSERID, SYSUSERID: res.adapterResponse.SYSUSERID,
SYSHOSTINFOLONG: res.SYSHOSTINFOLONG, SYSHOSTINFOLONG: res.adapterResponse.SYSHOSTINFOLONG,
SYSENCODING: res.SYSENCODING, SYSENCODING: res.adapterResponse.SYSENCODING,
AUTOEXEC: res.AUTOEXEC, AUTOEXEC: res.adapterResponse.AUTOEXEC,
ISADMIN: res.globvars[0].ISADMIN, ISADMIN: res.adapterResponse.globvars[0].ISADMIN,
DC_ADMIN_GROUP: res.globvars[0].DC_ADMIN_GROUP DC_ADMIN_GROUP: res.adapterResponse.globvars[0].DC_ADMIN_GROUP,
APP_LOC: dcAdapterSettings?.appLoc
} }
let libs = res.sasdatasets let libs = res.adapterResponse.sasdatasets
let libGroup: any = {} let libGroup: any = {}
let libsAndTables let libsAndTables
let libraries let libraries
@ -153,7 +162,7 @@ export class AppService {
globals.editor.libsAndTables = libsAndTables globals.editor.libsAndTables = libsAndTables
} }
globals.xlmaps = res.xlmaps.map((xlmap: any) => ({ globals.xlmaps = res.adapterResponse.xlmaps.map((xlmap: any) => ({
id: xlmap[0], id: xlmap[0],
description: xlmap[1], description: xlmap[1],
targetDS: xlmap[2] targetDS: xlmap[2]
@ -162,9 +171,9 @@ export class AppService {
globals.editor.libraries = libraries globals.editor.libraries = libraries
globals.editor.startupSet = true globals.editor.startupSet = true
globals.dcLib = res.globvars[0].DCLIB globals.dcLib = res.adapterResponse.globvars[0].DCLIB
await this.licenceService.activation(res) await this.licenceService.activation(res.adapterResponse)
}) })
.catch((err: any) => { .catch((err: any) => {
startupServiceError = true startupServiceError = true

View File

@ -18,6 +18,7 @@ import { isSpecialMissing } from '@sasjs/utils/input/validators'
import { Col } from '../shared/dc-validator/models/col.model' import { Col } from '../shared/dc-validator/models/col.model'
import { get } from 'lodash-es' import { get } from 'lodash-es'
import { EditorsStageDataSASResponse } from '../models/sas/editors-stagedata.model' import { EditorsStageDataSASResponse } from '../models/sas/editors-stagedata.model'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
@Injectable() @Injectable()
export class SasStoreService { export class SasStoreService {
@ -58,12 +59,10 @@ export class SasStoreService {
) { ) {
const tables: any = {} const tables: any = {}
tables[tableName] = [tableData] tables[tableName] = [tableData]
const res: EditorsGetDataSASResponse = await this.sasService.request( const res: RequestWrapperResponse<EditorsGetDataSASResponse> =
program, await this.sasService.request(program, tables)
tables
)
const response: EditorsGetDataServiceResponse = { const response: EditorsGetDataServiceResponse = {
data: res, data: res.adapterResponse,
libds: libds libds: libds
} }
return response return response
@ -84,8 +83,9 @@ export class SasStoreService {
tableName: string, tableName: string,
program: string, program: string,
$dataFormats: $DataFormats | null, $dataFormats: $DataFormats | null,
suppressErrorSuccessMessages?: boolean suppressErrorSuccessMessages?: boolean,
): Promise<EditorsStageDataSASResponse> { adapterConfig?: any
): Promise<RequestWrapperResponse<EditorsStageDataSASResponse>> {
// add sp as third argument of createData call // add sp as third argument of createData call
let tables: any = { let tables: any = {
@ -103,7 +103,7 @@ export class SasStoreService {
let res = await this.sasService.request<EditorsStageDataSASResponse>( let res = await this.sasService.request<EditorsStageDataSASResponse>(
program, program,
tables, tables,
null, adapterConfig,
{ {
suppressErrorAbortModal: suppressErrorSuccessMessages, suppressErrorAbortModal: suppressErrorSuccessMessages,
suppressSuccessAbortModal: suppressErrorSuccessMessages suppressSuccessAbortModal: suppressErrorSuccessMessages
@ -127,8 +127,7 @@ export class SasStoreService {
) { ) {
let tables: any = {} let tables: any = {}
tables[tableName] = [tableData] tables[tableName] = [tableData]
let res: any = await this.sasService.request(program, tables) return (await this.sasService.request(program, tables)).adapterResponse
return res
} }
/** /**
@ -152,8 +151,8 @@ export class SasStoreService {
* @returns All submits * @returns All submits
*/ */
public async getSubmitts() { public async getSubmitts() {
let res: any = await this.sasService.request('editors/getsubmits', null) return (await this.sasService.request('editors/getsubmits', null))
return res .adapterResponse
} }
/** /**
@ -161,7 +160,8 @@ export class SasStoreService {
* @returns All libraries * @returns All libraries
*/ */
public async viewLibs() { public async viewLibs() {
return this.sasService.request('public/viewlibs', null) return (await this.sasService.request('public/viewlibs', null))
.adapterResponse
} }
public async refreshLibInfo(libref: string) { public async refreshLibInfo(libref: string) {
@ -169,30 +169,22 @@ export class SasStoreService {
lib2refresh: [{ libref }] lib2refresh: [{ libref }]
} }
return this.sasService.request('public/refreshlibinfo', data) return (await this.sasService.request('public/refreshlibinfo', data))
} .adapterResponse
public async versionHistory(libDataset: any) {
const data = { iwant: [{ LIBDS: libDataset }] }
let res: any = await this.sasService.request(
'public/getversionhistory',
data
)
return res
} }
public async viewTables(lib: any) { public async viewTables(lib: any) {
let tables = { SASControlTable: [{ MPLIB: lib }] } let tables = { SASControlTable: [{ MPLIB: lib }] }
let res: any = await this.sasService.request('public/viewtables', tables) return (await this.sasService.request('public/viewtables', tables))
return res .adapterResponse
} }
public async viewData(libDataset: any, filter_pk: any) { public async viewData(libDataset: any, filter_pk: any) {
let tables = { let tables = {
SASControlTable: [{ LIBDS: libDataset, FILTER_RK: filter_pk }] SASControlTable: [{ LIBDS: libDataset, FILTER_RK: filter_pk }]
} }
let res: any = await this.sasService.request('public/viewdata', tables) return (await this.sasService.request('public/viewdata', tables))
return res .adapterResponse
} }
public async viewDataSearch( public async viewDataSearch(
@ -213,41 +205,36 @@ export class SasStoreService {
} }
] ]
} }
let res: any = await this.sasService.request('public/viewdata', tables)
return res return (await this.sasService.request('public/viewdata', tables))
.adapterResponse
} }
public async getXLMapRules(id: string) { public async getXLMapRules(id: string) {
const tables = { const tables = {
getxlmaps_in: [{ XLMAP_ID: id }] getxlmaps_in: [{ XLMAP_ID: id }]
} }
const res: any = await this.sasService.request('editors/getxlmaps', tables) return (await this.sasService.request('editors/getxlmaps', tables))
return res .adapterResponse
}
public async getDetails(tableData: any, tableName: string, program: string) {
let tables: any = {}
tables[tableName] = [tableData]
let res: any = await this.sasService.request(program, tables)
return res
} }
public async showDiffs(tableData: any, tableName: string, program: string) { public async showDiffs(tableData: any, tableName: string, program: string) {
let tables: any = {} let tables: any = {}
tables[tableName] = [tableData] tables[tableName] = [tableData]
let res: any = await this.sasService.request(program, tables, { return (
useComputeApi: false await this.sasService.request(program, tables, {
}) useComputeApi: null // Using WEB APPROACH as a temporary workaround until VIYA JES API is fixed. For other server types then VIYA this is not applicable
return res })
).adapterResponse
} }
public async rejecting(tableData: any, tableName: string, program: string) { public async rejecting(tableData: any, tableName: string, program: string) {
let tables: any = {} let tables: any = {}
tables[tableName] = [tableData] tables[tableName] = [tableData]
let res: any = await this.sasService.request(program, tables, { return (
useComputeApi: false await this.sasService.request(program, tables, {
}) useComputeApi: null // Using WEB APPROACH as a temporary workaround until VIYA JES API is fixed. For other server types then VIYA this is not applicable
return res })
).adapterResponse
} }
public async approveTable( public async approveTable(
@ -258,15 +245,13 @@ export class SasStoreService {
let tables: any = {} let tables: any = {}
tables[tableName] = [tableData] tables[tableName] = [tableData]
let res: any = await this.sasService.request(program, tables) return (await this.sasService.request(program, tables)).adapterResponse
return res
} }
public async getHistory(tableData: any, tableName: string, program: string) { public async getHistory(tableData: any, tableName: string, program: string) {
let tables: any = {} let tables: any = {}
tables[tableName] = [tableData] tables[tableName] = [tableData]
let res: any = await this.sasService.request(program, tables) return (await this.sasService.request(program, tables)).adapterResponse
return res
} }
setQueryVariables(dataset: string, cols: any) { setQueryVariables(dataset: string, cols: any) {
@ -278,8 +263,8 @@ export class SasStoreService {
public async getChangeInfo(tableId: any) { public async getChangeInfo(tableId: any) {
let obj = { TABLE: tableId } let obj = { TABLE: tableId }
let table = { SASControlTable: [obj] } let table = { SASControlTable: [obj] }
let res: any = await this.sasService.request('public/getchangeinfo', table) return (await this.sasService.request('public/getchangeinfo', table))
return res .adapterResponse
} }
public async getQueryValues( public async getQueryValues(
@ -304,13 +289,13 @@ export class SasStoreService {
tables.FILTERQUERY = filterQuery tables.FILTERQUERY = filterQuery
} }
let res: any = await this.sasService return (
.request('public/getcolvals', tables) await this.sasService
.catch((er: any) => { .request('public/getcolvals', tables)
throw er .catch((er: any) => {
}) throw er
})
return res ).adapterResponse
} }
public async saveQuery(libds: string, filterQuery: QueryClause[]) { public async saveQuery(libds: string, filterQuery: QueryClause[]) {
@ -319,21 +304,17 @@ export class SasStoreService {
filterquery: filterQuery filterquery: filterQuery
} }
let res: any = await this.sasService.request( const res = await this.sasService.request('public/validatefilter', tables)
'public/validatefilter',
tables
)
this.filter.next(res) this.filter.next(res)
return res
return res.adapterResponse
} }
public async openTable(tableId: string) { public async openTable(tableId: string) {
let tables = { iwant: [{ table_id: tableId }] } let tables = { iwant: [{ table_id: tableId }] }
let res: any = await this.sasService.request( return (await this.sasService.request('auditors/getstagetable', tables))
'auditors/getstagetable', .adapterResponse
tables
)
return res
} }
public checkOperator(operator: any, value: any, type: string) { public checkOperator(operator: any, value: any, type: string) {

View File

@ -1,9 +1,21 @@
import { HttpClient } from '@angular/common/http' import {
HttpClient,
HttpContext,
HttpErrorResponse,
HttpHeaders,
HttpParams
} from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Observable } from 'rxjs' import { catchError, Observable, throwError } from 'rxjs'
import { Collection } from '../viya-api-explorer/models/collection.model' import { Collection } from '../viya-api-explorer/models/collection.model'
import { AppStoreService } from './app-store.service' import { AppStoreService } from './app-store.service'
import { ViyaApis } from '../viya-api-explorer/models/viya-apis.models' import { ViyaApis } from '../viya-api-explorer/models/viya-apis.models'
import { ViyaApiFolderMembers } from '../viya-api-explorer/models/viya-api-folder-content.model'
import { ViyaApiFolder } from '../viya-api-explorer/models/viya-api-folder.model'
import { ViyaApiIdentities } from '../viya-api-explorer/models/viya-api-identities.model'
import { ViyaApiCurrentUser } from '../viya-api-explorer/models/viya-api-current-user.model'
import { ViyaComputeContexts } from '../viya-api-explorer/models/viya-compute-contexts.model'
import { ComputeContextDetails } from '../viya-api-explorer/models/viya-compute-context-details.model'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -22,7 +34,8 @@ export class SasViyaService {
}, },
Compute: { Compute: {
jobs: '/jobDefinitions', jobs: '/jobDefinitions',
jobExecution: '/jobExecution' jobExecution: '/jobExecution',
contexts: '/compute/contexts'
}, },
Decision_Management: { Decision_Management: {
modelManagement: '/modelManagement', modelManagement: '/modelManagement',
@ -57,15 +70,23 @@ export class SasViyaService {
constructor( constructor(
private http: HttpClient, private http: HttpClient,
private appStoreService: AppStoreService private appStoreService: AppStoreService
) { ) {}
/**
* This function is replacing the constructor.
* The reason for this is timing issues, other services eg. sas.service, app-store.service
* must be initialized before this bit of code is executed.
* This function is being called by `sas.service`
*/
setup() {
const adapterConfig = this.appStoreService.getDcAdapterSettings() const adapterConfig = this.appStoreService.getDcAdapterSettings()
this.serverUrl = adapterConfig?.serverUrl || '' this.serverUrl = adapterConfig?.serverUrl || ''
//example // example collection request
this.getByCollection('jobs').subscribe((res) => { // this.getByCollection('jobs').subscribe((res) => {
console.log('res', res) // console.log('res', res)
}) // })
} }
/** /**
@ -82,7 +103,7 @@ export class SasViyaService {
* @returns * @returns
*/ */
getByUrl(url: string): Observable<Collection> { getByUrl(url: string): Observable<Collection> {
return this.http.get<Collection>(`${this.serverUrl}${url}`, { return this.get<Collection>(`${this.serverUrl}${url}`, {
withCredentials: true withCredentials: true
}) })
} }
@ -93,8 +114,106 @@ export class SasViyaService {
* @returns * @returns
*/ */
getByCollection(apiCollection: string): Observable<Collection> { getByCollection(apiCollection: string): Observable<Collection> {
return this.http.get<Collection>(`${this.serverUrl}${apiCollection}`, { return this.get<Collection>(`${this.serverUrl}${apiCollection}`, {
withCredentials: true withCredentials: true
}) })
} }
getComputeContexts(): Observable<ViyaComputeContexts> {
return this.get<ViyaComputeContexts>(`${this.serverUrl}/compute/contexts`, {
withCredentials: true
})
}
getComputeContextById(id: string): Observable<ComputeContextDetails> {
return this.get<ComputeContextDetails>(
`${this.serverUrl}/compute/contexts/${id}`,
{
withCredentials: true
}
)
}
/**
* @param path Path to the folder
* @returns The folder info object
*/
getFolderByPath(path: string): Observable<ViyaApiFolder> {
return this.get<ViyaApiFolder>(
`${this.serverUrl}/folders/folders/@item?path=${path}`,
{
withCredentials: true
}
)
}
getFolderMembers(folderId: string): Observable<ViyaApiFolderMembers> {
return this.get<ViyaApiFolderMembers>(
`${this.serverUrl}/folders/folders/${folderId}/members`,
{
withCredentials: true
}
)
}
getAdminGroups(limit: number = 5000): Observable<ViyaApiIdentities> {
return this.get<ViyaApiIdentities>(
`${this.serverUrl}/identities/groups?sortBy=name&limit=${limit}`,
{
withCredentials: true
}
)
}
getCurrentUser(): Observable<ViyaApiCurrentUser> {
return this.get<ViyaApiCurrentUser>(
`${this.serverUrl}/identities/users/@currentUser`,
{
withCredentials: true
}
)
}
get<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[]
}
context?: HttpContext
observe?: 'body'
params?:
| HttpParams
| {
[param: string]:
| string
| number
| boolean
| ReadonlyArray<string | number | boolean>
}
reportProgress?: boolean
responseType?: 'json'
withCredentials?: boolean
transferCache?:
| {
includeHeaders?: string[]
}
| boolean
}
): Observable<T> {
return this.http.get<T>(url, options).pipe(
catchError((err: HttpErrorResponse) => {
console.log('url', url)
console.log('err.status', err.status)
if (err.status === 449 || err.status === 401) {
// Retry once if we got a 449
return this.http.get<T>(url, options)
}
// Otherwise propagate the error
return throwError(() => err)
})
)
}
} }

View File

@ -12,8 +12,13 @@ import { ServerType } from '@sasjs/utils/types/serverType'
import { DcAdapterSettings } from '../models/DcAdapterSettings' import { DcAdapterSettings } from '../models/DcAdapterSettings'
import { AppStoreService } from './app-store.service' import { AppStoreService } from './app-store.service'
import { LoggerService } from './logger.service' import { LoggerService } from './logger.service'
import { RequestWrapperOptions } from '../models/RequestWrapperOptions' import { RequestWrapperOptions } from '../models/request-wrapper/RequestWrapperOptions'
import { ErrorBody } from '../models/ErrorBody' import { ErrorBody } from '../models/ErrorBody'
import { UploadFileResponse } from '../models/UploadFile'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'
import { SasViyaService } from './sas-viya.service'
import { ViyaApiFolder } from '../viya-api-explorer/models/viya-api-folder.model'
import { ViyaApiFolderMembers } from '../viya-api-explorer/models/viya-api-folder-content.model'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -36,6 +41,7 @@ export class SasService {
private userService: UserService, private userService: UserService,
private eventService: EventService, private eventService: EventService,
private sasjsService: SasjsService, private sasjsService: SasjsService,
private sasViyaService: SasViyaService,
private loggerService: LoggerService, private loggerService: LoggerService,
private router: Router private router: Router
) {} ) {}
@ -49,6 +55,7 @@ export class SasService {
this.dcAdapterSettings = this.appStoreService.getDcAdapterSettings() this.dcAdapterSettings = this.appStoreService.getDcAdapterSettings()
this.sasjsService.setup() this.sasjsService.setup()
this.sasViyaService.setup()
if (!this.dcAdapterSettings) { if (!this.dcAdapterSettings) {
this.eventService.showInfoModal( this.eventService.showInfoModal(
@ -95,14 +102,16 @@ export class SasService {
* @param config additional parameters to force eg. { debug: false } * @param config additional parameters to force eg. { debug: false }
* @param wrapperOptions used to provide options to the request wrapper function * @param wrapperOptions used to provide options to the request wrapper function
* for example to suppress error or success abort modals after request is finished * for example to suppress error or success abort modals after request is finished
* @returns * @returns adapter response or an error. It will return the `log` as well.
* The log could be potentially be wrong if multiple requests happen because the log this
* function return is the last request in the Adapter Array for the given URL.
*/ */
public request<responseType = any>( public request<responseType = any>(
url: string, url: string,
data: any, data: any,
config?: any, config?: any,
wrapperOptions?: RequestWrapperOptions wrapperOptions?: RequestWrapperOptions
): Promise<responseType> { ): Promise<RequestWrapperResponse<responseType>> {
url = 'services/' + url url = 'services/' + url
if (!wrapperOptions) wrapperOptions = {} if (!wrapperOptions) wrapperOptions = {}
@ -117,9 +126,16 @@ export class SasService {
}) })
.then( .then(
(res: any) => { (res: any) => {
const sasRequest = this.sasjsAdapter
.getSasRequests()
.find((rq) => rq.serviceLink === url)
if (res.login === false) { if (res.login === false) {
this.shouldLogin.next(true) this.shouldLogin.next(true)
reject(false) reject({
adapterResponse: false,
log: sasRequest?.logFile
})
} }
if (!this.userService.user && res.MF_GETUSER) { if (!this.userService.user && res.MF_GETUSER) {
@ -141,7 +157,12 @@ export class SasService {
} }
if (res.status === 404) { if (res.status === 404) {
reject({ MESSAGE: res.body || 'SAS Responded with error' }) reject({
adapterResponse: {
MESSAGE: res.body || 'SAS Responded with error'
},
log: sasRequest?.logFile
})
} }
if (typeof res.sasjsAbort !== 'undefined') { if (typeof res.sasjsAbort !== 'undefined') {
@ -157,7 +178,12 @@ export class SasService {
this.eventService.startupDataLoaded() this.eventService.startupDataLoaded()
this.router.navigateByUrl('/deploy') this.router.navigateByUrl('/deploy')
reject({ error: abortMsg }) reject({
adapterResponse: {
error: abortMsg
},
log: sasRequest?.logFile
})
return return
} }
@ -174,14 +200,26 @@ export class SasService {
) )
} }
reject({ error: abortMsg }) reject({
adapterResponse: {
error: abortMsg
},
log: sasRequest?.logFile
})
} }
resolve(res) resolve({
adapterResponse: res,
log: sasRequest?.logFile
})
}, },
(err: { error: ErrorBody | undefined }) => { (err: { error: ErrorBody | undefined }) => {
console.error(err) console.error(err)
const sasRequest = this.sasjsAdapter
.getSasRequests()
.find((rq) => rq.serviceLink === url)
if (err.error) { if (err.error) {
let errorMessage: string | undefined = err.error.message let errorMessage: string | undefined = err.error.message
let log: string | undefined let log: string | undefined
@ -205,10 +243,18 @@ export class SasService {
'Request error' 'Request error'
) )
} }
reject({ error: errorMessage }) reject({
adapterResponse: {
error: errorMessage
},
log: sasRequest?.logFile
})
} }
reject(err) reject({
adapterResponse: err,
log: sasRequest?.logFile
})
} }
) )
}) })
@ -222,8 +268,35 @@ export class SasService {
* @param params Aditional parameters eg. { debug: false } * @param params Aditional parameters eg. { debug: false }
* @returns HTTP Response * @returns HTTP Response
*/ */
public uploadFile(sasService: string, files: UploadFile[], params: any) { public uploadFile(
return this.sasjsAdapter.uploadFile(sasService, files, params) sasService: string,
files: UploadFile[],
params: any
): Promise<UploadFileResponse> {
return new Promise((resolve, reject) => {
this.sasjsAdapter.uploadFile(sasService, files, params).then(
(res) => {
const sasRequest = this.sasjsAdapter
.getSasRequests()
.find((rq) => rq.serviceLink === 'services/editors/loadfile')
resolve({
adapterResponse: res,
log: sasRequest?.logFile
})
},
(err) => {
const sasRequest = this.sasjsAdapter
.getSasRequests()
.find((rq) => rq.serviceLink === 'services/editors/loadfile')
reject({
response: err,
log: sasRequest?.logFile
})
}
)
})
} }
public async login(username: string, password: string) { public async login(username: string, password: string) {
@ -355,21 +428,93 @@ export class SasService {
typeof this.sasjsAdapter.getFolder !== 'undefined' typeof this.sasjsAdapter.getFolder !== 'undefined'
let appLocExists: boolean = false let appLocExists: boolean = false
let errorMessage: string | undefined = undefined
if (getFolderExistsInAdapter) { if (getFolderExistsInAdapter) {
appLocExists = await this.appLocCheck(path) const results = await this.appLocCheck(path)
appLocExists = results.found
errorMessage = results.errorMessage
} else { } else {
appLocExists = await this.appLocCheckPreAxiosdAdapter(path) appLocExists = await this.appLocCheckPreAxiosdAdapter(path)
} }
if (appLocExists) { if (appLocExists) {
this.loadStartupServiceEmitter.emit() // Check if there is appLoc/services/admin/makedata.sas present
// if yes, it needs to be run, so we redirect to /deploy
// if not, we load the startup service
this.viyaMakedataSuccessfull().then(
(success: boolean) => {
if (success) {
this.loadStartupServiceEmitter.emit()
} else {
this.eventService.startupDataLoaded()
this.router.navigateByUrl('/deploy')
}
},
(error: any) => {
console.error('Error while looking for the file: makedata.sas', error)
}
)
} else {
const errorMessageToShow =
(errorMessage ||
'Viya services are not present on the current appLoc, or API not reachable. Check the ADAPTER configuration.') +
`\nAppLoc: ${path}`
this.eventService.showInfoModal('Error', errorMessageToShow)
} }
} }
public appLocCheck(path: string): Promise<boolean> { private async viyaMakedataSuccessfull(): Promise<boolean> {
return new Promise((resolve, reject) => {
const sasjsConfig = this.getSasjsConfig()
const configuratorFolder = `${sasjsConfig.appLoc}/services/admin`
this.sasViyaService.getFolderByPath(configuratorFolder).subscribe(
(folderInfo: ViyaApiFolder) => {
const folderId = folderInfo.id
if (!folderId) {
console.error(
`Folder ID is not present. ${configuratorFolder}`,
sasjsConfig
)
resolve(false)
}
this.sasViyaService.getFolderMembers(folderId).subscribe(
(members: ViyaApiFolderMembers) => {
if (
!members.items.some((item: any) => item.name === 'makedata')
) {
// Makedata.sas is not present, which means it was run
resolve(true)
} else {
// Makedata.sas is present, which means it was not run
resolve(false)
}
},
(err: any) => {
console.error('Error getting folder contents', err)
reject()
}
)
},
(err: any) => {
console.warn('Error getting folder info', err)
reject(err)
}
)
})
}
public appLocCheck(
path: string
): Promise<{ found: boolean; errorMessage?: string }> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
let statusNotFound: boolean = false let fetchError: string = ''
let res: any let res: any
@ -380,20 +525,21 @@ export class SasService {
this.appLocCheckPending = true this.appLocCheckPending = true
this.shouldLogin.next(true) this.shouldLogin.next(true)
resolve(false) resolve({ found: false })
} else if (err.name === 'NotFoundeError') {
fetchError = err.message
} else { } else {
statusNotFound = true fetchError =
'Viya services are not present on the current appLoc, or API not reachable. Check the ADAPTER configuration.'
} }
} }
if (statusNotFound) { if (fetchError.length) {
console.warn('Viya services are not present on the current appLoc.') console.warn(fetchError)
this.eventService.startupDataLoaded() return resolve({ found: false, errorMessage: fetchError })
this.router.navigateByUrl('/deploy')
return resolve(false)
} }
resolve(true) resolve({ found: true })
}) })
} }

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,10 @@
> >
<h3 class="modal-title"> <h3 class="modal-title">
{{ data.modalTitle }} {{ data.modalTitle }}
<p *ngIf="data.sasService && data.sasService.length > 0" class="sasService"> <p
*ngIf="data.sasService && data.sasService.length > 0"
class="sasService mt-0"
>
SAS Service: <strong>{{ data.sasService }}</strong> SAS Service: <strong>{{ data.sasService }}</strong>
</p> </p>
</h3> </h3>

View File

@ -1,43 +0,0 @@
.clr-abort-modal {
::ng-deep {
.modal {
z-index: 2050;
}
.modal-title-wrapper {
width: 100%;
}
.modal {
z-index: 2050;
}
}
}
.modal-title {
position: relative;
}
.sasService {
position: absolute;
top: 0px;
right: 10px;
margin: 0;
}
.modal-footer {
position: relative;
border-top: 1px solid #dcdcdc;
}
.systext {
overflow: auto;
margin-top: 20px;
padding: 10px 0;
border-top: 1px solid #dcdcdc;
p {
margin-top: 0;
word-wrap: break-word;
}
}

View File

@ -1,4 +1,11 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { ServerType } from '@sasjs/utils/types/serverType' import { ServerType } from '@sasjs/utils/types/serverType'
import { EventService } from 'src/app/services/event.service' import { EventService } from 'src/app/services/event.service'
@ -9,7 +16,8 @@ import { AbortDetails, InfoModal } from '../../models/InfoModal'
@Component({ @Component({
selector: 'app-info-modal', selector: 'app-info-modal',
templateUrl: './info-modal.component.html', templateUrl: './info-modal.component.html',
styleUrls: ['./info-modal.component.scss'] styleUrls: ['./info-modal.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class InfoModalComponent implements OnInit { export class InfoModalComponent implements OnInit {
@Output() onConfirmModalClick: EventEmitter<any> = new EventEmitter() @Output() onConfirmModalClick: EventEmitter<any> = new EventEmitter()

View File

@ -1,3 +0,0 @@
clr-alerts {
display: block;
}

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, ViewEncapsulation } from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { Alert } from './alert' import { Alert } from './alert'
import { AlertsService } from './alerts.service' import { AlertsService } from './alerts.service'
@ -6,7 +6,8 @@ import { AlertsService } from './alerts.service'
@Component({ @Component({
selector: 'app-alerts', selector: 'app-alerts',
templateUrl: './alerts.component.html', templateUrl: './alerts.component.html',
styleUrls: ['./alerts.component.scss'] styleUrls: ['./alerts.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class AlertsComponent implements OnInit { export class AlertsComponent implements OnInit {
public alerts: Array<Alert> = [] public alerts: Array<Alert> = []

View File

@ -1,75 +0,0 @@
.input-val {
border: 0px;
background: transparent;
border-bottom: 1px solid #999999;
}
input {
width: 100%;
outline: none;
&::-webkit-calendar-picker-indicator {
margin-top: -5px;
}
}
::ng-deep {
body[cds-theme="dark"] {
.datalist {
background: #21333b;
border: 1px solid #575757;
}
input {
color: #fff;
}
.datalist option {
color: #fff
}
}
body[cds-theme="light"] {
.datalist {
background: #fff;
}
}
}
.autocomplete-wrapper {
.overlay {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 1500;
}
.datalist {
position: fixed;
box-shadow: 0px 3px 10px -1px #0000002b;
overflow: auto;
z-index: 2000;
::ng-deep {
option {
padding: 5px 10px;
cursor: pointer;
&:hover {
background: #0000000f;
}
&.focused {
background: #0000000f;
}
}
}
}
.load-more {
text-align: center;
border-top: 1px solid #e6e6e6;
}
}

View File

@ -7,7 +7,8 @@ import {
Input, Input,
OnInit, OnInit,
Output, Output,
ViewChild ViewChild,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
export type OnLoadingMoreEvent = { export type OnLoadingMoreEvent = {
@ -17,7 +18,8 @@ export type OnLoadingMoreEvent = {
@Component({ @Component({
selector: 'app-autocomplete', selector: 'app-autocomplete',
templateUrl: './autocomplete.component.html', templateUrl: './autocomplete.component.html',
styleUrls: ['./autocomplete.component.scss'] styleUrls: ['./autocomplete.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class AutocompleteComponent implements OnInit, AfterViewInit { export class AutocompleteComponent implements OnInit, AfterViewInit {
@ViewChild('input') inputElement: any @ViewChild('input') inputElement: any

View File

@ -1,3 +0,0 @@
.unset {
color: unset;
}

View File

@ -1,9 +1,10 @@
import { Component, Input, OnInit } from '@angular/core' import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'
@Component({ @Component({
selector: 'contact-link', selector: 'contact-link',
templateUrl: './contact-link.component.html', templateUrl: './contact-link.component.html',
styleUrls: ['./contact-link.component.scss'] styleUrls: ['./contact-link.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class ContactLinkComponent implements OnInit { export class ContactLinkComponent implements OnInit {
@Input() classes: string = '' @Input() classes: string = ''

View File

@ -1,35 +0,0 @@
.modal-body {
clr-tabs {
max-height: 70vh;
}
::ng-deep {
.tab-content {
width: 100%;
overflow: auto;
.datagrid-outer-wrapper {
width: 100%;
}
}
}
}
clr-modal {
::ng-deep {
.modal-dialog {
height: 100%;
}
}
}
.clickable-row {
cursor: pointer;
}
::ng-deep {
.datagrid-table .datagrid-cell:focus {
outline: none;
outline-offset: 0;
}
}

View File

@ -5,7 +5,8 @@ import {
OnChanges, OnChanges,
OnInit, OnInit,
Output, Output,
SimpleChanges SimpleChanges,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { DSMeta, Version } from 'src/app/models/sas/editors-getdata.model' import { DSMeta, Version } from 'src/app/models/sas/editors-getdata.model'
import { Tab } from './models/dsmeta-groupped.model' import { Tab } from './models/dsmeta-groupped.model'
@ -13,7 +14,8 @@ import { Tab } from './models/dsmeta-groupped.model'
@Component({ @Component({
selector: 'app-dataset-info', selector: 'app-dataset-info',
templateUrl: './dataset-info.component.html', templateUrl: './dataset-info.component.html',
styleUrls: ['./dataset-info.component.scss'] styleUrls: ['./dataset-info.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class DatasetInfoComponent implements OnInit, OnChanges { export class DatasetInfoComponent implements OnInit, OnChanges {
@Input() open: boolean = false @Input() open: boolean = false

View File

@ -1,17 +0,0 @@
clr-tree-node button {
white-space: nowrap;
}
clr-tree {
::ng-deep {
.clr-tree-node-content-container {
&:focus {
.clr-treenode-link {
background: #e8e8e8;
background: var(--clr-tree-link-hover-color, #e8e8e8);
text-decoration: none;
}
}
}
}
}

View File

@ -9,7 +9,8 @@ import {
Output, Output,
QueryList, QueryList,
SimpleChanges, SimpleChanges,
ViewChildren ViewChildren,
ViewEncapsulation
} from '@angular/core' } from '@angular/core'
import { HelperService } from 'src/app/services/helper.service' import { HelperService } from 'src/app/services/helper.service'
import { LicenceService } from 'src/app/services/licence.service' import { LicenceService } from 'src/app/services/licence.service'
@ -20,7 +21,8 @@ import { TableClickEmitter } from './models/TableClickEmitter'
@Component({ @Component({
selector: 'dc-tree', selector: 'dc-tree',
templateUrl: './dc-tree.component.html', templateUrl: './dc-tree.component.html',
styleUrls: ['./dc-tree.component.scss'] styleUrls: ['./dc-tree.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class DcTreeComponent implements OnInit, AfterViewInit, OnChanges { export class DcTreeComponent implements OnInit, AfterViewInit, OnChanges {
// REFACTOR NOTICE // REFACTOR NOTICE

View File

@ -2,7 +2,7 @@ import { getHotDataSchema } from '../utils/getHotDataSchema'
describe('DC Validator - hot data schema', () => { describe('DC Validator - hot data schema', () => {
it('should return correct value for col type', () => { it('should return correct value for col type', () => {
expect(getHotDataSchema('numeric')).toEqual(0) expect(getHotDataSchema('numeric')).toEqual('')
expect( expect(
getHotDataSchema('autocomplete', { data: '', source: [1, 2, 3] }) getHotDataSchema('autocomplete', { data: '', source: [1, 2, 3] })
).toEqual(1) ).toEqual(1)

View File

@ -1,7 +1,7 @@
import { DcValidation } from '../models/dc-validation.model' import { DcValidation } from '../models/dc-validation.model'
const schemaTypeMap: { [key: string]: any } = { const schemaTypeMap: { [key: string]: any } = {
numeric: 0, numeric: '',
default: '' default: ''
} }

View File

@ -1,4 +1,4 @@
<ng-container *ngIf="options$ | async as options"> <div *ngIf="options$ | async as options" class="excel-password-root">
<clr-modal <clr-modal
[clrModalOpen]="options.open" [clrModalOpen]="options.open"
[clrModalSize]="'md'" [clrModalSize]="'md'"
@ -11,6 +11,7 @@
<p class="m-0">Please enter password:</p> <p class="m-0">Please enter password:</p>
<input <input
#filePasswordInput #filePasswordInput
[(ngModel)]="passwordInput"
data-lpignore="true" data-lpignore="true"
autocomplete="off" autocomplete="off"
id="filePasswordInput" id="filePasswordInput"
@ -19,20 +20,25 @@
/> />
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<p *ngIf="options.error" class="m-0 color-red"> <div>
Sorry that didn't work, try again. <p *ngIf="options.error" class="m-0 color-red">
</p> Sorry that didn't work, try again.
<button type="button" class="btn btn-sm btn-outline" (click)="close()"> </p>
Cancel </div>
</button>
<button <div class="buttons">
type="button" <button type="button" class="btn btn-sm btn-outline" (click)="close()">
class="btn btn-sm btn-success-outline" Cancel
[disabled]="filePasswordInput.value.length < 1" </button>
(click)="close(filePasswordInput.value)" <button
> type="button"
Unlock class="btn btn-sm btn-success-outline"
</button> [disabled]="filePasswordInput.value.length < 1"
(click)="close(filePasswordInput.value)"
>
Unlock
</button>
</div>
</div> </div>
</clr-modal> </clr-modal>
</ng-container> </div>

View File

@ -1,24 +1,25 @@
import { Component } from '@angular/core' import { Component, ViewEncapsulation } from '@angular/core'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { import { ExcelPasswordModalService } from './excel-password-modal.service'
ExcelPasswordModalService, import { Options } from './models/options.interface'
Options
} from './excel-password-modal.service'
@Component({ @Component({
selector: 'app-excel-password-modal', selector: 'app-excel-password-modal',
templateUrl: './excel-password-modal.component.html' styleUrls: ['./excel-password-modal.component.scss'],
templateUrl: './excel-password-modal.component.html',
encapsulation: ViewEncapsulation.None
}) })
export class ExcelPasswordModalComponent { export class ExcelPasswordModalComponent {
options$: Observable<Options> options$: Observable<Options> = this.excelPasswordModalService.optionsSubject$
fileUnlockError: boolean = false fileUnlockError: boolean = false
constructor(private excelPasswordModalService: ExcelPasswordModalService) { passwordInput: string = ''
this.options$ = this.excelPasswordModalService.optionsSubject$
} constructor(private excelPasswordModalService: ExcelPasswordModalService) {}
close(password?: string) { close(password?: string) {
this.passwordInput = ''
this.excelPasswordModalService.close(password) this.excelPasswordModalService.close(password)
} }
} }

Some files were not shown because too many files have changed in this diff Show More