hlsv 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -3
  3. data/README.md +36 -29
  4. data/lib/hlsv/analysis_registry.rb +47 -0
  5. data/lib/hlsv/analysis_runner.rb +193 -0
  6. data/lib/hlsv/config_manager.rb +125 -0
  7. data/lib/hlsv/html2word.rb +18 -18
  8. data/lib/hlsv/path_guard.rb +32 -0
  9. data/lib/hlsv/sdtm_validation/dataset.rb +169 -0
  10. data/lib/hlsv/sdtm_validation/define.rb +142 -0
  11. data/lib/hlsv/sdtm_validation/report.rb +357 -0
  12. data/lib/hlsv/sdtm_validation.rb +390 -0
  13. data/lib/hlsv/url_helper.rb +28 -0
  14. data/lib/hlsv/version.rb +1 -1
  15. data/lib/hlsv/web_app.rb +264 -418
  16. data/lib/hlsv.rb +12 -5
  17. data/public/css/accessibility/accessibility.css +33 -0
  18. data/public/css/base/layout.css +33 -0
  19. data/public/css/base/reset.css +23 -0
  20. data/public/css/base/typography.css +31 -0
  21. data/public/css/components/buttons.css +142 -0
  22. data/public/css/components/file-tree.css +107 -0
  23. data/public/css/components/footer.css +43 -0
  24. data/public/css/components/forms.css +56 -0
  25. data/public/css/components/header.css +52 -0
  26. data/public/css/components/status.css +56 -0
  27. data/public/css/features/csv-table.css +204 -0
  28. data/public/css/features/file-browser.css +208 -0
  29. data/public/css/responsive/responsive.css +133 -0
  30. data/public/css/styles.css +25 -0
  31. data/public/css/styles_csv.css +23 -0
  32. data/public/favicon.ico +0 -0
  33. data/public/js/analysis.js +201 -0
  34. data/public/js/app.js +63 -0
  35. data/public/js/browser.js +172 -0
  36. data/public/js/config.js +214 -0
  37. data/public/js/results.js +240 -0
  38. data/public/js/utils.js +57 -0
  39. data/views/csv_view.erb +11 -12
  40. data/views/index.erb +70 -19
  41. data/views/{report_template.erb → report.erb} +203 -188
  42. metadata +39 -41
  43. data/lib/hlsv/find_keys.rb +0 -979
  44. data/lib/hlsv/mon_script.rb +0 -169
  45. data/public/app.js +0 -569
  46. data/public/styles.css +0 -586
  47. data/public/styles_csv.css +0 -448
data/public/styles.css DELETED
@@ -1,586 +0,0 @@
1
- /*
2
- Copyright (c) 2026 AdClin
3
- Licensed under the GNU Affero General Public License v3.0 or later.
4
- See the LICENSE file for details.
5
- */
6
-
7
- /* ===========================
8
- RESET & BASE STYLES
9
- =========================== */
10
- * {
11
- box-sizing: border-box;
12
- }
13
-
14
- body {
15
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
- max-width: 1200px;
17
- margin: 0 auto;
18
- padding: 20px;
19
- background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
20
- line-height: 1.6;
21
- color: #333;
22
- }
23
-
24
- /* ===========================
25
- HEADER WITH BRANDING
26
- =========================== */
27
- .app-header {
28
- display: flex;
29
- justify-content: space-between;
30
- align-items: center;
31
- margin-bottom: 20px;
32
- padding: 20px;
33
- background: white;
34
- border-radius: 8px;
35
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
36
- }
37
-
38
- .app-header h1 {
39
- margin: 0;
40
- padding: 0;
41
- border: none;
42
- color: #1a1a1a;
43
- font-size: 2em;
44
- }
45
-
46
- .company-branding {
47
- display: flex;
48
- align-items: center;
49
- }
50
-
51
- .company-link {
52
- display: flex;
53
- align-items: center;
54
- gap: 12px;
55
- text-decoration: none;
56
- transition: all 0.3s ease;
57
- }
58
-
59
- .company-link:hover {
60
- opacity: 0.8;
61
- transform: translateY(-2px);
62
- }
63
-
64
- .company-logo {
65
- height: 50px;
66
- width: auto;
67
- object-fit: contain;
68
- }
69
-
70
- .company-name {
71
- font-size: 1.2em;
72
- font-weight: 600;
73
- color: #2c3e50;
74
- }
75
-
76
- /* ===========================
77
- FOOTER WITH COPYRIGHT AND LICENSE
78
- =========================== */
79
- .app-footer {
80
- margin-top: 40px;
81
- padding: 20px;
82
- background: white;
83
- border-radius: 8px;
84
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
85
- text-align: center;
86
- }
87
-
88
- .footer-content {
89
- display: flex;
90
- justify-content: center;
91
- align-items: center;
92
- flex-wrap: wrap;
93
- gap: 15px;
94
- color: #6c757d;
95
- font-size: 0.9em;
96
- }
97
-
98
- .footer-content .separator {
99
- color: #dee2e6;
100
- font-weight: bold;
101
- }
102
-
103
- .footer-content a {
104
- color: #007bff;
105
- text-decoration: none;
106
- transition: color 0.3s ease;
107
- }
108
-
109
- .footer-content a:hover {
110
- color: #0056b3;
111
- text-decoration: underline;
112
- }
113
-
114
- /* ===========================
115
- TYPOGRAPHY
116
- =========================== */
117
- h1 {
118
- color: #1a1a1a;
119
- border-bottom: 3px solid #007bff;
120
- padding-bottom: 15px;
121
- margin-bottom: 20px;
122
- font-size: 2em;
123
- }
124
-
125
- h2 {
126
- color: #333;
127
- margin-top: 0;
128
- font-size: 1.5em;
129
- border-bottom: 2px solid #e9ecef;
130
- padding-bottom: 10px;
131
- }
132
-
133
- h3 {
134
- color: #495057;
135
- margin: 20px 0 10px 0;
136
- font-size: 1.2em;
137
- }
138
-
139
- h4 {
140
- color: #6c757d;
141
- margin: 15px 0 10px 0;
142
- font-size: 1.1em;
143
- }
144
-
145
- /* ===========================
146
- CONTAINERS
147
- =========================== */
148
- .container {
149
- background: white;
150
- padding: 30px;
151
- border-radius: 8px;
152
- margin-bottom: 25px;
153
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
154
- transition: box-shadow 0.3s ease;
155
- }
156
-
157
- .container:hover {
158
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
159
- }
160
-
161
- .info {
162
- background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%);
163
- padding: 15px;
164
- border-radius: 6px;
165
- margin-bottom: 20px;
166
- border-left: 4px solid #17a2b8;
167
- font-weight: 500;
168
- }
169
-
170
- .info-section {
171
- background: #f8f9fa;
172
- padding: 20px;
173
- border-radius: 6px;
174
- margin: 15px 0;
175
- border-left: 4px solid #6c757d;
176
- }
177
-
178
- /* ===========================
179
- FORM ELEMENTS
180
- =========================== */
181
- .form-group {
182
- margin: 20px 0;
183
- }
184
-
185
- .form-group label {
186
- display: block;
187
- font-weight: 600;
188
- margin-bottom: 8px;
189
- color: #333;
190
- }
191
-
192
- .form-group label .required {
193
- color: #dc3545;
194
- margin-left: 4px;
195
- }
196
-
197
- .form-group input[type="text"],
198
- .form-group select {
199
- width: 100%;
200
- padding: 12px;
201
- border: 2px solid #ddd;
202
- border-radius: 6px;
203
- font-size: 14px;
204
- transition: border-color 0.3s ease, box-shadow 0.3s ease;
205
- }
206
-
207
- .form-group input[type="text"]:focus,
208
- .form-group select:focus {
209
- outline: none;
210
- border-color: #007bff;
211
- box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
212
- }
213
-
214
- .form-group input.error-field {
215
- border-color: #dc3545;
216
- background-color: #fff5f5;
217
- }
218
-
219
- .form-group small {
220
- display: block;
221
- margin-top: 6px;
222
- color: #6c757d;
223
- font-size: 13px;
224
- line-height: 1.4;
225
- }
226
-
227
- .config-grid {
228
- display: grid;
229
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
230
- gap: 20px;
231
- margin: 20px 0;
232
- }
233
-
234
- /* ===========================
235
- BUTTONS
236
- =========================== */
237
- button {
238
- padding: 12px 24px;
239
- border: none;
240
- border-radius: 6px;
241
- cursor: pointer;
242
- font-size: 14px;
243
- font-weight: 600;
244
- margin-right: 10px;
245
- margin-bottom: 10px;
246
- transition: all 0.3s ease;
247
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
248
- }
249
-
250
- button:hover {
251
- transform: translateY(-2px);
252
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
253
- }
254
-
255
- button:active {
256
- transform: translateY(0);
257
- }
258
-
259
- button:disabled {
260
- background: #ccc;
261
- cursor: not-allowed;
262
- opacity: 0.6;
263
- transform: none;
264
- }
265
-
266
- button:disabled:hover {
267
- transform: none;
268
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
269
- }
270
-
271
- /* Button Variants */
272
- .btn-primary,
273
- button {
274
- background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
275
- color: white;
276
- }
277
-
278
- .btn-primary:hover,
279
- button:hover:not(:disabled) {
280
- background: linear-gradient(135deg, #0056b3 0%, #004085 100%);
281
- }
282
-
283
- .btn-secondary,
284
- button.secondary {
285
- background: linear-gradient(135deg, #6c757d 0%, #545b62 100%);
286
- color: white;
287
- }
288
-
289
- .btn-secondary:hover,
290
- button.secondary:hover {
291
- background: linear-gradient(135deg, #545b62 0%, #3d4246 100%);
292
- }
293
-
294
- .btn-success,
295
- button.success {
296
- background: linear-gradient(135deg, #28a745 0%, #218838 100%);
297
- color: white;
298
- }
299
-
300
- .btn-success:hover,
301
- button.success:hover {
302
- background: linear-gradient(135deg, #218838 0%, #1e7e34 100%);
303
- }
304
-
305
- .btn-danger,
306
- button.danger {
307
- background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
308
- color: white;
309
- }
310
-
311
- .btn-danger:hover,
312
- button.danger:hover {
313
- background: linear-gradient(135deg, #c82333 0%, #bd2130 100%);
314
- }
315
-
316
- .button-group {
317
- display: flex;
318
- flex-wrap: wrap;
319
- gap: 10px;
320
- margin: 20px 0;
321
- }
322
-
323
- /* ===========================
324
- STATUS MESSAGES
325
- =========================== */
326
- .status {
327
- padding: 15px 20px;
328
- border-radius: 6px;
329
- margin: 15px 0;
330
- animation: slideIn 0.3s ease;
331
- }
332
-
333
- @keyframes slideIn {
334
- from {
335
- opacity: 0;
336
- transform: translateY(-10px);
337
- }
338
- to {
339
- opacity: 1;
340
- transform: translateY(0);
341
- }
342
- }
343
-
344
- .status.success {
345
- background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
346
- color: #155724;
347
- border-left: 4px solid #28a745;
348
- }
349
-
350
- .status.error {
351
- background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
352
- color: #721c24;
353
- border-left: 4px solid #dc3545;
354
- }
355
-
356
- .status.processing {
357
- background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
358
- color: #856404;
359
- border-left: 4px solid #ffc107;
360
- }
361
-
362
- /* ===========================
363
- FILE DISPLAY
364
- =========================== */
365
- .file-tree {
366
- margin-top: 20px;
367
- }
368
-
369
- .folder-item {
370
- margin: 10px 0;
371
- }
372
-
373
- .folder-header {
374
- padding: 12px 15px;
375
- background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
376
- border-radius: 6px;
377
- cursor: pointer;
378
- transition: all 0.3s ease;
379
- border-left: 3px solid #007bff;
380
- display: flex;
381
- align-items: center;
382
- }
383
-
384
- .folder-header:hover {
385
- background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
386
- transform: translateX(5px);
387
- }
388
-
389
- .folder-header:focus {
390
- outline: 2px solid #007bff;
391
- outline-offset: 2px;
392
- }
393
-
394
- .folder-actions {
395
- margin-left: auto;
396
- display: flex;
397
- gap: 6px;
398
- flex-shrink: 0;
399
- }
400
-
401
- .folder-icon {
402
- margin-right: 10px;
403
- font-size: 1.3em;
404
- transition: transform 0.3s ease;
405
- }
406
-
407
- .folder-content {
408
- margin-left: 20px;
409
- margin-top: 10px;
410
- }
411
-
412
- .file-item {
413
- display: flex;
414
- justify-content: space-between;
415
- align-items: center;
416
- padding: 12px 15px;
417
- background: #f8f9fa;
418
- border-radius: 6px;
419
- margin: 8px 0;
420
- border-left: 3px solid #6c757d;
421
- transition: all 0.3s ease;
422
- }
423
-
424
- .file-item:hover {
425
- background: #e9ecef;
426
- transform: translateX(5px);
427
- }
428
-
429
- .file-info {
430
- flex: 1;
431
- }
432
-
433
- .file-name {
434
- font-weight: 600;
435
- color: #333;
436
- margin-bottom: 4px;
437
- display: flex;
438
- align-items: center;
439
- gap: 8px;
440
- }
441
-
442
- .file-meta {
443
- font-size: 12px;
444
- color: #6c757d;
445
- margin-top: 4px;
446
- }
447
-
448
- /* ===========================
449
- UTILITY CLASSES
450
- =========================== */
451
- code {
452
- background: #f8f9fa;
453
- padding: 3px 8px;
454
- border-radius: 4px;
455
- font-family: 'Courier New', monospace;
456
- color: #e83e8c;
457
- font-size: 0.9em;
458
- }
459
-
460
- pre {
461
- background: #f8f9fa;
462
- padding: 15px;
463
- border-radius: 6px;
464
- overflow-x: auto;
465
- font-size: 13px;
466
- border: 1px solid #dee2e6;
467
- }
468
-
469
- /* ===========================
470
- RESPONSIVE DESIGN
471
- =========================== */
472
- @media (max-width: 768px) {
473
- body {
474
- padding: 10px;
475
- }
476
-
477
- .app-header {
478
- flex-direction: column;
479
- gap: 15px;
480
- text-align: center;
481
- }
482
-
483
- .app-header h1 {
484
- font-size: 1.5em;
485
- }
486
-
487
- .company-logo {
488
- height: 40px;
489
- }
490
-
491
- .company-name {
492
- font-size: 1em;
493
- }
494
-
495
- .container {
496
- padding: 20px;
497
- }
498
-
499
- .config-grid {
500
- grid-template-columns: 1fr;
501
- }
502
-
503
- h2 {
504
- font-size: 1.3em;
505
- }
506
-
507
- .button-group {
508
- flex-direction: column;
509
- }
510
-
511
- .button-group button {
512
- width: 100%;
513
- margin-right: 0;
514
- }
515
-
516
- .file-item {
517
- flex-direction: column;
518
- align-items: flex-start;
519
- gap: 10px;
520
- }
521
-
522
- .file-item > div:last-child {
523
- width: 100%;
524
- }
525
-
526
- .file-item button {
527
- width: 100%;
528
- }
529
-
530
- .footer-content {
531
- flex-direction: column;
532
- gap: 8px;
533
- }
534
-
535
- .footer-content .separator {
536
- display: none;
537
- }
538
- }
539
-
540
- @media (max-width: 480px) {
541
- .app-header h1 {
542
- font-size: 1.3em;
543
- }
544
-
545
- button {
546
- padding: 10px 16px;
547
- font-size: 13px;
548
- }
549
-
550
- .company-logo {
551
- height: 35px;
552
- }
553
- }
554
-
555
- /* ===========================
556
- ACCESSIBILITY
557
- =========================== */
558
- @media (prefers-reduced-motion: reduce) {
559
- * {
560
- animation: none !important;
561
- transition: none !important;
562
- }
563
- }
564
-
565
- /* Focus visible for keyboard navigation */
566
- button:focus-visible,
567
- input:focus-visible,
568
- select:focus-visible,
569
- .folder-header:focus-visible,
570
- .company-link:focus-visible {
571
- outline: 2px solid #007bff;
572
- outline-offset: 2px;
573
- }
574
-
575
- /* High contrast mode support */
576
- @media (prefers-contrast: high) {
577
- .container,
578
- .app-header,
579
- .app-footer {
580
- border: 2px solid #000;
581
- }
582
-
583
- button {
584
- border: 2px solid #000;
585
- }
586
- }