eyeling 1.13.2 → 1.13.4

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.
@@ -0,0 +1,575 @@
1
+ # ========================================================================================
2
+ # ODRL + DPV risk assessment (Healthcare & Life Sciences) with ranked, explainable output.
3
+ #
4
+ # What this file demonstrates
5
+ # - Models a healthcare / life-sciences data use agreement as an ODRL policy (odrl:Policy)
6
+ # containing permissions, duties, constraints, and clause references.
7
+ # - Captures a patient’s expectations (needs/preferences) as a lightweight profile.
8
+ # - Uses N3 rules with log:includes / log:notIncludes to detect missing safeguards, e.g.:
9
+ # * secondary research use without explicit opt‑in consent
10
+ # * external genomic data sharing without de-identification/pseudonymisation
11
+ # * automated triage without human review/override (human‑in‑the‑loop)
12
+ # * retention exceeding the patient’s preferred limit
13
+ # - Generates DPV risks (dpv:Risk) and classifies them using DPV‑RISK concepts.
14
+ # - Proposes mitigations as dpv:RiskMitigationMeasure resources and links them to risks
15
+ # via dpv:isMitigatedByMeasure.
16
+ # - Computes a numeric score per risk and prints a ranked report where each risk is
17
+ # followed by its mitigation(s).
18
+ #
19
+ # How to run (Eyeling)
20
+ # - Deterministic ranked printing of log:outputString requires “strings mode”:
21
+ # eyeling -r odrl-dpv-healthcare-risk-ranked.n3
22
+ #
23
+ # How ranking works
24
+ # - Output lines are emitted as log:outputString triples.
25
+ # - With -r, Eyeling prints these strings in key order.
26
+ # - The key encodes an inverse score (1000 - score) so higher scores appear first.
27
+ # - Mitigation lines use a key that sorts immediately after their corresponding risk.
28
+ #
29
+ # Structure
30
+ # 1) Parties, data assets, and a processing context (dpv:Process)
31
+ # 2) Patient profile (needs/preferences)
32
+ # 3) Agreement with ODRL policy clauses (intentionally includes gaps)
33
+ # 4) Risk rules + mitigations (ODRL -> DPV/DPV‑RISK)
34
+ # 5) Score normalisation -> risk level/severity
35
+ # 6) Ranked report output (risks + mitigations)
36
+ #
37
+ # Disclaimer
38
+ # - This is an illustrative example, not legal advice or a compliance checklist.
39
+ #
40
+ # References
41
+ # - Eyeling handbook: https://eyereasoner.github.io/eyeling/HANDBOOK
42
+ # - ODRL vocab: https://www.w3.org/TR/odrl-vocab/
43
+ # - DPV core: https://w3id.org/dpv
44
+ # - DPV-PD: https://w3id.org/dpv/pd
45
+ # - DPV-RISK: https://w3id.org/dpv/risk
46
+ # ========================================================================================
47
+
48
+ @prefix : <https://example.org/odrl-dpv-healthcare-risk-ranked#> .
49
+ @prefix dct: <http://purl.org/dc/terms/> .
50
+ @prefix dpv: <https://w3id.org/dpv#> .
51
+ @prefix pd: <https://w3id.org/dpv/pd#> .
52
+ @prefix risk: <https://w3id.org/dpv/risk#> .
53
+ @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
54
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
55
+ @prefix math: <http://www.w3.org/2000/10/swap/math#> .
56
+ @prefix string:<http://www.w3.org/2000/10/swap/string#> .
57
+ @prefix skos: <http://www.w3.org/2004/02/skos/core#> .
58
+ @prefix duo: <http://purl.obolibrary.org/obo/DUO_> .
59
+ @prefix hl7ca: <http://terminology.hl7.org/CodeSystem/consentaction#> .
60
+
61
+ # ---------------------------------------------------
62
+ # Domain vocabularies used (healthcare/life-sciences)
63
+ # ---------------------------------------------------
64
+ # - GA4GH Data Use Ontology (DUO): https://obofoundry.org/ontology/duo.html
65
+ # We use DUO codes as values for odrl:purpose constraints (via odrl:rightOperandReference).
66
+ # * duo:0000006 (HMB) health/medical/biomedical research
67
+ # * duo:0000042 (GRU) general research use
68
+ # * duo:0000043 (CC) clinical care use
69
+ #
70
+ # - HL7 Consent Action Codes: http://terminology.hl7.org/CodeSystem/consentaction
71
+ # We mint code IRIs using a fragment (#code) purely for convenience in this example:
72
+ # * hl7ca:use, hl7ca:disclose, hl7ca:collect
73
+ #
74
+
75
+ :PurposeHMB a skos:Concept ;
76
+ skos:prefLabel "DUO HMB: Health/Medical/Biomedical Research" ;
77
+ skos:exactMatch duo:0000006 .
78
+
79
+ :PurposeGRU a skos:Concept ;
80
+ skos:prefLabel "DUO GRU: General Research Use" ;
81
+ skos:exactMatch duo:0000042 .
82
+
83
+ :PurposeCC a skos:Concept ;
84
+ skos:prefLabel "DUO CC: Clinical Care Use" ;
85
+ skos:exactMatch duo:0000043 .
86
+
87
+ # ----------------------------------------------
88
+ # 1) Parties, data assets, and "process context"
89
+ # ----------------------------------------------
90
+
91
+ :Hospital a odrl:Party ; dct:title "Example Hospital" .
92
+ :ResearchUnit a odrl:Party ; dct:title "Hospital Research Unit" .
93
+ :PharmaPartner a odrl:Party ; dct:title "Pharma Partner" .
94
+ :ClinicalAIService a odrl:Party ; dct:title "Clinical AI Service Provider" .
95
+
96
+ :HealthRecordData a pd:HealthRecord ; dct:title "Electronic Health Record (EHR) data" .
97
+ :GenomicData a pd:Genetic ; dct:title "Genomic sequencing data" .
98
+
99
+ :ProcessContextHC1 a dpv:Process ;
100
+ dct:title "Example healthcare processing context" .
101
+
102
+ # --------------------------------------
103
+ # 2) Patient profile (needs/preferences)
104
+ # --------------------------------------
105
+
106
+ :PatientExample a :PatientProfile, odrl:Party ;
107
+ dct:title "Example patient profile" ;
108
+ :hasNeed :Need_ConsentForResearch,
109
+ :Need_DeIdentifyBeforeSharing,
110
+ :Need_HumanReviewForAutomatedTriage,
111
+ :Need_RetentionLimit3y .
112
+
113
+ :Need_ConsentForResearch a :Need ;
114
+ dct:description "Explicit opt-in consent required before secondary use for research/publication." ;
115
+ :importance 35 .
116
+
117
+ :Need_DeIdentifyBeforeSharing a :Need ;
118
+ dct:description "De-identify or pseudonymise health/genetic data before sharing with external partners." ;
119
+ :importance 35 .
120
+
121
+ :Need_HumanReviewForAutomatedTriage a :Need ;
122
+ dct:description "Human review/override required for automated triage decisions." ;
123
+ :importance 25 .
124
+
125
+ :Need_RetentionLimit3y a :Need ;
126
+ dct:description "Retention limited to 3 years unless a specific legal obligation requires longer retention." ;
127
+ :importance 15 ;
128
+ :maxRetentionDays 1095 .
129
+
130
+ # ----------------------------------------------------------------
131
+ # 3) Agreement + ODRL policy clauses (intentionally contains gaps)
132
+ # ----------------------------------------------------------------
133
+
134
+ :ClauseH1 a :Clause ;
135
+ :clauseId "H1" ;
136
+ :text "Hospital may use EHR and genomic data for internal clinical research and publication." .
137
+
138
+ :ClauseH2 a :Clause ;
139
+ :clauseId "H2" ;
140
+ :text "Hospital may share genomic data with pharmaceutical partners for drug discovery and R&D." .
141
+
142
+ :ClauseH3 a :Clause ;
143
+ :clauseId "H3" ;
144
+ :text "Hospital may use automated triage and prioritisation systems using EHR data." .
145
+
146
+ :ClauseH4 a :Clause ;
147
+ :clauseId "H4" ;
148
+ :text "Hospital retains patient health records for 10 years." .
149
+
150
+ :AgreementHC1 a odrl:Agreement ;
151
+ dct:title "Example Healthcare & Life-Sciences Data Use Agreement" ;
152
+ :policyGraph {
153
+
154
+ :PolicyHC1 a odrl:Policy ;
155
+ odrl:permission
156
+ :PermResearchUse,
157
+ :PermShareWithPharma,
158
+ :PermAutomatedTriage,
159
+ :PermRetention10y .
160
+
161
+ # Permission: internal clinical research (NO explicit-consent constraint -> should trigger R1)
162
+ :PermResearchUse a odrl:Permission ;
163
+ odrl:assigner :Hospital ;
164
+ odrl:assignee :ResearchUnit ;
165
+ odrl:action hl7ca:use ;
166
+ odrl:target :HealthRecordData, :GenomicData ;
167
+ odrl:constraint [
168
+ a odrl:Constraint ;
169
+ odrl:leftOperand odrl:purpose ;
170
+ odrl:operator odrl:eq ;
171
+ odrl:rightOperandReference :PurposeHMB
172
+ ] ;
173
+
174
+ :clause :ClauseH1 .
175
+
176
+ # Permission: share genomic data with pharma (NO de-identification constraint -> should trigger R2)
177
+ :PermShareWithPharma a odrl:Permission ;
178
+ odrl:assigner :Hospital ;
179
+ odrl:assignee :PharmaPartner ;
180
+ odrl:action hl7ca:disclose ;
181
+ odrl:target :GenomicData ;
182
+ odrl:constraint [
183
+ a odrl:Constraint ;
184
+ odrl:leftOperand odrl:purpose ;
185
+ odrl:operator odrl:eq ;
186
+ odrl:rightOperandReference :PurposeHMB
187
+ ] ;
188
+
189
+ :clause :ClauseH2 .
190
+
191
+ # Permission: automated triage (NO human-review duty -> should trigger R3)
192
+ :PermAutomatedTriage a odrl:Permission ;
193
+ odrl:assigner :Hospital ;
194
+ odrl:assignee :ClinicalAIService ;
195
+ odrl:action hl7ca:use ;
196
+ odrl:target :HealthRecordData ;
197
+ odrl:constraint [
198
+ a odrl:Constraint ;
199
+ odrl:leftOperand odrl:purpose ;
200
+ odrl:operator odrl:eq ;
201
+ odrl:rightOperandReference :PurposeCC
202
+ ] ;
203
+
204
+ :clause :ClauseH3 ;
205
+ odrl:duty [
206
+ a odrl:Duty ;
207
+ odrl:action :humanReview ;
208
+ odrl:constraint [
209
+ a odrl:Constraint ;
210
+ odrl:leftOperand :encryptionAtRest ;
211
+ odrl:operator odrl:eq ;
212
+ odrl:rightOperand true
213
+ ]
214
+ ] .
215
+
216
+ # Permission: retain for 10 years (should trigger R4 vs patient max 3 years)
217
+ :PermRetention10y a odrl:Permission ;
218
+ odrl:assigner :Hospital ;
219
+ odrl:assignee :Hospital ;
220
+ odrl:action hl7ca:collect ;
221
+ odrl:target :HealthRecordData ;
222
+ odrl:constraint [
223
+ a odrl:Constraint ;
224
+ odrl:leftOperand odrl:purpose ;
225
+ odrl:operator odrl:eq ;
226
+ odrl:rightOperandReference :PurposeCC
227
+ ] ;
228
+
229
+ :clause :ClauseH4 ;
230
+ odrl:constraint [
231
+ a odrl:Constraint ;
232
+ odrl:leftOperand :retentionDays ;
233
+ odrl:operator odrl:eq ;
234
+ odrl:rightOperand 3650
235
+ ] .
236
+ } .
237
+
238
+ # -----------------------------------------------------------------
239
+ # 4) Risk detection rules + mitigation suggestions (DPV + DPV-RISK)
240
+ # -----------------------------------------------------------------
241
+
242
+ # R1: Secondary research use WITHOUT explicit consent constraint
243
+ {
244
+ :AgreementHC1 :policyGraph ?G .
245
+ :PatientExample :hasNeed :Need_ConsentForResearch .
246
+ :Need_ConsentForResearch :importance ?w .
247
+
248
+ ?G log:includes {
249
+ :PermResearchUse a odrl:Permission ;
250
+ :clause ?clause .
251
+ }.
252
+
253
+ ?G log:notIncludes {
254
+ :PermResearchUse odrl:constraint [
255
+ a odrl:Constraint ;
256
+ odrl:leftOperand :explicitConsent ;
257
+ odrl:operator odrl:eq ;
258
+ odrl:rightOperand true
259
+ ] .
260
+ }.
261
+
262
+ ?clause :clauseId ?cid ; :text ?txt .
263
+
264
+ (85 ?w) math:sum ?raw .
265
+ ( :AgreementHC1 :PatientExample :ResearchNoConsent ?cid ) log:skolem ?risk .
266
+ ( :AgreementHC1 :PatientExample :ResearchNoConsentSource ?cid ) log:skolem ?src .
267
+ ( ?risk :M1 ) log:skolem ?m1 .
268
+
269
+ ( "Risk: health/genomic data may be used for research without explicit opt-in consent. Clause %s: %s"
270
+ ?cid ?txt ) string:format ?why .
271
+ }
272
+ =>
273
+ {
274
+ ?src a risk:RiskSource, risk:LegalComplianceRisk ;
275
+ dct:source :PermResearchUse ;
276
+ dct:description "Research use permitted without explicit consent constraint." .
277
+
278
+ ?risk a dpv:Risk, risk:PolicyRisk, risk:CustomerConfidenceLoss ;
279
+ dct:source :PermResearchUse ;
280
+ risk:hasRiskSource ?src ;
281
+ dpv:hasConsequence risk:CustomerConfidenceLoss ;
282
+ dpv:hasImpact risk:NonMaterialDamage, risk:FinancialLoss ;
283
+ :aboutClause ?clause ;
284
+ :scoreRaw ?raw ;
285
+ :violatesNeed :Need_ConsentForResearch ;
286
+ dct:description ?why .
287
+
288
+ :ProcessContextHC1 dpv:hasRisk ?risk .
289
+
290
+ ?m1 a dpv:RiskMitigationMeasure ;
291
+ dct:description "Add an explicit consent constraint for secondary research use." ;
292
+ dpv:mitigatesRisk ?risk ;
293
+ :suggestAdd {
294
+ :PermResearchUse odrl:constraint [
295
+ a odrl:Constraint ;
296
+ odrl:leftOperand :explicitConsent ;
297
+ odrl:operator odrl:eq ;
298
+ odrl:rightOperand true
299
+ ] .
300
+ } .
301
+
302
+ ?risk dpv:isMitigatedByMeasure ?m1 .
303
+ } .
304
+
305
+ # R2: Sharing genomic data WITHOUT de-identification/pseudonymisation requirement
306
+ {
307
+ :AgreementHC1 :policyGraph ?G .
308
+ :PatientExample :hasNeed :Need_DeIdentifyBeforeSharing .
309
+ :Need_DeIdentifyBeforeSharing :importance ?w .
310
+
311
+ ?G log:includes {
312
+ :PermShareWithPharma a odrl:Permission ;
313
+ :clause ?clause .
314
+ }.
315
+
316
+ ?G log:notIncludes {
317
+ :PermShareWithPharma odrl:constraint [
318
+ a odrl:Constraint ;
319
+ odrl:leftOperand :deIdentified ;
320
+ odrl:operator odrl:eq ;
321
+ odrl:rightOperand true
322
+ ] .
323
+ }.
324
+
325
+ ?clause :clauseId ?cid ; :text ?txt .
326
+
327
+ (90 ?w) math:sum ?raw .
328
+ ( :AgreementHC1 :PatientExample :ShareNoDeId ?cid ) log:skolem ?risk .
329
+ ( :AgreementHC1 :PatientExample :ShareNoDeIdSource ?cid ) log:skolem ?src .
330
+ ( ?risk :M1 ) log:skolem ?m1 .
331
+
332
+ ( "Risk: genomic data may be shared with external pharma partners without a de-identification/pseudonymisation requirement. Clause %s: %s"
333
+ ?cid ?txt ) string:format ?why .
334
+ }
335
+ =>
336
+ {
337
+ ?src a risk:RiskSource, risk:LegalComplianceRisk ;
338
+ dct:source :PermShareWithPharma ;
339
+ dct:description "External sharing permitted without de-identification/pseudonymisation requirement." .
340
+
341
+ ?risk a dpv:Risk, risk:UnwantedDisclosureData, risk:ReputationalRisk ;
342
+ dct:source :PermShareWithPharma ;
343
+ risk:hasRiskSource ?src ;
344
+ dpv:hasConsequence risk:CustomerConfidenceLoss ;
345
+ dpv:hasImpact risk:NonMaterialDamage, risk:FinancialLoss, risk:Discrimination ;
346
+ :aboutClause ?clause ;
347
+ :scoreRaw ?raw ;
348
+ :violatesNeed :Need_DeIdentifyBeforeSharing ;
349
+ dct:description ?why .
350
+
351
+ :ProcessContextHC1 dpv:hasRisk ?risk .
352
+
353
+ ?m1 a dpv:RiskMitigationMeasure ;
354
+ dct:description "Require de-identification/pseudonymisation before external sharing of genomic data." ;
355
+ dpv:mitigatesRisk ?risk ;
356
+ :suggestAdd {
357
+ :PermShareWithPharma odrl:constraint [
358
+ a odrl:Constraint ;
359
+ odrl:leftOperand :deIdentified ;
360
+ odrl:operator odrl:eq ;
361
+ odrl:rightOperand true
362
+ ] ;
363
+ odrl:duty [
364
+ a odrl:Duty ;
365
+ odrl:action :deIdentify ;
366
+ odrl:constraint [
367
+ a odrl:Constraint ;
368
+ odrl:leftOperand :deIdentificationStandard ;
369
+ odrl:operator odrl:eq ;
370
+ odrl:rightOperand :StateOfTheArt
371
+ ]
372
+ ] .
373
+ } .
374
+
375
+ ?risk dpv:isMitigatedByMeasure ?m1 .
376
+ } .
377
+
378
+ # R3: Automated triage WITHOUT human review/override duty
379
+ {
380
+ :AgreementHC1 :policyGraph ?G .
381
+ :PatientExample :hasNeed :Need_HumanReviewForAutomatedTriage .
382
+ :Need_HumanReviewForAutomatedTriage :importance ?w .
383
+
384
+ ?G log:includes {
385
+ :PermAutomatedTriage a odrl:Permission ;
386
+ :clause ?clause .
387
+ }.
388
+
389
+ ?G log:notIncludes {
390
+ :PermAutomatedTriage odrl:duty [
391
+ a odrl:Duty ;
392
+ odrl:action :humanReview
393
+ ] .
394
+ }.
395
+
396
+ ?clause :clauseId ?cid ; :text ?txt .
397
+
398
+ (80 ?w) math:sum ?raw .
399
+ ( :AgreementHC1 :PatientExample :AutoTriageNoHuman ?cid ) log:skolem ?risk .
400
+ ( :AgreementHC1 :PatientExample :AutoTriageNoHumanSource ?cid ) log:skolem ?src .
401
+ ( ?risk :M1 ) log:skolem ?m1 .
402
+
403
+ ( "Risk: automated triage may affect care pathways without a human review/override safeguard. Clause %s: %s"
404
+ ?cid ?txt ) string:format ?why .
405
+ }
406
+ =>
407
+ {
408
+ ?src a risk:RiskSource, risk:PolicyRisk ;
409
+ dct:source :PermAutomatedTriage ;
410
+ dct:description "Automated triage permitted without a human review/override duty." .
411
+
412
+ ?risk a dpv:Risk, risk:QualityRisk, risk:InabilityToProvideHealthCare ;
413
+ dct:source :PermAutomatedTriage ;
414
+ risk:hasRiskSource ?src ;
415
+ dpv:hasConsequence risk:InabilityToProvideHealthCare ;
416
+ dpv:hasImpact risk:PhysicalHarm, risk:NonMaterialDamage ;
417
+ :aboutClause ?clause ;
418
+ :scoreRaw ?raw ;
419
+ :violatesNeed :Need_HumanReviewForAutomatedTriage ;
420
+ dct:description ?why .
421
+
422
+ :ProcessContextHC1 dpv:hasRisk ?risk .
423
+
424
+ ?m1 a dpv:RiskMitigationMeasure ;
425
+ dct:description "Add a duty requiring human review/override for triage decisions (human-in-the-loop)." ;
426
+ dpv:mitigatesRisk ?risk ;
427
+ :suggestAdd {
428
+ :PermAutomatedTriage odrl:duty [
429
+ a odrl:Duty ;
430
+ odrl:action :humanReview ;
431
+ odrl:constraint [
432
+ a odrl:Constraint ;
433
+ odrl:leftOperand :reviewBeforeDecision ;
434
+ odrl:operator odrl:eq ;
435
+ odrl:rightOperand true
436
+ ]
437
+ ] .
438
+ } .
439
+
440
+ ?risk dpv:isMitigatedByMeasure ?m1 .
441
+ } .
442
+
443
+ # R4: Retention exceeds patient max (3 years)
444
+ {
445
+ :AgreementHC1 :policyGraph ?G .
446
+ :PatientExample :hasNeed :Need_RetentionLimit3y .
447
+ :Need_RetentionLimit3y :importance ?w ; :maxRetentionDays ?max .
448
+
449
+ ?G log:includes {
450
+ :PermRetention10y a odrl:Permission ;
451
+ odrl:constraint [
452
+ a odrl:Constraint ;
453
+ odrl:leftOperand :retentionDays ;
454
+ odrl:rightOperand ?days
455
+ ] ;
456
+ :clause ?clause .
457
+ }.
458
+
459
+ ?days math:greaterThan ?max .
460
+ ?clause :clauseId ?cid ; :text ?txt .
461
+
462
+ (55 ?w) math:sum ?raw .
463
+ ( :AgreementHC1 :PatientExample :RetentionTooLong ?cid ) log:skolem ?risk .
464
+ ( :AgreementHC1 :PatientExample :RetentionTooLongSource ?cid ) log:skolem ?src .
465
+ ( ?risk :M1 ) log:skolem ?m1 .
466
+
467
+ ( "Risk: retention (%s days) exceeds patient preference (%s days). Clause %s: %s"
468
+ ?days ?max ?cid ?txt ) string:format ?why .
469
+ }
470
+ =>
471
+ {
472
+ ?src a risk:RiskSource, risk:PolicyRisk ;
473
+ dct:source :PermRetention10y ;
474
+ dct:description "Retention period exceeds patient preference." .
475
+
476
+ ?risk a dpv:Risk, risk:PolicyRisk, risk:CustomerConfidenceLoss ;
477
+ dct:source :PermRetention10y ;
478
+ risk:hasRiskSource ?src ;
479
+ dpv:hasConsequence risk:CustomerConfidenceLoss ;
480
+ dpv:hasImpact risk:NonMaterialDamage ;
481
+ :aboutClause ?clause ;
482
+ :scoreRaw ?raw ;
483
+ :violatesNeed :Need_RetentionLimit3y ;
484
+ dct:description ?why .
485
+
486
+ :ProcessContextHC1 dpv:hasRisk ?risk .
487
+
488
+ ?m1 a dpv:RiskMitigationMeasure ;
489
+ dct:description "Limit retention to 3 years (or document the legal obligation requiring longer retention)." ;
490
+ dpv:mitigatesRisk ?risk ;
491
+ :suggestAdd {
492
+ :PermRetention10y odrl:constraint [
493
+ a odrl:Constraint ;
494
+ odrl:leftOperand :retentionDays ;
495
+ odrl:operator odrl:lteq ;
496
+ odrl:rightOperand 1095
497
+ ] .
498
+ } .
499
+
500
+ ?risk dpv:isMitigatedByMeasure ?m1 .
501
+ } .
502
+
503
+ # ------------------------------------------------
504
+ # 5) Score normalization + DPV-RISK severity/level
505
+ # ------------------------------------------------
506
+
507
+ { ?r a dpv:Risk ; :scoreRaw ?raw . ?raw math:greaterThan 100 . }
508
+ => { ?r :score 100 . } .
509
+
510
+ { ?r a dpv:Risk ; :scoreRaw ?raw . 100 math:notLessThan ?raw . }
511
+ => { ?r :score ?raw . } .
512
+
513
+ { ?r a dpv:Risk ; :score ?s . ?s math:greaterThan 79 . }
514
+ => { ?r dpv:hasSeverity risk:HighSeverity ; dpv:hasRiskLevel risk:HighRisk . } .
515
+
516
+ { ?r a dpv:Risk ; :score ?s . ?s math:lessThan 80 . ?s math:greaterThan 49 . }
517
+ => { ?r dpv:hasSeverity risk:ModerateSeverity ; dpv:hasRiskLevel risk:ModerateRisk . } .
518
+
519
+ { ?r a dpv:Risk ; :score ?s . ?s math:lessThan 50 . }
520
+ => { ?r dpv:hasSeverity risk:LowSeverity ; dpv:hasRiskLevel risk:LowRisk . } .
521
+
522
+ # -------------------------------------------------------------------
523
+ # 6) Ranked explainable output (Eyeling -r prints these in key order)
524
+ # -------------------------------------------------------------------
525
+
526
+ # Header
527
+ {
528
+ :AgreementHC1 dct:title ?alabel .
529
+ :PatientExample dct:title ?plabel .
530
+ ( "\n=== Ranked DPV Risk Report (Healthcare & Life Sciences) ===\nAgreement: %s\nProfile: %s\n\n"
531
+ ?alabel ?plabel ) string:format ?hdr .
532
+ }
533
+ =>
534
+ {
535
+ ( :AgreementHC1 :PatientExample 0 ) log:outputString ?hdr .
536
+ } .
537
+
538
+ # Risk lines (key includes inverse score = 1000 - score)
539
+ {
540
+ ?r a dpv:Risk ;
541
+ :score ?score ;
542
+ dpv:hasRiskLevel ?lvl ;
543
+ dpv:hasSeverity ?sev ;
544
+ :aboutClause ?clause ;
545
+ dct:description ?why .
546
+ ?clause :clauseId ?cid .
547
+
548
+ ( 1000 ?score ) math:difference ?inv .
549
+
550
+ ( "score=%s (%s, %s) clause %s\n %s\n\n"
551
+ ?score ?lvl ?sev ?cid ?why ) string:format ?line .
552
+ }
553
+ =>
554
+ {
555
+ ( :AgreementHC1 :PatientExample 1 ?inv ?cid 0 ?r ) log:outputString ?line .
556
+ } .
557
+
558
+ # Mitigation lines (same ordering as their risk)
559
+ {
560
+ ?r a dpv:Risk ;
561
+ :score ?score ;
562
+ dpv:isMitigatedByMeasure ?m ;
563
+ :aboutClause ?clause .
564
+ ?clause :clauseId ?cid .
565
+ ?m dct:description ?md .
566
+
567
+ ( 1000 ?score ) math:difference ?inv .
568
+
569
+ ( " - mitigation for clause %s: %s\n"
570
+ ?cid ?md ) string:format ?mline .
571
+ }
572
+ =>
573
+ {
574
+ ( :AgreementHC1 :PatientExample 1 ?inv ?cid 1 ?r ?m ) log:outputString ?mline .
575
+ } .