truthound-dashboard 1.4.4__py3-none-any.whl → 1.5.0__py3-none-any.whl
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.
- truthound_dashboard/api/alerts.py +75 -86
- truthound_dashboard/api/anomaly.py +7 -13
- truthound_dashboard/api/cross_alerts.py +38 -52
- truthound_dashboard/api/drift.py +49 -59
- truthound_dashboard/api/drift_monitor.py +234 -79
- truthound_dashboard/api/enterprise_sampling.py +498 -0
- truthound_dashboard/api/history.py +57 -5
- truthound_dashboard/api/lineage.py +3 -48
- truthound_dashboard/api/maintenance.py +104 -49
- truthound_dashboard/api/mask.py +1 -2
- truthound_dashboard/api/middleware.py +2 -1
- truthound_dashboard/api/model_monitoring.py +435 -311
- truthound_dashboard/api/notifications.py +227 -191
- truthound_dashboard/api/notifications_advanced.py +21 -20
- truthound_dashboard/api/observability.py +586 -0
- truthound_dashboard/api/plugins.py +2 -433
- truthound_dashboard/api/profile.py +199 -37
- truthound_dashboard/api/quality_reporter.py +701 -0
- truthound_dashboard/api/reports.py +7 -16
- truthound_dashboard/api/router.py +66 -0
- truthound_dashboard/api/rule_suggestions.py +5 -5
- truthound_dashboard/api/scan.py +17 -19
- truthound_dashboard/api/schedules.py +85 -50
- truthound_dashboard/api/schema_evolution.py +6 -6
- truthound_dashboard/api/schema_watcher.py +667 -0
- truthound_dashboard/api/sources.py +98 -27
- truthound_dashboard/api/tiering.py +1323 -0
- truthound_dashboard/api/triggers.py +14 -11
- truthound_dashboard/api/validations.py +12 -11
- truthound_dashboard/api/versioning.py +1 -6
- truthound_dashboard/core/__init__.py +129 -3
- truthound_dashboard/core/actions/__init__.py +62 -0
- truthound_dashboard/core/actions/custom.py +426 -0
- truthound_dashboard/core/actions/notifications.py +910 -0
- truthound_dashboard/core/actions/storage.py +472 -0
- truthound_dashboard/core/actions/webhook.py +281 -0
- truthound_dashboard/core/anomaly.py +262 -67
- truthound_dashboard/core/anomaly_explainer.py +4 -3
- truthound_dashboard/core/backends/__init__.py +67 -0
- truthound_dashboard/core/backends/base.py +299 -0
- truthound_dashboard/core/backends/errors.py +191 -0
- truthound_dashboard/core/backends/factory.py +423 -0
- truthound_dashboard/core/backends/mock_backend.py +451 -0
- truthound_dashboard/core/backends/truthound_backend.py +718 -0
- truthound_dashboard/core/checkpoint/__init__.py +87 -0
- truthound_dashboard/core/checkpoint/adapters.py +814 -0
- truthound_dashboard/core/checkpoint/checkpoint.py +491 -0
- truthound_dashboard/core/checkpoint/runner.py +270 -0
- truthound_dashboard/core/connections.py +437 -10
- truthound_dashboard/core/converters/__init__.py +14 -0
- truthound_dashboard/core/converters/truthound.py +620 -0
- truthound_dashboard/core/cross_alerts.py +540 -320
- truthound_dashboard/core/datasource_factory.py +1672 -0
- truthound_dashboard/core/drift_monitor.py +216 -20
- truthound_dashboard/core/enterprise_sampling.py +1291 -0
- truthound_dashboard/core/interfaces/__init__.py +225 -0
- truthound_dashboard/core/interfaces/actions.py +652 -0
- truthound_dashboard/core/interfaces/base.py +247 -0
- truthound_dashboard/core/interfaces/checkpoint.py +676 -0
- truthound_dashboard/core/interfaces/protocols.py +664 -0
- truthound_dashboard/core/interfaces/reporters.py +650 -0
- truthound_dashboard/core/interfaces/routing.py +646 -0
- truthound_dashboard/core/interfaces/triggers.py +619 -0
- truthound_dashboard/core/lineage.py +407 -71
- truthound_dashboard/core/model_monitoring.py +431 -3
- truthound_dashboard/core/notifications/base.py +4 -0
- truthound_dashboard/core/notifications/channels.py +501 -1203
- truthound_dashboard/core/notifications/deduplication/__init__.py +81 -115
- truthound_dashboard/core/notifications/deduplication/service.py +131 -348
- truthound_dashboard/core/notifications/dispatcher.py +202 -11
- truthound_dashboard/core/notifications/escalation/__init__.py +119 -106
- truthound_dashboard/core/notifications/escalation/engine.py +168 -358
- truthound_dashboard/core/notifications/routing/__init__.py +88 -128
- truthound_dashboard/core/notifications/routing/engine.py +90 -317
- truthound_dashboard/core/notifications/stats_aggregator.py +246 -1
- truthound_dashboard/core/notifications/throttling/__init__.py +67 -50
- truthound_dashboard/core/notifications/throttling/builder.py +117 -255
- truthound_dashboard/core/notifications/truthound_adapter.py +842 -0
- truthound_dashboard/core/phase5/collaboration.py +1 -1
- truthound_dashboard/core/plugins/lifecycle/__init__.py +0 -13
- truthound_dashboard/core/quality_reporter.py +1359 -0
- truthound_dashboard/core/report_history.py +0 -6
- truthound_dashboard/core/reporters/__init__.py +175 -14
- truthound_dashboard/core/reporters/adapters.py +943 -0
- truthound_dashboard/core/reporters/base.py +0 -3
- truthound_dashboard/core/reporters/builtin/__init__.py +18 -0
- truthound_dashboard/core/reporters/builtin/csv_reporter.py +111 -0
- truthound_dashboard/core/reporters/builtin/html_reporter.py +270 -0
- truthound_dashboard/core/reporters/builtin/json_reporter.py +127 -0
- truthound_dashboard/core/reporters/compat.py +266 -0
- truthound_dashboard/core/reporters/csv_reporter.py +2 -35
- truthound_dashboard/core/reporters/factory.py +526 -0
- truthound_dashboard/core/reporters/interfaces.py +745 -0
- truthound_dashboard/core/reporters/registry.py +1 -10
- truthound_dashboard/core/scheduler.py +165 -0
- truthound_dashboard/core/schema_evolution.py +3 -3
- truthound_dashboard/core/schema_watcher.py +1528 -0
- truthound_dashboard/core/services.py +595 -76
- truthound_dashboard/core/store_manager.py +810 -0
- truthound_dashboard/core/streaming_anomaly.py +169 -4
- truthound_dashboard/core/tiering.py +1309 -0
- truthound_dashboard/core/triggers/evaluators.py +178 -8
- truthound_dashboard/core/truthound_adapter.py +2620 -197
- truthound_dashboard/core/unified_alerts.py +23 -20
- truthound_dashboard/db/__init__.py +8 -0
- truthound_dashboard/db/database.py +8 -2
- truthound_dashboard/db/models.py +944 -25
- truthound_dashboard/db/repository.py +2 -0
- truthound_dashboard/main.py +11 -0
- truthound_dashboard/schemas/__init__.py +177 -16
- truthound_dashboard/schemas/base.py +44 -23
- truthound_dashboard/schemas/collaboration.py +19 -6
- truthound_dashboard/schemas/cross_alerts.py +19 -3
- truthound_dashboard/schemas/drift.py +61 -55
- truthound_dashboard/schemas/drift_monitor.py +67 -23
- truthound_dashboard/schemas/enterprise_sampling.py +653 -0
- truthound_dashboard/schemas/lineage.py +0 -33
- truthound_dashboard/schemas/mask.py +10 -8
- truthound_dashboard/schemas/model_monitoring.py +89 -10
- truthound_dashboard/schemas/notifications_advanced.py +13 -0
- truthound_dashboard/schemas/observability.py +453 -0
- truthound_dashboard/schemas/plugins.py +0 -280
- truthound_dashboard/schemas/profile.py +154 -247
- truthound_dashboard/schemas/quality_reporter.py +403 -0
- truthound_dashboard/schemas/reports.py +2 -2
- truthound_dashboard/schemas/rule_suggestion.py +8 -1
- truthound_dashboard/schemas/scan.py +4 -24
- truthound_dashboard/schemas/schedule.py +11 -3
- truthound_dashboard/schemas/schema_watcher.py +727 -0
- truthound_dashboard/schemas/source.py +17 -2
- truthound_dashboard/schemas/tiering.py +822 -0
- truthound_dashboard/schemas/triggers.py +16 -0
- truthound_dashboard/schemas/unified_alerts.py +7 -0
- truthound_dashboard/schemas/validation.py +0 -13
- truthound_dashboard/schemas/validators/base.py +41 -21
- truthound_dashboard/schemas/validators/business_rule_validators.py +244 -0
- truthound_dashboard/schemas/validators/localization_validators.py +273 -0
- truthound_dashboard/schemas/validators/ml_feature_validators.py +308 -0
- truthound_dashboard/schemas/validators/profiling_validators.py +275 -0
- truthound_dashboard/schemas/validators/referential_validators.py +312 -0
- truthound_dashboard/schemas/validators/registry.py +93 -8
- truthound_dashboard/schemas/validators/timeseries_validators.py +389 -0
- truthound_dashboard/schemas/versioning.py +1 -6
- truthound_dashboard/static/index.html +2 -2
- truthound_dashboard-1.5.0.dist-info/METADATA +309 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/RECORD +149 -148
- truthound_dashboard/core/plugins/hooks/__init__.py +0 -63
- truthound_dashboard/core/plugins/hooks/decorators.py +0 -367
- truthound_dashboard/core/plugins/hooks/manager.py +0 -403
- truthound_dashboard/core/plugins/hooks/protocols.py +0 -265
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +0 -584
- truthound_dashboard/core/reporters/junit_reporter.py +0 -233
- truthound_dashboard/core/reporters/markdown_reporter.py +0 -207
- truthound_dashboard/core/reporters/pdf_reporter.py +0 -209
- truthound_dashboard/static/assets/_baseUniq-BcrSP13d.js +0 -1
- truthound_dashboard/static/assets/arc-DlYjKwIL.js +0 -1
- truthound_dashboard/static/assets/architectureDiagram-VXUJARFQ-Bb2drbQM.js +0 -36
- truthound_dashboard/static/assets/blockDiagram-VD42YOAC-BlsPG1CH.js +0 -122
- truthound_dashboard/static/assets/c4Diagram-YG6GDRKO-B9JdUoaC.js +0 -10
- truthound_dashboard/static/assets/channel-Q6mHF1Hd.js +0 -1
- truthound_dashboard/static/assets/chunk-4BX2VUAB-DmyoPVuJ.js +0 -1
- truthound_dashboard/static/assets/chunk-55IACEB6-Bcz6Siv8.js +0 -1
- truthound_dashboard/static/assets/chunk-B4BG7PRW-Br3G5Rum.js +0 -165
- truthound_dashboard/static/assets/chunk-DI55MBZ5-DuM9c23u.js +0 -220
- truthound_dashboard/static/assets/chunk-FMBD7UC4-DNU-5mvT.js +0 -15
- truthound_dashboard/static/assets/chunk-QN33PNHL-Im2yNcmS.js +0 -1
- truthound_dashboard/static/assets/chunk-QZHKN3VN-kZr8XFm1.js +0 -1
- truthound_dashboard/static/assets/chunk-TZMSLE5B-Q__360q_.js +0 -1
- truthound_dashboard/static/assets/classDiagram-2ON5EDUG-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/classDiagram-v2-WZHVMYZB-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/clone-BOt2LwD0.js +0 -1
- truthound_dashboard/static/assets/cose-bilkent-S5V4N54A-CBDw6iac.js +0 -1
- truthound_dashboard/static/assets/dagre-6UL2VRFP-XdKqmmY9.js +0 -4
- truthound_dashboard/static/assets/diagram-PSM6KHXK-DAZ8nx9V.js +0 -24
- truthound_dashboard/static/assets/diagram-QEK2KX5R-BRvDTbGD.js +0 -43
- truthound_dashboard/static/assets/diagram-S2PKOQOG-bQcczUkl.js +0 -24
- truthound_dashboard/static/assets/erDiagram-Q2GNP2WA-DPje7VMN.js +0 -60
- truthound_dashboard/static/assets/flowDiagram-NV44I4VS-B7BVtFVS.js +0 -162
- truthound_dashboard/static/assets/ganttDiagram-JELNMOA3-D6WKSS7U.js +0 -267
- truthound_dashboard/static/assets/gitGraphDiagram-NY62KEGX-D3vtVd3y.js +0 -65
- truthound_dashboard/static/assets/graph-BKgNKZVp.js +0 -1
- truthound_dashboard/static/assets/index-C6JSrkHo.css +0 -1
- truthound_dashboard/static/assets/index-DkU82VsU.js +0 -1800
- truthound_dashboard/static/assets/infoDiagram-WHAUD3N6-DnNCT429.js +0 -2
- truthound_dashboard/static/assets/journeyDiagram-XKPGCS4Q-DGiMozqS.js +0 -139
- truthound_dashboard/static/assets/kanban-definition-3W4ZIXB7-BV2gUgli.js +0 -89
- truthound_dashboard/static/assets/katex-Cu_Erd72.js +0 -261
- truthound_dashboard/static/assets/layout-DI2MfQ5G.js +0 -1
- truthound_dashboard/static/assets/min-DYdgXVcT.js +0 -1
- truthound_dashboard/static/assets/mindmap-definition-VGOIOE7T-C7x4ruxz.js +0 -68
- truthound_dashboard/static/assets/pieDiagram-ADFJNKIX-CAJaAB9f.js +0 -30
- truthound_dashboard/static/assets/quadrantDiagram-AYHSOK5B-DeqwDI46.js +0 -7
- truthound_dashboard/static/assets/requirementDiagram-UZGBJVZJ-e3XDpZIM.js +0 -64
- truthound_dashboard/static/assets/sankeyDiagram-TZEHDZUN-CNnAv5Ux.js +0 -10
- truthound_dashboard/static/assets/sequenceDiagram-WL72ISMW-Dsne-Of3.js +0 -145
- truthound_dashboard/static/assets/stateDiagram-FKZM4ZOC-Ee0sQXyb.js +0 -1
- truthound_dashboard/static/assets/stateDiagram-v2-4FDKWEC3-B26KqW_W.js +0 -1
- truthound_dashboard/static/assets/timeline-definition-IT6M3QCI-DZYi2yl3.js +0 -61
- truthound_dashboard/static/assets/treemap-KMMF4GRG-CY3f8In2.js +0 -128
- truthound_dashboard/static/assets/unmerged_dictionaries-Dd7xcPWG.js +0 -1
- truthound_dashboard/static/assets/xychartDiagram-PRI3JC2R-CS7fydZZ.js +0 -7
- truthound_dashboard-1.4.4.dist-info/METADATA +0 -507
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import{_ as n,s as gi,g as xi,q as Xt,p as di,a as pi,b as fi,l as Nt,H as yi,e as mi,y as bi,E as Ct,D as Yt,F as Ai,K as wi,i as Ci,M as Bt,N as Si,L as Wt,O as zt}from"./index-DkU82VsU.js";var mt=function(){var s=n(function(W,r,u,g){for(u=u||{},g=W.length;g--;u[W[g]]=r);return u},"o"),t=[1,10,12,14,16,18,19,21,23],i=[2,6],e=[1,3],a=[1,5],c=[1,6],d=[1,7],m=[1,5,10,12,14,16,18,19,21,23,34,35,36],b=[1,25],P=[1,26],I=[1,28],R=[1,29],L=[1,30],z=[1,31],F=[1,32],D=[1,33],V=[1,34],f=[1,35],C=[1,36],l=[1,37],M=[1,43],B=[1,42],U=[1,47],X=[1,50],h=[1,10,12,14,16,18,19,21,23,34,35,36],k=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],w=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],S=[1,64],$={trace:n(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:n(function(r,u,g,x,A,o,nt){var p=o.length-1;switch(A){case 5:x.setOrientation(o[p]);break;case 9:x.setDiagramTitle(o[p].text.trim());break;case 12:x.setLineData({text:"",type:"text"},o[p]);break;case 13:x.setLineData(o[p-1],o[p]);break;case 14:x.setBarData({text:"",type:"text"},o[p]);break;case 15:x.setBarData(o[p-1],o[p]);break;case 16:this.$=o[p].trim(),x.setAccTitle(this.$);break;case 17:case 18:this.$=o[p].trim(),x.setAccDescription(this.$);break;case 19:this.$=o[p-1];break;case 20:this.$=[Number(o[p-2]),...o[p]];break;case 21:this.$=[Number(o[p])];break;case 22:x.setXAxisTitle(o[p]);break;case 23:x.setXAxisTitle(o[p-1]);break;case 24:x.setXAxisTitle({type:"text",text:""});break;case 25:x.setXAxisBand(o[p]);break;case 26:x.setXAxisRangeData(Number(o[p-2]),Number(o[p]));break;case 27:this.$=o[p-1];break;case 28:this.$=[o[p-2],...o[p]];break;case 29:this.$=[o[p]];break;case 30:x.setYAxisTitle(o[p]);break;case 31:x.setYAxisTitle(o[p-1]);break;case 32:x.setYAxisTitle({type:"text",text:""});break;case 33:x.setYAxisRangeData(Number(o[p-2]),Number(o[p]));break;case 37:this.$={text:o[p],type:"text"};break;case 38:this.$={text:o[p],type:"text"};break;case 39:this.$={text:o[p],type:"markdown"};break;case 40:this.$=o[p];break;case 41:this.$=o[p-1]+""+o[p];break}},"anonymous"),table:[s(t,i,{3:1,4:2,7:4,5:e,34:a,35:c,36:d}),{1:[3]},s(t,i,{4:2,7:4,3:8,5:e,34:a,35:c,36:d}),s(t,i,{4:2,7:4,6:9,3:10,5:e,8:[1,11],34:a,35:c,36:d}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},s(m,[2,34]),s(m,[2,35]),s(m,[2,36]),{1:[2,1]},s(t,i,{4:2,7:4,3:21,5:e,34:a,35:c,36:d}),{1:[2,3]},s(m,[2,5]),s(t,[2,7],{4:22,34:a,35:c,36:d}),{11:23,37:24,38:b,39:P,40:27,41:I,42:R,43:L,44:z,45:F,46:D,47:V,48:f,49:C,50:l},{11:39,13:38,24:M,27:B,29:40,30:41,37:24,38:b,39:P,40:27,41:I,42:R,43:L,44:z,45:F,46:D,47:V,48:f,49:C,50:l},{11:45,15:44,27:U,33:46,37:24,38:b,39:P,40:27,41:I,42:R,43:L,44:z,45:F,46:D,47:V,48:f,49:C,50:l},{11:49,17:48,24:X,37:24,38:b,39:P,40:27,41:I,42:R,43:L,44:z,45:F,46:D,47:V,48:f,49:C,50:l},{11:52,17:51,24:X,37:24,38:b,39:P,40:27,41:I,42:R,43:L,44:z,45:F,46:D,47:V,48:f,49:C,50:l},{20:[1,53]},{22:[1,54]},s(h,[2,18]),{1:[2,2]},s(h,[2,8]),s(h,[2,9]),s(k,[2,37],{40:55,41:I,42:R,43:L,44:z,45:F,46:D,47:V,48:f,49:C,50:l}),s(k,[2,38]),s(k,[2,39]),s(w,[2,40]),s(w,[2,42]),s(w,[2,43]),s(w,[2,44]),s(w,[2,45]),s(w,[2,46]),s(w,[2,47]),s(w,[2,48]),s(w,[2,49]),s(w,[2,50]),s(w,[2,51]),s(h,[2,10]),s(h,[2,22],{30:41,29:56,24:M,27:B}),s(h,[2,24]),s(h,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:b,39:P,40:27,41:I,42:R,43:L,44:z,45:F,46:D,47:V,48:f,49:C,50:l},s(h,[2,11]),s(h,[2,30],{33:60,27:U}),s(h,[2,32]),{31:[1,61]},s(h,[2,12]),{17:62,24:X},{25:63,27:S},s(h,[2,14]),{17:65,24:X},s(h,[2,16]),s(h,[2,17]),s(w,[2,41]),s(h,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},s(h,[2,31]),{27:[1,69]},s(h,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},s(h,[2,15]),s(h,[2,26]),s(h,[2,27]),{11:59,32:72,37:24,38:b,39:P,40:27,41:I,42:R,43:L,44:z,45:F,46:D,47:V,48:f,49:C,50:l},s(h,[2,33]),s(h,[2,19]),{25:73,27:S},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:n(function(r,u){if(u.recoverable)this.trace(r);else{var g=new Error(r);throw g.hash=u,g}},"parseError"),parse:n(function(r){var u=this,g=[0],x=[],A=[null],o=[],nt=this.table,p="",lt=0,Et=0,hi=2,It=1,li=o.slice.call(arguments,1),_=Object.create(this.lexer),Y={yy:{}};for(var dt in this.yy)Object.prototype.hasOwnProperty.call(this.yy,dt)&&(Y.yy[dt]=this.yy[dt]);_.setInput(r,Y.yy),Y.yy.lexer=_,Y.yy.parser=this,typeof _.yylloc>"u"&&(_.yylloc={});var pt=_.yylloc;o.push(pt);var ci=_.options&&_.options.ranges;typeof Y.yy.parseError=="function"?this.parseError=Y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ui(v){g.length=g.length-2*v,A.length=A.length-v,o.length=o.length-v}n(ui,"popStack");function Vt(){var v;return v=x.pop()||_.lex()||It,typeof v!="number"&&(v instanceof Array&&(x=v,v=x.pop()),v=u.symbols_[v]||v),v}n(Vt,"lex");for(var T,H,E,ft,q={},ct,O,Mt,ut;;){if(H=g[g.length-1],this.defaultActions[H]?E=this.defaultActions[H]:((T===null||typeof T>"u")&&(T=Vt()),E=nt[H]&&nt[H][T]),typeof E>"u"||!E.length||!E[0]){var yt="";ut=[];for(ct in nt[H])this.terminals_[ct]&&ct>hi&&ut.push("'"+this.terminals_[ct]+"'");_.showPosition?yt="Parse error on line "+(lt+1)+`:
|
|
2
|
-
`+_.showPosition()+`
|
|
3
|
-
Expecting `+ut.join(", ")+", got '"+(this.terminals_[T]||T)+"'":yt="Parse error on line "+(lt+1)+": Unexpected "+(T==It?"end of input":"'"+(this.terminals_[T]||T)+"'"),this.parseError(yt,{text:_.match,token:this.terminals_[T]||T,line:_.yylineno,loc:pt,expected:ut})}if(E[0]instanceof Array&&E.length>1)throw new Error("Parse Error: multiple actions possible at state: "+H+", token: "+T);switch(E[0]){case 1:g.push(T),A.push(_.yytext),o.push(_.yylloc),g.push(E[1]),T=null,Et=_.yyleng,p=_.yytext,lt=_.yylineno,pt=_.yylloc;break;case 2:if(O=this.productions_[E[1]][1],q.$=A[A.length-O],q._$={first_line:o[o.length-(O||1)].first_line,last_line:o[o.length-1].last_line,first_column:o[o.length-(O||1)].first_column,last_column:o[o.length-1].last_column},ci&&(q._$.range=[o[o.length-(O||1)].range[0],o[o.length-1].range[1]]),ft=this.performAction.apply(q,[p,Et,lt,Y.yy,E[1],A,o].concat(li)),typeof ft<"u")return ft;O&&(g=g.slice(0,-1*O*2),A=A.slice(0,-1*O),o=o.slice(0,-1*O)),g.push(this.productions_[E[1]][0]),A.push(q.$),o.push(q._$),Mt=nt[g[g.length-2]][g[g.length-1]],g.push(Mt);break;case 3:return!0}}return!0},"parse")},Lt=function(){var W={EOF:1,parseError:n(function(u,g){if(this.yy.parser)this.yy.parser.parseError(u,g);else throw new Error(u)},"parseError"),setInput:n(function(r,u){return this.yy=u||this.yy||{},this._input=r,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:n(function(){var r=this._input[0];this.yytext+=r,this.yyleng++,this.offset++,this.match+=r,this.matched+=r;var u=r.match(/(?:\r\n?|\n).*/g);return u?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),r},"input"),unput:n(function(r){var u=r.length,g=r.split(/(?:\r\n?|\n)/g);this._input=r+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-u),this.offset-=u;var x=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),g.length-1&&(this.yylineno-=g.length-1);var A=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:g?(g.length===x.length?this.yylloc.first_column:0)+x[x.length-g.length].length-g[0].length:this.yylloc.first_column-u},this.options.ranges&&(this.yylloc.range=[A[0],A[0]+this.yyleng-u]),this.yyleng=this.yytext.length,this},"unput"),more:n(function(){return this._more=!0,this},"more"),reject:n(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
|
|
4
|
-
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:n(function(r){this.unput(this.match.slice(r))},"less"),pastInput:n(function(){var r=this.matched.substr(0,this.matched.length-this.match.length);return(r.length>20?"...":"")+r.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:n(function(){var r=this.match;return r.length<20&&(r+=this._input.substr(0,20-r.length)),(r.substr(0,20)+(r.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:n(function(){var r=this.pastInput(),u=new Array(r.length+1).join("-");return r+this.upcomingInput()+`
|
|
5
|
-
`+u+"^"},"showPosition"),test_match:n(function(r,u){var g,x,A;if(this.options.backtrack_lexer&&(A={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(A.yylloc.range=this.yylloc.range.slice(0))),x=r[0].match(/(?:\r\n?|\n).*/g),x&&(this.yylineno+=x.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:x?x[x.length-1].length-x[x.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+r[0].length},this.yytext+=r[0],this.match+=r[0],this.matches=r,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(r[0].length),this.matched+=r[0],g=this.performAction.call(this,this.yy,this,u,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),g)return g;if(this._backtrack){for(var o in A)this[o]=A[o];return!1}return!1},"test_match"),next:n(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var r,u,g,x;this._more||(this.yytext="",this.match="");for(var A=this._currentRules(),o=0;o<A.length;o++)if(g=this._input.match(this.rules[A[o]]),g&&(!u||g[0].length>u[0].length)){if(u=g,x=o,this.options.backtrack_lexer){if(r=this.test_match(g,A[o]),r!==!1)return r;if(this._backtrack){u=!1;continue}else return!1}else if(!this.options.flex)break}return u?(r=this.test_match(u,A[x]),r!==!1?r:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text.
|
|
6
|
-
`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:n(function(){var u=this.next();return u||this.lex()},"lex"),begin:n(function(u){this.conditionStack.push(u)},"begin"),popState:n(function(){var u=this.conditionStack.length-1;return u>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:n(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:n(function(u){return u=this.conditionStack.length-1-Math.abs(u||0),u>=0?this.conditionStack[u]:"INITIAL"},"topState"),pushState:n(function(u){this.begin(u)},"pushState"),stateStackSize:n(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:n(function(u,g,x,A){switch(x){case 0:break;case 1:break;case 2:return this.popState(),34;case 3:return this.popState(),34;case 4:return 34;case 5:break;case 6:return 10;case 7:return this.pushState("acc_title"),19;case 8:return this.popState(),"acc_title_value";case 9:return this.pushState("acc_descr"),21;case 10:return this.popState(),"acc_descr_value";case 11:this.pushState("acc_descr_multiline");break;case 12:this.popState();break;case 13:return"acc_descr_multiline_value";case 14:return 5;case 15:return 5;case 16:return 8;case 17:return this.pushState("axis_data"),"X_AXIS";case 18:return this.pushState("axis_data"),"Y_AXIS";case 19:return this.pushState("axis_band_data"),24;case 20:return 31;case 21:return this.pushState("data"),16;case 22:return this.pushState("data"),18;case 23:return this.pushState("data_inner"),24;case 24:return 27;case 25:return this.popState(),26;case 26:this.popState();break;case 27:this.pushState("string");break;case 28:this.popState();break;case 29:return"STR";case 30:return 24;case 31:return 26;case 32:return 43;case 33:return"COLON";case 34:return 44;case 35:return 28;case 36:return 45;case 37:return 46;case 38:return 48;case 39:return 50;case 40:return 47;case 41:return 41;case 42:return 49;case 43:return 42;case 44:break;case 45:return 35;case 46:return 36}},"anonymous"),rules:[/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:(\r?\n))/i,/^(?:(\r?\n))/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:title\b)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:\{)/i,/^(?:[^\}]*)/i,/^(?:xychart-beta\b)/i,/^(?:xychart\b)/i,/^(?:(?:vertical|horizontal))/i,/^(?:x-axis\b)/i,/^(?:y-axis\b)/i,/^(?:\[)/i,/^(?:-->)/i,/^(?:line\b)/i,/^(?:bar\b)/i,/^(?:\[)/i,/^(?:[+-]?(?:\d+(?:\.\d+)?|\.\d+))/i,/^(?:\])/i,/^(?:(?:`\) \{ this\.pushState\(md_string\); \}\n<md_string>\(\?:\(\?!`"\)\.\)\+ \{ return MD_STR; \}\n<md_string>\(\?:`))/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:\[)/i,/^(?:\])/i,/^(?:[A-Za-z]+)/i,/^(?::)/i,/^(?:\+)/i,/^(?:,)/i,/^(?:=)/i,/^(?:\*)/i,/^(?:#)/i,/^(?:[\_])/i,/^(?:\.)/i,/^(?:&)/i,/^(?:-)/i,/^(?:[0-9]+)/i,/^(?:\s+)/i,/^(?:;)/i,/^(?:$)/i],conditions:{data_inner:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,18,21,22,24,25,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},data:{rules:[0,1,3,4,5,6,7,9,11,14,15,16,17,18,21,22,23,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},axis_band_data:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,18,21,22,25,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},axis_data:{rules:[0,1,2,4,5,6,7,9,11,14,15,16,17,18,19,20,21,22,24,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0},acc_descr_multiline:{rules:[12,13],inclusive:!1},acc_descr:{rules:[10],inclusive:!1},acc_title:{rules:[8],inclusive:!1},title:{rules:[],inclusive:!1},md_string:{rules:[],inclusive:!1},string:{rules:[28,29],inclusive:!1},INITIAL:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,18,21,22,26,27,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],inclusive:!0}}};return W}();$.lexer=Lt;function N(){this.yy={}}return n(N,"Parser"),N.prototype=$,$.Parser=N,new N}();mt.parser=mt;var _i=mt;function bt(s){return s.type==="bar"}n(bt,"isBarPlot");function St(s){return s.type==="band"}n(St,"isBandAxisData");function G(s){return s.type==="linear"}n(G,"isLinearAxisData");var j,Ht=(j=class{constructor(t){this.parentGroup=t}getMaxDimension(t,i){if(!this.parentGroup)return{width:t.reduce((c,d)=>Math.max(d.length,c),0)*i,height:i};const e={width:0,height:0},a=this.parentGroup.append("g").attr("visibility","hidden").attr("font-size",i);for(const c of t){const d=Si(a,1,c),m=d?d.width:c.length*i,b=d?d.height:i;e.width=Math.max(e.width,m),e.height=Math.max(e.height,b)}return a.remove(),e}},n(j,"TextDimensionCalculatorWithFont"),j),Ft=.7,Ot=.2,Q,Ut=(Q=class{constructor(t,i,e,a){this.axisConfig=t,this.title=i,this.textDimensionCalculator=e,this.axisThemeConfig=a,this.boundingRect={x:0,y:0,width:0,height:0},this.axisPosition="left",this.showTitle=!1,this.showLabel=!1,this.showTick=!1,this.showAxisLine=!1,this.outerPadding=0,this.titleTextHeight=0,this.labelTextHeight=0,this.range=[0,10],this.boundingRect={x:0,y:0,width:0,height:0},this.axisPosition="left"}setRange(t){this.range=t,this.axisPosition==="left"||this.axisPosition==="right"?this.boundingRect.height=t[1]-t[0]:this.boundingRect.width=t[1]-t[0],this.recalculateScale()}getRange(){return[this.range[0]+this.outerPadding,this.range[1]-this.outerPadding]}setAxisPosition(t){this.axisPosition=t,this.setRange(this.range)}getTickDistance(){const t=this.getRange();return Math.abs(t[0]-t[1])/this.getTickValues().length}getAxisOuterPadding(){return this.outerPadding}getLabelDimension(){return this.textDimensionCalculator.getMaxDimension(this.getTickValues().map(t=>t.toString()),this.axisConfig.labelFontSize)}recalculateOuterPaddingToDrawBar(){Ft*this.getTickDistance()>this.outerPadding*2&&(this.outerPadding=Math.floor(Ft*this.getTickDistance()/2)),this.recalculateScale()}calculateSpaceIfDrawnHorizontally(t){let i=t.height;if(this.axisConfig.showAxisLine&&i>this.axisConfig.axisLineWidth&&(i-=this.axisConfig.axisLineWidth,this.showAxisLine=!0),this.axisConfig.showLabel){const e=this.getLabelDimension(),a=Ot*t.width;this.outerPadding=Math.min(e.width/2,a);const c=e.height+this.axisConfig.labelPadding*2;this.labelTextHeight=e.height,c<=i&&(i-=c,this.showLabel=!0)}if(this.axisConfig.showTick&&i>=this.axisConfig.tickLength&&(this.showTick=!0,i-=this.axisConfig.tickLength),this.axisConfig.showTitle&&this.title){const e=this.textDimensionCalculator.getMaxDimension([this.title],this.axisConfig.titleFontSize),a=e.height+this.axisConfig.titlePadding*2;this.titleTextHeight=e.height,a<=i&&(i-=a,this.showTitle=!0)}this.boundingRect.width=t.width,this.boundingRect.height=t.height-i}calculateSpaceIfDrawnVertical(t){let i=t.width;if(this.axisConfig.showAxisLine&&i>this.axisConfig.axisLineWidth&&(i-=this.axisConfig.axisLineWidth,this.showAxisLine=!0),this.axisConfig.showLabel){const e=this.getLabelDimension(),a=Ot*t.height;this.outerPadding=Math.min(e.height/2,a);const c=e.width+this.axisConfig.labelPadding*2;c<=i&&(i-=c,this.showLabel=!0)}if(this.axisConfig.showTick&&i>=this.axisConfig.tickLength&&(this.showTick=!0,i-=this.axisConfig.tickLength),this.axisConfig.showTitle&&this.title){const e=this.textDimensionCalculator.getMaxDimension([this.title],this.axisConfig.titleFontSize),a=e.height+this.axisConfig.titlePadding*2;this.titleTextHeight=e.height,a<=i&&(i-=a,this.showTitle=!0)}this.boundingRect.width=t.width-i,this.boundingRect.height=t.height}calculateSpace(t){return this.axisPosition==="left"||this.axisPosition==="right"?this.calculateSpaceIfDrawnVertical(t):this.calculateSpaceIfDrawnHorizontally(t),this.recalculateScale(),{width:this.boundingRect.width,height:this.boundingRect.height}}setBoundingBoxXY(t){this.boundingRect.x=t.x,this.boundingRect.y=t.y}getDrawableElementsForLeftAxis(){const t=[];if(this.showAxisLine){const i=this.boundingRect.x+this.boundingRect.width-this.axisConfig.axisLineWidth/2;t.push({type:"path",groupTexts:["left-axis","axisl-line"],data:[{path:`M ${i},${this.boundingRect.y} L ${i},${this.boundingRect.y+this.boundingRect.height} `,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&t.push({type:"text",groupTexts:["left-axis","label"],data:this.getTickValues().map(i=>({text:i.toString(),x:this.boundingRect.x+this.boundingRect.width-(this.showLabel?this.axisConfig.labelPadding:0)-(this.showTick?this.axisConfig.tickLength:0)-(this.showAxisLine?this.axisConfig.axisLineWidth:0),y:this.getScaleValue(i),fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"middle",horizontalPos:"right"}))}),this.showTick){const i=this.boundingRect.x+this.boundingRect.width-(this.showAxisLine?this.axisConfig.axisLineWidth:0);t.push({type:"path",groupTexts:["left-axis","ticks"],data:this.getTickValues().map(e=>({path:`M ${i},${this.getScaleValue(e)} L ${i-this.axisConfig.tickLength},${this.getScaleValue(e)}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&t.push({type:"text",groupTexts:["left-axis","title"],data:[{text:this.title,x:this.boundingRect.x+this.axisConfig.titlePadding,y:this.boundingRect.y+this.boundingRect.height/2,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:270,verticalPos:"top",horizontalPos:"center"}]}),t}getDrawableElementsForBottomAxis(){const t=[];if(this.showAxisLine){const i=this.boundingRect.y+this.axisConfig.axisLineWidth/2;t.push({type:"path",groupTexts:["bottom-axis","axis-line"],data:[{path:`M ${this.boundingRect.x},${i} L ${this.boundingRect.x+this.boundingRect.width},${i}`,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&t.push({type:"text",groupTexts:["bottom-axis","label"],data:this.getTickValues().map(i=>({text:i.toString(),x:this.getScaleValue(i),y:this.boundingRect.y+this.axisConfig.labelPadding+(this.showTick?this.axisConfig.tickLength:0)+(this.showAxisLine?this.axisConfig.axisLineWidth:0),fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}))}),this.showTick){const i=this.boundingRect.y+(this.showAxisLine?this.axisConfig.axisLineWidth:0);t.push({type:"path",groupTexts:["bottom-axis","ticks"],data:this.getTickValues().map(e=>({path:`M ${this.getScaleValue(e)},${i} L ${this.getScaleValue(e)},${i+this.axisConfig.tickLength}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&t.push({type:"text",groupTexts:["bottom-axis","title"],data:[{text:this.title,x:this.range[0]+(this.range[1]-this.range[0])/2,y:this.boundingRect.y+this.boundingRect.height-this.axisConfig.titlePadding-this.titleTextHeight,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}]}),t}getDrawableElementsForTopAxis(){const t=[];if(this.showAxisLine){const i=this.boundingRect.y+this.boundingRect.height-this.axisConfig.axisLineWidth/2;t.push({type:"path",groupTexts:["top-axis","axis-line"],data:[{path:`M ${this.boundingRect.x},${i} L ${this.boundingRect.x+this.boundingRect.width},${i}`,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&t.push({type:"text",groupTexts:["top-axis","label"],data:this.getTickValues().map(i=>({text:i.toString(),x:this.getScaleValue(i),y:this.boundingRect.y+(this.showTitle?this.titleTextHeight+this.axisConfig.titlePadding*2:0)+this.axisConfig.labelPadding,fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}))}),this.showTick){const i=this.boundingRect.y;t.push({type:"path",groupTexts:["top-axis","ticks"],data:this.getTickValues().map(e=>({path:`M ${this.getScaleValue(e)},${i+this.boundingRect.height-(this.showAxisLine?this.axisConfig.axisLineWidth:0)} L ${this.getScaleValue(e)},${i+this.boundingRect.height-this.axisConfig.tickLength-(this.showAxisLine?this.axisConfig.axisLineWidth:0)}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&t.push({type:"text",groupTexts:["top-axis","title"],data:[{text:this.title,x:this.boundingRect.x+this.boundingRect.width/2,y:this.boundingRect.y+this.axisConfig.titlePadding,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}]}),t}getDrawableElements(){if(this.axisPosition==="left")return this.getDrawableElementsForLeftAxis();if(this.axisPosition==="right")throw Error("Drawing of right axis is not implemented");return this.axisPosition==="bottom"?this.getDrawableElementsForBottomAxis():this.axisPosition==="top"?this.getDrawableElementsForTopAxis():[]}},n(Q,"BaseAxis"),Q),K,ki=(K=class extends Ut{constructor(t,i,e,a,c){super(t,a,c,i),this.categories=e,this.scale=Bt().domain(this.categories).range(this.getRange())}setRange(t){super.setRange(t)}recalculateScale(){this.scale=Bt().domain(this.categories).range(this.getRange()).paddingInner(1).paddingOuter(0).align(.5),Nt.trace("BandAxis axis final categories, range: ",this.categories,this.getRange())}getTickValues(){return this.categories}getScaleValue(t){return this.scale(t)??this.getRange()[0]}},n(K,"BandAxis"),K),Z,Ti=(Z=class extends Ut{constructor(t,i,e,a,c){super(t,a,c,i),this.domain=e,this.scale=Wt().domain(this.domain).range(this.getRange())}getTickValues(){return this.scale.ticks()}recalculateScale(){const t=[...this.domain];this.axisPosition==="left"&&t.reverse(),this.scale=Wt().domain(t).range(this.getRange())}getScaleValue(t){return this.scale(t)}},n(Z,"LinearAxis"),Z);function At(s,t,i,e){const a=new Ht(e);return St(s)?new ki(t,i,s.categories,s.title,a):new Ti(t,i,[s.min,s.max],s.title,a)}n(At,"getAxis");var J,Ri=(J=class{constructor(t,i,e,a){this.textDimensionCalculator=t,this.chartConfig=i,this.chartData=e,this.chartThemeConfig=a,this.boundingRect={x:0,y:0,width:0,height:0},this.showChartTitle=!1}setBoundingBoxXY(t){this.boundingRect.x=t.x,this.boundingRect.y=t.y}calculateSpace(t){const i=this.textDimensionCalculator.getMaxDimension([this.chartData.title],this.chartConfig.titleFontSize),e=Math.max(i.width,t.width),a=i.height+2*this.chartConfig.titlePadding;return i.width<=e&&i.height<=a&&this.chartConfig.showTitle&&this.chartData.title&&(this.boundingRect.width=e,this.boundingRect.height=a,this.showChartTitle=!0),{width:this.boundingRect.width,height:this.boundingRect.height}}getDrawableElements(){const t=[];return this.showChartTitle&&t.push({groupTexts:["chart-title"],type:"text",data:[{fontSize:this.chartConfig.titleFontSize,text:this.chartData.title,verticalPos:"middle",horizontalPos:"center",x:this.boundingRect.x+this.boundingRect.width/2,y:this.boundingRect.y+this.boundingRect.height/2,fill:this.chartThemeConfig.titleColor,rotation:0}]}),t}},n(J,"ChartTitle"),J);function $t(s,t,i,e){const a=new Ht(e);return new Ri(a,s,t,i)}n($t,"getChartTitleComponent");var tt,Di=(tt=class{constructor(t,i,e,a,c){this.plotData=t,this.xAxis=i,this.yAxis=e,this.orientation=a,this.plotIndex=c}getDrawableElement(){const t=this.plotData.data.map(e=>[this.xAxis.getScaleValue(e[0]),this.yAxis.getScaleValue(e[1])]);let i;return this.orientation==="horizontal"?i=zt().y(e=>e[0]).x(e=>e[1])(t):i=zt().x(e=>e[0]).y(e=>e[1])(t),i?[{groupTexts:["plot",`line-plot-${this.plotIndex}`],type:"path",data:[{path:i,strokeFill:this.plotData.strokeFill,strokeWidth:this.plotData.strokeWidth}]}]:[]}},n(tt,"LinePlot"),tt),it,vi=(it=class{constructor(t,i,e,a,c,d){this.barData=t,this.boundingRect=i,this.xAxis=e,this.yAxis=a,this.orientation=c,this.plotIndex=d}getDrawableElement(){const t=this.barData.data.map(c=>[this.xAxis.getScaleValue(c[0]),this.yAxis.getScaleValue(c[1])]),e=Math.min(this.xAxis.getAxisOuterPadding()*2,this.xAxis.getTickDistance())*(1-.05),a=e/2;return this.orientation==="horizontal"?[{groupTexts:["plot",`bar-plot-${this.plotIndex}`],type:"rect",data:t.map(c=>({x:this.boundingRect.x,y:c[0]-a,height:e,width:c[1]-this.boundingRect.x,fill:this.barData.fill,strokeWidth:0,strokeFill:this.barData.fill}))}]:[{groupTexts:["plot",`bar-plot-${this.plotIndex}`],type:"rect",data:t.map(c=>({x:c[0]-a,y:c[1],width:e,height:this.boundingRect.y+this.boundingRect.height-c[1],fill:this.barData.fill,strokeWidth:0,strokeFill:this.barData.fill}))}]}},n(it,"BarPlot"),it),et,Pi=(et=class{constructor(t,i,e){this.chartConfig=t,this.chartData=i,this.chartThemeConfig=e,this.boundingRect={x:0,y:0,width:0,height:0}}setAxes(t,i){this.xAxis=t,this.yAxis=i}setBoundingBoxXY(t){this.boundingRect.x=t.x,this.boundingRect.y=t.y}calculateSpace(t){return this.boundingRect.width=t.width,this.boundingRect.height=t.height,{width:this.boundingRect.width,height:this.boundingRect.height}}getDrawableElements(){if(!(this.xAxis&&this.yAxis))throw Error("Axes must be passed to render Plots");const t=[];for(const[i,e]of this.chartData.plots.entries())switch(e.type){case"line":{const a=new Di(e,this.xAxis,this.yAxis,this.chartConfig.chartOrientation,i);t.push(...a.getDrawableElement())}break;case"bar":{const a=new vi(e,this.boundingRect,this.xAxis,this.yAxis,this.chartConfig.chartOrientation,i);t.push(...a.getDrawableElement())}break}return t}},n(et,"BasePlot"),et);function qt(s,t,i){return new Pi(s,t,i)}n(qt,"getPlotComponent");var st,Li=(st=class{constructor(t,i,e,a){this.chartConfig=t,this.chartData=i,this.componentStore={title:$t(t,i,e,a),plot:qt(t,i,e),xAxis:At(i.xAxis,t.xAxis,{titleColor:e.xAxisTitleColor,labelColor:e.xAxisLabelColor,tickColor:e.xAxisTickColor,axisLineColor:e.xAxisLineColor},a),yAxis:At(i.yAxis,t.yAxis,{titleColor:e.yAxisTitleColor,labelColor:e.yAxisLabelColor,tickColor:e.yAxisTickColor,axisLineColor:e.yAxisLineColor},a)}}calculateVerticalSpace(){let t=this.chartConfig.width,i=this.chartConfig.height,e=0,a=0,c=Math.floor(t*this.chartConfig.plotReservedSpacePercent/100),d=Math.floor(i*this.chartConfig.plotReservedSpacePercent/100),m=this.componentStore.plot.calculateSpace({width:c,height:d});t-=m.width,i-=m.height,m=this.componentStore.title.calculateSpace({width:this.chartConfig.width,height:i}),a=m.height,i-=m.height,this.componentStore.xAxis.setAxisPosition("bottom"),m=this.componentStore.xAxis.calculateSpace({width:t,height:i}),i-=m.height,this.componentStore.yAxis.setAxisPosition("left"),m=this.componentStore.yAxis.calculateSpace({width:t,height:i}),e=m.width,t-=m.width,t>0&&(c+=t,t=0),i>0&&(d+=i,i=0),this.componentStore.plot.calculateSpace({width:c,height:d}),this.componentStore.plot.setBoundingBoxXY({x:e,y:a}),this.componentStore.xAxis.setRange([e,e+c]),this.componentStore.xAxis.setBoundingBoxXY({x:e,y:a+d}),this.componentStore.yAxis.setRange([a,a+d]),this.componentStore.yAxis.setBoundingBoxXY({x:0,y:a}),this.chartData.plots.some(b=>bt(b))&&this.componentStore.xAxis.recalculateOuterPaddingToDrawBar()}calculateHorizontalSpace(){let t=this.chartConfig.width,i=this.chartConfig.height,e=0,a=0,c=0,d=Math.floor(t*this.chartConfig.plotReservedSpacePercent/100),m=Math.floor(i*this.chartConfig.plotReservedSpacePercent/100),b=this.componentStore.plot.calculateSpace({width:d,height:m});t-=b.width,i-=b.height,b=this.componentStore.title.calculateSpace({width:this.chartConfig.width,height:i}),e=b.height,i-=b.height,this.componentStore.xAxis.setAxisPosition("left"),b=this.componentStore.xAxis.calculateSpace({width:t,height:i}),t-=b.width,a=b.width,this.componentStore.yAxis.setAxisPosition("top"),b=this.componentStore.yAxis.calculateSpace({width:t,height:i}),i-=b.height,c=e+b.height,t>0&&(d+=t,t=0),i>0&&(m+=i,i=0),this.componentStore.plot.calculateSpace({width:d,height:m}),this.componentStore.plot.setBoundingBoxXY({x:a,y:c}),this.componentStore.yAxis.setRange([a,a+d]),this.componentStore.yAxis.setBoundingBoxXY({x:a,y:e}),this.componentStore.xAxis.setRange([c,c+m]),this.componentStore.xAxis.setBoundingBoxXY({x:0,y:c}),this.chartData.plots.some(P=>bt(P))&&this.componentStore.xAxis.recalculateOuterPaddingToDrawBar()}calculateSpace(){this.chartConfig.chartOrientation==="horizontal"?this.calculateHorizontalSpace():this.calculateVerticalSpace()}getDrawableElement(){this.calculateSpace();const t=[];this.componentStore.plot.setAxes(this.componentStore.xAxis,this.componentStore.yAxis);for(const i of Object.values(this.componentStore))t.push(...i.getDrawableElements());return t}},n(st,"Orchestrator"),st),at,Ei=(at=class{static build(t,i,e,a){return new Li(t,i,e,a).getDrawableElement()}},n(at,"XYChartBuilder"),at),ot=0,Gt,rt=Tt(),ht=kt(),y=Rt(),wt=ht.plotColorPalette.split(",").map(s=>s.trim()),gt=!1,_t=!1;function kt(){const s=wi(),t=Ct();return Yt(s.xyChart,t.themeVariables.xyChart)}n(kt,"getChartDefaultThemeConfig");function Tt(){const s=Ct();return Yt(Ai.xyChart,s.xyChart)}n(Tt,"getChartDefaultConfig");function Rt(){return{yAxis:{type:"linear",title:"",min:1/0,max:-1/0},xAxis:{type:"band",title:"",categories:[]},title:"",plots:[]}}n(Rt,"getChartDefaultData");function xt(s){const t=Ct();return Ci(s.trim(),t)}n(xt,"textSanitizer");function jt(s){Gt=s}n(jt,"setTmpSVGG");function Qt(s){s==="horizontal"?rt.chartOrientation="horizontal":rt.chartOrientation="vertical"}n(Qt,"setOrientation");function Kt(s){y.xAxis.title=xt(s.text)}n(Kt,"setXAxisTitle");function Dt(s,t){y.xAxis={type:"linear",title:y.xAxis.title,min:s,max:t},gt=!0}n(Dt,"setXAxisRangeData");function Zt(s){y.xAxis={type:"band",title:y.xAxis.title,categories:s.map(t=>xt(t.text))},gt=!0}n(Zt,"setXAxisBand");function Jt(s){y.yAxis.title=xt(s.text)}n(Jt,"setYAxisTitle");function ti(s,t){y.yAxis={type:"linear",title:y.yAxis.title,min:s,max:t},_t=!0}n(ti,"setYAxisRangeData");function ii(s){const t=Math.min(...s),i=Math.max(...s),e=G(y.yAxis)?y.yAxis.min:1/0,a=G(y.yAxis)?y.yAxis.max:-1/0;y.yAxis={type:"linear",title:y.yAxis.title,min:Math.min(e,t),max:Math.max(a,i)}}n(ii,"setYAxisRangeFromPlotData");function vt(s){let t=[];if(s.length===0)return t;if(!gt){const i=G(y.xAxis)?y.xAxis.min:1/0,e=G(y.xAxis)?y.xAxis.max:-1/0;Dt(Math.min(i,1),Math.max(e,s.length))}if(_t||ii(s),St(y.xAxis)&&(t=y.xAxis.categories.map((i,e)=>[i,s[e]])),G(y.xAxis)){const i=y.xAxis.min,e=y.xAxis.max,a=(e-i)/(s.length-1),c=[];for(let d=i;d<=e;d+=a)c.push(`${d}`);t=c.map((d,m)=>[d,s[m]])}return t}n(vt,"transformDataWithoutCategory");function Pt(s){return wt[s===0?0:s%wt.length]}n(Pt,"getPlotColorFromPalette");function ei(s,t){const i=vt(t);y.plots.push({type:"line",strokeFill:Pt(ot),strokeWidth:2,data:i}),ot++}n(ei,"setLineData");function si(s,t){const i=vt(t);y.plots.push({type:"bar",fill:Pt(ot),data:i}),ot++}n(si,"setBarData");function ai(){if(y.plots.length===0)throw Error("No Plot to render, please provide a plot with some data");return y.title=Xt(),Ei.build(rt,y,ht,Gt)}n(ai,"getDrawableElem");function ni(){return ht}n(ni,"getChartThemeConfig");function oi(){return rt}n(oi,"getChartConfig");function ri(){return y}n(ri,"getXYChartData");var Ii=n(function(){bi(),ot=0,rt=Tt(),y=Rt(),ht=kt(),wt=ht.plotColorPalette.split(",").map(s=>s.trim()),gt=!1,_t=!1},"clear"),Vi={getDrawableElem:ai,clear:Ii,setAccTitle:fi,getAccTitle:pi,setDiagramTitle:di,getDiagramTitle:Xt,getAccDescription:xi,setAccDescription:gi,setOrientation:Qt,setXAxisTitle:Kt,setXAxisRangeData:Dt,setXAxisBand:Zt,setYAxisTitle:Jt,setYAxisRangeData:ti,setLineData:ei,setBarData:si,setTmpSVGG:jt,getChartThemeConfig:ni,getChartConfig:oi,getXYChartData:ri},Mi=n((s,t,i,e)=>{const a=e.db,c=a.getChartThemeConfig(),d=a.getChartConfig(),m=a.getXYChartData().plots[0].data.map(f=>f[1]);function b(f){return f==="top"?"text-before-edge":"middle"}n(b,"getDominantBaseLine");function P(f){return f==="left"?"start":f==="right"?"end":"middle"}n(P,"getTextAnchor");function I(f){return`translate(${f.x}, ${f.y}) rotate(${f.rotation||0})`}n(I,"getTextTransformation"),Nt.debug(`Rendering xychart chart
|
|
7
|
-
`+s);const R=yi(t),L=R.append("g").attr("class","main"),z=L.append("rect").attr("width",d.width).attr("height",d.height).attr("class","background");mi(R,d.height,d.width,!0),R.attr("viewBox",`0 0 ${d.width} ${d.height}`),z.attr("fill",c.backgroundColor),a.setTmpSVGG(R.append("g").attr("class","mermaid-tmp-group"));const F=a.getDrawableElem(),D={};function V(f){let C=L,l="";for(const[M]of f.entries()){let B=L;M>0&&D[l]&&(B=D[l]),l+=f[M],C=D[l],C||(C=D[l]=B.append("g").attr("class",f[M]))}return C}n(V,"getGroup");for(const f of F){if(f.data.length===0)continue;const C=V(f.groupTexts);switch(f.type){case"rect":if(C.selectAll("rect").data(f.data).enter().append("rect").attr("x",l=>l.x).attr("y",l=>l.y).attr("width",l=>l.width).attr("height",l=>l.height).attr("fill",l=>l.fill).attr("stroke",l=>l.strokeFill).attr("stroke-width",l=>l.strokeWidth),d.showDataLabel)if(d.chartOrientation==="horizontal"){let l=function(h,k){const{data:w,label:S}=h;return k*S.length*M<=w.width-10};n(l,"fitsHorizontally");const M=.7,B=f.data.map((h,k)=>({data:h,label:m[k].toString()})).filter(h=>h.data.width>0&&h.data.height>0),U=B.map(h=>{const{data:k}=h;let w=k.height*.7;for(;!l(h,w)&&w>0;)w-=1;return w}),X=Math.floor(Math.min(...U));C.selectAll("text").data(B).enter().append("text").attr("x",h=>h.data.x+h.data.width-10).attr("y",h=>h.data.y+h.data.height/2).attr("text-anchor","end").attr("dominant-baseline","middle").attr("fill","black").attr("font-size",`${X}px`).text(h=>h.label)}else{let l=function(h,k,w){const{data:S,label:$}=h,N=k*$.length*.7,W=S.x+S.width/2,r=W-N/2,u=W+N/2,g=r>=S.x&&u<=S.x+S.width,x=S.y+w+k<=S.y+S.height;return g&&x};n(l,"fitsInBar");const M=10,B=f.data.map((h,k)=>({data:h,label:m[k].toString()})).filter(h=>h.data.width>0&&h.data.height>0),U=B.map(h=>{const{data:k,label:w}=h;let S=k.width/(w.length*.7);for(;!l(h,S,M)&&S>0;)S-=1;return S}),X=Math.floor(Math.min(...U));C.selectAll("text").data(B).enter().append("text").attr("x",h=>h.data.x+h.data.width/2).attr("y",h=>h.data.y+M).attr("text-anchor","middle").attr("dominant-baseline","hanging").attr("fill","black").attr("font-size",`${X}px`).text(h=>h.label)}break;case"text":C.selectAll("text").data(f.data).enter().append("text").attr("x",0).attr("y",0).attr("fill",l=>l.fill).attr("font-size",l=>l.fontSize).attr("dominant-baseline",l=>b(l.verticalPos)).attr("text-anchor",l=>P(l.horizontalPos)).attr("transform",l=>I(l)).text(l=>l.text);break;case"path":C.selectAll("path").data(f.data).enter().append("path").attr("d",l=>l.path).attr("fill",l=>l.fill?l.fill:"none").attr("stroke",l=>l.strokeFill).attr("stroke-width",l=>l.strokeWidth);break}}},"draw"),Bi={draw:Mi},zi={parser:_i,db:Vi,renderer:Bi};export{zi as diagram};
|
|
@@ -1,507 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: truthound-dashboard
|
|
3
|
-
Version: 1.4.4
|
|
4
|
-
Summary: Open-source data quality dashboard - GX Cloud alternative
|
|
5
|
-
Author-email: Truthound Team <team@truthound.dev>
|
|
6
|
-
License-Expression: Apache-2.0
|
|
7
|
-
License-File: LICENSE
|
|
8
|
-
Keywords: dashboard,data-quality,monitoring,truthound,validation
|
|
9
|
-
Classifier: Development Status :: 3 - Alpha
|
|
10
|
-
Classifier: Framework :: FastAPI
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Intended Audience :: Science/Research
|
|
13
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
-
Classifier: Operating System :: OS Independent
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Classifier: Topic :: Database
|
|
18
|
-
Classifier: Topic :: Scientific/Engineering
|
|
19
|
-
Requires-Python: >=3.11
|
|
20
|
-
Requires-Dist: aiosmtplib>=3.0.0
|
|
21
|
-
Requires-Dist: aiosqlite>=0.19.0
|
|
22
|
-
Requires-Dist: apscheduler>=3.10.0
|
|
23
|
-
Requires-Dist: cryptography>=41.0.0
|
|
24
|
-
Requires-Dist: fastapi>=0.110.0
|
|
25
|
-
Requires-Dist: httpx>=0.26.0
|
|
26
|
-
Requires-Dist: jinja2>=3.1.0
|
|
27
|
-
Requires-Dist: numpy>=1.24.0
|
|
28
|
-
Requires-Dist: polars>=0.20.0
|
|
29
|
-
Requires-Dist: pydantic-settings>=2.1.0
|
|
30
|
-
Requires-Dist: pydantic>=2.5.0
|
|
31
|
-
Requires-Dist: pyyaml>=6.0.0
|
|
32
|
-
Requires-Dist: rich>=13.0.0
|
|
33
|
-
Requires-Dist: sqlalchemy[asyncio]>=2.0.0
|
|
34
|
-
Requires-Dist: truthound>=1.0.5
|
|
35
|
-
Requires-Dist: typer>=0.9.0
|
|
36
|
-
Requires-Dist: uvicorn[standard]>=0.27.0
|
|
37
|
-
Provides-Extra: dev
|
|
38
|
-
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
39
|
-
Requires-Dist: httpx>=0.26.0; extra == 'dev'
|
|
40
|
-
Requires-Dist: mypy>=1.8.0; extra == 'dev'
|
|
41
|
-
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
42
|
-
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
43
|
-
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
44
|
-
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
45
|
-
Provides-Extra: redis
|
|
46
|
-
Requires-Dist: redis>=5.0.0; extra == 'redis'
|
|
47
|
-
Provides-Extra: translate
|
|
48
|
-
Description-Content-Type: text/markdown
|
|
49
|
-
|
|
50
|
-
# truthound-dashboard
|
|
51
|
-
|
|
52
|
-
> **⚠️ UNDER ACTIVE DEVELOPMENT**: This project is currently in active development. APIs and features may change without notice. Not recommended for production use yet.
|
|
53
|
-
|
|
54
|
-
[](https://pypi.org/project/truthound-dashboard/)
|
|
55
|
-
[](https://www.python.org/downloads/)
|
|
56
|
-
[](https://opensource.org/licenses/Apache-2.0)
|
|
57
|
-
[](https://intlayer.org)
|
|
58
|
-
[](https://pepy.tech/project/truthound-dashboard)
|
|
59
|
-
|
|
60
|
-
A web-based data quality monitoring dashboard for [truthound](https://github.com/seadonggyun4/truthound).
|
|
61
|
-
|
|
62
|
-
[Documentation](https://truthound.netlify.app) | [PyPI](https://pypi.org/project/truthound-dashboard/) | [Live Demo](https://truthound-dashborad.vercel.app/)
|
|
63
|
-
|
|
64
|
-
> **Try the Live Demo**: Experience the full dashboard interface with mock data at [truthound-dashborad.vercel.app](https://truthound-dashborad.vercel.app/). No installation required!
|
|
65
|
-
>
|
|
66
|
-
> **Note**: The demo page displays in English only. Language selection is not available in the demo due to Intlayer's runtime requirements. When you install and run truthound-dashboard locally, full multi-language support (English, Korean, and AI-translated languages) works as expected.
|
|
67
|
-
|
|
68
|
-
## Overview
|
|
69
|
-
<img width="300" height="300" alt="Truthound_icon" src="https://github.com/user-attachments/assets/90d9e806-8895-45ec-97dc-f8300da4d997" />
|
|
70
|
-
|
|
71
|
-
truthound-dashboard provides a graphical interface for managing data sources, executing validations, tracking historical results, scheduling automated checks, and configuring notifications. It serves as an alternative to commercial data quality platforms.
|
|
72
|
-
|
|
73
|
-
## Feature Comparison with GX Cloud
|
|
74
|
-
|
|
75
|
-
| Feature | GX Cloud (Paid) | truthound-dashboard |
|
|
76
|
-
|---------|-----------------|---------------------|
|
|
77
|
-
| Data Source Management | Available | Available |
|
|
78
|
-
| Schema Learning | Available | Available |
|
|
79
|
-
| Validation Execution | Available | Available |
|
|
80
|
-
| Validator Registry | Available | Available (150+ validators) |
|
|
81
|
-
| Validation History | Available | Available |
|
|
82
|
-
| Scheduled Validations | Available | Available |
|
|
83
|
-
| Slack Notifications | Available | Available |
|
|
84
|
-
| Email Notifications | Available | Available |
|
|
85
|
-
| Webhook Notifications | Available | Available |
|
|
86
|
-
| Drift Detection | Available | Available (5 methods) |
|
|
87
|
-
| Data Profiling | Available | Available |
|
|
88
|
-
| PII Scan | Available | Available (GDPR/CCPA/LGPD) |
|
|
89
|
-
| Data Masking | Available | Available (redact/hash/fake) |
|
|
90
|
-
| Anomaly Detection | Limited | Available (6 algorithms) |
|
|
91
|
-
| Data Lineage | Available | Available (3 viz options) |
|
|
92
|
-
| Model Monitoring | Available | Available |
|
|
93
|
-
| Reports & Export | Available | Available (6 formats) |
|
|
94
|
-
| Plugins Marketplace | Not Available | Available |
|
|
95
|
-
| Maintenance Tools | Limited | Available |
|
|
96
|
-
| Dark Mode | Available | Available |
|
|
97
|
-
| Multi-language | Limited | 2 languages (en, ko) + AI translation CLI |
|
|
98
|
-
| License | Commercial | Apache 2.0 |
|
|
99
|
-
|
|
100
|
-
## Requirements
|
|
101
|
-
|
|
102
|
-
- Python 3.11 or higher
|
|
103
|
-
- truthound >= 1.0.5
|
|
104
|
-
|
|
105
|
-
## Installation
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
pip install truthound-dashboard
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
This command automatically installs [truthound](https://github.com/seadonggyun4/truthound) as a dependency.
|
|
112
|
-
|
|
113
|
-
## Usage
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
# Start the dashboard server (default port: 8765)
|
|
117
|
-
truthound serve
|
|
118
|
-
|
|
119
|
-
# Specify a custom port
|
|
120
|
-
truthound serve --port 9000
|
|
121
|
-
|
|
122
|
-
# Enable development mode with hot reload
|
|
123
|
-
truthound serve --reload
|
|
124
|
-
|
|
125
|
-
# Disable automatic browser opening
|
|
126
|
-
truthound serve --no-browser
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
The dashboard interface is accessible at `http://localhost:8765`.
|
|
130
|
-
|
|
131
|
-
## Implemented Features
|
|
132
|
-
|
|
133
|
-
### Data Source Management
|
|
134
|
-
- Supported file formats: CSV, Parquet, JSON
|
|
135
|
-
- Supported databases (13 connectors): PostgreSQL, MySQL, SQLite, BigQuery, Snowflake, Redshift, Databricks, Oracle, SQL Server, Spark
|
|
136
|
-
- Connection validation and management UI
|
|
137
|
-
- Dynamic configuration forms per source type
|
|
138
|
-
|
|
139
|
-
### Schema Management
|
|
140
|
-
- Automated schema generation using `th.learn`
|
|
141
|
-
- Manual schema editing in YAML format
|
|
142
|
-
|
|
143
|
-
### Validation
|
|
144
|
-
- On-demand validation execution using `th.check`
|
|
145
|
-
- 150+ validators across 15 categories (schema, completeness, uniqueness, distribution, string, datetime, aggregate, cross-table, multi-column, query, table, geospatial, drift, anomaly, privacy)
|
|
146
|
-
- Per-validator parameter configuration with UI
|
|
147
|
-
- Persistent storage of validation results
|
|
148
|
-
- Issue classification by severity (Critical, High, Medium, Low)
|
|
149
|
-
- Advanced options: column filtering, min_severity, parallel execution, SQL pushdown
|
|
150
|
-
- ML-based caching layer for expensive operations
|
|
151
|
-
|
|
152
|
-
### Anomaly Detection
|
|
153
|
-
- 6 ML algorithms: IsolationForest, LocalOutlierFactor, DBSCAN, OneClassSVM, EllipticEnvelope, Ensemble
|
|
154
|
-
- Streaming anomaly detection support
|
|
155
|
-
- Explainability with feature contribution analysis
|
|
156
|
-
- Batch detection with progress tracking
|
|
157
|
-
- Algorithm comparison and agreement scoring
|
|
158
|
-
|
|
159
|
-
### Drift Monitoring
|
|
160
|
-
- 5 detection methods: Kolmogorov-Smirnov, Population Stability Index (PSI), Chi-Square, Jensen-Shannon, Auto
|
|
161
|
-
- 4 sampling strategies: Random, Stratified, Reservoir, Systematic
|
|
162
|
-
- Column-level distribution comparison
|
|
163
|
-
- Drift trend visualization and alerting
|
|
164
|
-
- Root cause analysis and remediation suggestions
|
|
165
|
-
|
|
166
|
-
### Data Lineage
|
|
167
|
-
- Interactive lineage graph visualization (D3.js/Mermaid/Cytoscape)
|
|
168
|
-
- Column-level lineage tracking
|
|
169
|
-
- Impact analysis (upstream/downstream)
|
|
170
|
-
- OpenLineage standard integration
|
|
171
|
-
- Webhook support for lineage events
|
|
172
|
-
- Performance optimization with lazy loading and virtualization
|
|
173
|
-
|
|
174
|
-
### Schema Evolution
|
|
175
|
-
- Automatic schema change detection
|
|
176
|
-
- Breaking vs non-breaking change classification
|
|
177
|
-
- Version timeline and comparison
|
|
178
|
-
- Change notification support
|
|
179
|
-
|
|
180
|
-
### Profile Comparison
|
|
181
|
-
- Profile-to-profile comparison
|
|
182
|
-
- Time-series trend analysis
|
|
183
|
-
- Quality metric visualization (null%, unique%)
|
|
184
|
-
- Historical profile snapshots
|
|
185
|
-
|
|
186
|
-
### Rule Suggestions
|
|
187
|
-
- Profile-based automatic rule generation
|
|
188
|
-
- Confidence scoring (high/medium/low)
|
|
189
|
-
- Bulk rule application
|
|
190
|
-
- Category-based filtering (completeness, uniqueness, distribution, string, datetime)
|
|
191
|
-
|
|
192
|
-
### Reports & Export
|
|
193
|
-
- 6 formats: HTML, PDF, CSV, JSON, Excel, Markdown
|
|
194
|
-
- Customizable themes for HTML/PDF reports
|
|
195
|
-
- Statistics dashboard (total reports, size, downloads, avg generation time)
|
|
196
|
-
- Search and filtering (by name, format, status)
|
|
197
|
-
- Report lifecycle management with automatic expiration
|
|
198
|
-
- Download tracking and batch cleanup
|
|
199
|
-
- Integration with validation schedules and notifications
|
|
200
|
-
|
|
201
|
-
### Plugins & Extensions
|
|
202
|
-
- Plugin marketplace for community extensions
|
|
203
|
-
- 4 plugin types: Validators, Reporters, Connectors, Transformers
|
|
204
|
-
- 4 security levels: Trusted, Verified, Unverified, Sandboxed
|
|
205
|
-
- Custom validator creation with UI (severity, category, parameters)
|
|
206
|
-
- Custom reporter creation with template support
|
|
207
|
-
- Plugin lifecycle management (install, enable, disable, uninstall)
|
|
208
|
-
- Filter by type and status
|
|
209
|
-
|
|
210
|
-
### Maintenance & System Health
|
|
211
|
-
- Auto maintenance scheduling with enable/disable toggle
|
|
212
|
-
- Retention policies with configurable ranges:
|
|
213
|
-
- Validation history: 1-365 days
|
|
214
|
-
- Profile snapshots: 1-100 per source
|
|
215
|
-
- Notification logs: 1-365 days
|
|
216
|
-
- Manual operations: cleanup, vacuum, cache clear
|
|
217
|
-
- Cache statistics monitoring (total, valid, expired entries, hit rate)
|
|
218
|
-
- Database optimization (VACUUM/ANALYZE)
|
|
219
|
-
- Real-time configuration updates
|
|
220
|
-
|
|
221
|
-
### Validation History
|
|
222
|
-
- Historical record of validation results
|
|
223
|
-
- Trend visualization
|
|
224
|
-
- Result versioning with 4 strategies (Incremental, Semantic, Timestamp, GitLike)
|
|
225
|
-
- Version comparison and rollback support
|
|
226
|
-
|
|
227
|
-
### Scheduling
|
|
228
|
-
- Cron-based scheduling using APScheduler
|
|
229
|
-
- Schedule controls: pause, resume, immediate execution
|
|
230
|
-
|
|
231
|
-
### Notifications
|
|
232
|
-
- Supported channels: Slack, Email, Webhook
|
|
233
|
-
- Configurable notification rules based on validation outcomes
|
|
234
|
-
- Notification delivery logs
|
|
235
|
-
|
|
236
|
-
### Advanced Notifications
|
|
237
|
-
- 9 provider channels: Slack, Email, Webhook, Discord, Telegram, PagerDuty, OpsGenie, Microsoft Teams, GitHub
|
|
238
|
-
- Rule-based routing with 11+ rule types (severity, issue count, pass rate, time window, tag, data asset, metadata, status, error)
|
|
239
|
-
- Deduplication: 4 window strategies (Sliding, Tumbling, Session, Adaptive), 6 policies
|
|
240
|
-
- Throttling: 5 methods (TokenBucket, LeakyBucket, FixedWindow, SlidingWindow, Adaptive)
|
|
241
|
-
- Multi-level escalation with state machine
|
|
242
|
-
- Incident management and acknowledgment
|
|
243
|
-
|
|
244
|
-
### Unified Alerts
|
|
245
|
-
- Cross-feature alert aggregation (validation, drift, anomaly, schema changes)
|
|
246
|
-
- Severity-based filtering (Critical, High, Medium, Low)
|
|
247
|
-
- Alert correlation and grouping
|
|
248
|
-
- Action tracking (acknowledged, resolved)
|
|
249
|
-
|
|
250
|
-
### Cross-Table Validation
|
|
251
|
-
- Referential integrity checks
|
|
252
|
-
- Foreign key validation
|
|
253
|
-
- SQL-based cross-table queries
|
|
254
|
-
- Automated trigger configuration
|
|
255
|
-
|
|
256
|
-
### Model Monitoring
|
|
257
|
-
- ML model performance tracking
|
|
258
|
-
- Metric monitoring (accuracy, precision, recall, F1, AUC-ROC)
|
|
259
|
-
- Alert rules for model degradation
|
|
260
|
-
- Model registration and versioning
|
|
261
|
-
|
|
262
|
-
### Automated Triggers
|
|
263
|
-
- Data change detection triggers
|
|
264
|
-
- Composite triggers (AND/OR combinations)
|
|
265
|
-
- Cron-based scheduling
|
|
266
|
-
- Interval-based execution
|
|
267
|
-
- Preview and testing support
|
|
268
|
-
|
|
269
|
-
### Drift Detection
|
|
270
|
-
- Dataset comparison using `th.compare`
|
|
271
|
-
- 5 detection methods: Kolmogorov-Smirnov (KS), Population Stability Index (PSI), Chi-Square, Jensen-Shannon (JS), Auto
|
|
272
|
-
- 4 sampling strategies: Random, Stratified, Reservoir, Systematic
|
|
273
|
-
- Column-level distribution comparison with visualizations
|
|
274
|
-
- Drift trend monitoring and alerting
|
|
275
|
-
- Root cause analysis and remediation suggestions
|
|
276
|
-
- Large dataset support with chunked processing
|
|
277
|
-
|
|
278
|
-
### Data Profiling
|
|
279
|
-
- Statistical profiling using `th.profile`
|
|
280
|
-
- Column-level statistics
|
|
281
|
-
- Sample size configuration for large datasets
|
|
282
|
-
|
|
283
|
-
### PII Scan
|
|
284
|
-
- Personal data detection using `th.scan`
|
|
285
|
-
- Supported PII types: email, phone, SSN, credit card, IP address, and more
|
|
286
|
-
- Regulation compliance: GDPR, CCPA, LGPD
|
|
287
|
-
- Configurable confidence threshold
|
|
288
|
-
|
|
289
|
-
### Data Masking
|
|
290
|
-
- Sensitive data protection using `th.mask`
|
|
291
|
-
- Three masking strategies: redact (asterisks), hash (SHA256), fake (realistic data)
|
|
292
|
-
- Auto-detection of PII columns
|
|
293
|
-
- Multiple output formats: CSV, Parquet, JSON
|
|
294
|
-
|
|
295
|
-
### Business Glossary
|
|
296
|
-
- Business term definitions with categories
|
|
297
|
-
- Term relationships (synonyms, related terms)
|
|
298
|
-
- Term lifecycle management (draft, approved, deprecated)
|
|
299
|
-
- Change history tracking
|
|
300
|
-
|
|
301
|
-
### Data Catalog
|
|
302
|
-
- Data asset registration (tables, files, APIs)
|
|
303
|
-
- Column-level metadata management
|
|
304
|
-
- Column-to-term mapping
|
|
305
|
-
- Quality score tracking
|
|
306
|
-
- Sensitivity classification (public, internal, confidential, restricted)
|
|
307
|
-
- Custom tagging
|
|
308
|
-
|
|
309
|
-
### Collaboration
|
|
310
|
-
- Comments on terms, assets, and columns
|
|
311
|
-
- Activity feed for tracking changes
|
|
312
|
-
|
|
313
|
-
### User Interface
|
|
314
|
-
- Light and dark theme support with system preference detection
|
|
315
|
-
- Internationalization: 2 built-in languages (English, Korean)
|
|
316
|
-
- AI-powered translation CLI to expand to 15+ languages (OpenAI, Anthropic, Mistral, Ollama)
|
|
317
|
-
- Type-safe translations using Intlayer framework
|
|
318
|
-
- Comprehensive E2E test coverage (197+ tests) for all features
|
|
319
|
-
|
|
320
|
-
## Internationalization
|
|
321
|
-
|
|
322
|
-
truthound-dashboard implements internationalization using [Intlayer](https://intlayer.org), a modern i18n framework that provides type-safe translations with component-level content declaration.
|
|
323
|
-
|
|
324
|
-
### Built-in Languages
|
|
325
|
-
|
|
326
|
-
The dashboard ships with **2 fully translated languages**:
|
|
327
|
-
- **English (en)** - Complete UI translation
|
|
328
|
-
- **Korean (ko)** - Complete UI translation
|
|
329
|
-
|
|
330
|
-
These languages are immediately available without additional configuration or setup.
|
|
331
|
-
|
|
332
|
-
### Extending Language Support
|
|
333
|
-
|
|
334
|
-
The dashboard can be extended to support 15+ additional languages using the AI-powered `translate` command. This CLI tool translates all UI content files from the built-in English and Korean to your target language.
|
|
335
|
-
|
|
336
|
-
**Note:** Additional languages are not included in the default installation and must be generated using the translation CLI before deployment.
|
|
337
|
-
|
|
338
|
-
#### Supported AI Providers
|
|
339
|
-
|
|
340
|
-
| Provider | Environment Variable | Models |
|
|
341
|
-
|----------|---------------------|--------|
|
|
342
|
-
| OpenAI | `OPENAI_API_KEY` | GPT-4o, GPT-4o-mini, GPT-4, GPT-3.5 |
|
|
343
|
-
| Anthropic | `ANTHROPIC_API_KEY` | Claude Sonnet 4, Claude Opus 4 |
|
|
344
|
-
| Mistral | `MISTRAL_API_KEY` | Mistral Large, Mistral Small |
|
|
345
|
-
| Ollama | Not required | Llama 3.2, Mistral, Qwen (local execution) |
|
|
346
|
-
|
|
347
|
-
#### Configuration
|
|
348
|
-
|
|
349
|
-
API credentials are configured through environment variables. This approach ensures that sensitive credentials are not exposed in command history or application logs.
|
|
350
|
-
|
|
351
|
-
**Temporary Session Configuration**
|
|
352
|
-
|
|
353
|
-
```bash
|
|
354
|
-
# OpenAI
|
|
355
|
-
export OPENAI_API_KEY=sk-xxxxxxxxxxxx
|
|
356
|
-
truthound translate -l fr -p openai
|
|
357
|
-
|
|
358
|
-
# Anthropic
|
|
359
|
-
export ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxx
|
|
360
|
-
truthound translate -l fr -p anthropic
|
|
361
|
-
|
|
362
|
-
# Mistral
|
|
363
|
-
export MISTRAL_API_KEY=xxxxxxxxxxxx
|
|
364
|
-
truthound translate -l fr -p mistral
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
**Persistent Configuration**
|
|
368
|
-
|
|
369
|
-
```bash
|
|
370
|
-
# Add to shell configuration file (~/.bashrc or ~/.zshrc)
|
|
371
|
-
echo 'export OPENAI_API_KEY=sk-xxxxxxxxxxxx' >> ~/.zshrc
|
|
372
|
-
source ~/.zshrc
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
**Inline Configuration**
|
|
376
|
-
|
|
377
|
-
```bash
|
|
378
|
-
OPENAI_API_KEY=sk-xxx truthound translate -l fr -p openai
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
#### Usage Examples
|
|
382
|
-
|
|
383
|
-
```bash
|
|
384
|
-
# Translate to French using OpenAI
|
|
385
|
-
truthound translate -l fr -p openai
|
|
386
|
-
|
|
387
|
-
# Translate to multiple languages
|
|
388
|
-
truthound translate -l ja,zh,de,fr -p anthropic
|
|
389
|
-
|
|
390
|
-
# Use local Ollama (no API key required)
|
|
391
|
-
truthound translate -l fr -p ollama
|
|
392
|
-
|
|
393
|
-
# Auto-detect available provider
|
|
394
|
-
truthound translate -l fr
|
|
395
|
-
|
|
396
|
-
# Preview files without making changes
|
|
397
|
-
truthound translate -l fr --dry-run
|
|
398
|
-
|
|
399
|
-
# List available providers and their status
|
|
400
|
-
truthound translate --list-providers
|
|
401
|
-
|
|
402
|
-
# List supported language codes
|
|
403
|
-
truthound translate --list-languages
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
#### Security Considerations
|
|
407
|
-
|
|
408
|
-
| Aspect | Risk Level | Description |
|
|
409
|
-
|--------|------------|-------------|
|
|
410
|
-
| Network transmission | None | API keys are used locally and transmitted only to the selected provider |
|
|
411
|
-
| Source code exposure | None | Credentials are injected via environment variables |
|
|
412
|
-
| Build artifact inclusion | None | Only translated content is persisted; credentials are not stored |
|
|
413
|
-
| API communication | Standard | Requests are made directly to provider endpoints using user credentials |
|
|
414
|
-
|
|
415
|
-
#### Supported Languages
|
|
416
|
-
|
|
417
|
-
The translation system supports 36 languages including: Arabic, Bulgarian, Chinese, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hebrew, Hindi, Hungarian, Indonesian, Italian, Japanese, Korean, Latvian, Lithuanian, Malay, Norwegian, Polish, Portuguese, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish, Thai, Turkish, Ukrainian, and Vietnamese.
|
|
418
|
-
|
|
419
|
-
Execute `truthound translate --list-languages` to view the complete list with language codes.
|
|
420
|
-
|
|
421
|
-
## Technology Stack
|
|
422
|
-
|
|
423
|
-
**Backend**
|
|
424
|
-
- FastAPI
|
|
425
|
-
- SQLAlchemy 2.0 (async)
|
|
426
|
-
- SQLite with aiosqlite
|
|
427
|
-
- APScheduler
|
|
428
|
-
- Pydantic 2.x
|
|
429
|
-
|
|
430
|
-
**Frontend**
|
|
431
|
-
- React 18
|
|
432
|
-
- TypeScript
|
|
433
|
-
- Vite
|
|
434
|
-
- TailwindCSS
|
|
435
|
-
- shadcn/ui
|
|
436
|
-
- Zustand
|
|
437
|
-
- [Intlayer](https://intlayer.org) (internationalization)
|
|
438
|
-
|
|
439
|
-
## Development Setup
|
|
440
|
-
|
|
441
|
-
```bash
|
|
442
|
-
# Clone the repository
|
|
443
|
-
git clone https://github.com/seadonggyun4/truthound-dashboard
|
|
444
|
-
cd truthound-dashboard
|
|
445
|
-
|
|
446
|
-
# Install backend dependencies
|
|
447
|
-
pip install -e ".[dev]"
|
|
448
|
-
|
|
449
|
-
# Start the backend server
|
|
450
|
-
truthound serve --reload
|
|
451
|
-
|
|
452
|
-
# In a separate terminal, install frontend dependencies
|
|
453
|
-
cd frontend
|
|
454
|
-
npm install
|
|
455
|
-
|
|
456
|
-
# Start the frontend development server
|
|
457
|
-
npm run dev
|
|
458
|
-
|
|
459
|
-
# Alternative: run with mock API (backend not required)
|
|
460
|
-
npm run dev:mock
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
## Preview with Mock Data
|
|
464
|
-
|
|
465
|
-
To explore the dashboard interface without configuring a backend or data sources, the repository includes a mock mode that simulates API responses using [Mock Service Worker (MSW)](https://mswjs.io/).
|
|
466
|
-
|
|
467
|
-
```bash
|
|
468
|
-
# Clone the repository
|
|
469
|
-
git clone https://github.com/seadonggyun4/truthound-dashboard
|
|
470
|
-
cd truthound-dashboard/frontend
|
|
471
|
-
|
|
472
|
-
# Install dependencies
|
|
473
|
-
npm install
|
|
474
|
-
|
|
475
|
-
# Start the development server with mock data
|
|
476
|
-
npm run dev:mock
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
The mock server provides realistic sample data for all dashboard features, enabling evaluation of the user interface and workflow without external dependencies.
|
|
480
|
-
|
|
481
|
-
## Testing
|
|
482
|
-
|
|
483
|
-
```bash
|
|
484
|
-
# Run tests
|
|
485
|
-
pytest
|
|
486
|
-
|
|
487
|
-
# Run tests with coverage report
|
|
488
|
-
pytest --cov=truthound_dashboard
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
## Documentation
|
|
492
|
-
|
|
493
|
-
Full documentation is available at [https://truthound.netlify.app](https://truthound.netlify.app).
|
|
494
|
-
|
|
495
|
-
- [Getting Started](./docs/getting-started.md)
|
|
496
|
-
- [Features](./docs/features.md)
|
|
497
|
-
- [API Reference](./docs/api.md)
|
|
498
|
-
- [Configuration](./docs/configuration.md)
|
|
499
|
-
|
|
500
|
-
## Related Projects
|
|
501
|
-
|
|
502
|
-
- [truthound](https://github.com/seadonggyun4/truthound) - Core data validation library
|
|
503
|
-
- [truthound-orchestration](https://github.com/seadonggyun4/truthound-orchestration) - Pipeline orchestration integration
|
|
504
|
-
|
|
505
|
-
## License
|
|
506
|
-
|
|
507
|
-
This project is licensed under the Apache License 2.0.
|
|
File without changes
|
{truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|