knartform 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 05dcabe6d14a5de527125fd163e244d7bea657003110b822060466d88a91d638
4
+ data.tar.gz: 436cd22c232b0fdb207de4c41b87abe50577fb61ccbbdf6b40e8f14fd88d4841
5
+ SHA512:
6
+ metadata.gz: 3b5e27b612d7299080f9e159ea379f3526689f05c99d9e4465513249da7dfbe73b9fbefd6ca4980f3554c83948e246ca38ec783909649693fab95eeebd799a64
7
+ data.tar.gz: af103048da19a284c91e1a47637e48ac27f4e0ac4024d4223b0836f5588bb592bef034e83397bd82adf525478370f78786ae5ccf845da5e47c93692f25ce07ae
@@ -0,0 +1 @@
1
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ knartform (0.6.0)
5
+ httparty
6
+ nokogiri
7
+ slim
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ httparty (0.16.4)
13
+ mime-types (~> 3.0)
14
+ multi_xml (>= 0.5.2)
15
+ mime-types (3.2.2)
16
+ mime-types-data (~> 3.2015)
17
+ mime-types-data (3.2018.0812)
18
+ mini_portile2 (2.4.0)
19
+ minitest (5.11.3)
20
+ multi_xml (0.6.0)
21
+ nokogiri (1.10.1)
22
+ mini_portile2 (~> 2.4.0)
23
+ rake (12.3.2)
24
+ slim (4.0.1)
25
+ temple (>= 0.7.6, < 0.9)
26
+ tilt (>= 2.0.6, < 2.1)
27
+ temple (0.8.0)
28
+ tilt (2.0.9)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ bundler
35
+ knartform!
36
+ minitest
37
+ rake
38
+
39
+ BUNDLED WITH
40
+ 1.17.2
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'pp'
5
+ require 'byebug'
6
+ require 'httparty'
7
+
8
+ require_relative "../lib/common"
9
+ include Repository
10
+ HELP = <<EOF
11
+
12
+ Audits an existing repository manifest.json by checking for the existance of referenced file paths and URLs. As all paths are relative to the provided manifest.json file, manually specifying the content directory is unnecessary.
13
+
14
+ USAGE: #{__FILE__} <manifest.json>
15
+
16
+ EXAMPLE:
17
+ #{__FILE__} manifest.json
18
+
19
+ EOF
20
+
21
+
22
+ def report(good, bad)
23
+ puts "\nGood file references: #{good.length}"
24
+ puts "Bad file references: #{bad.length}"
25
+ bad.each do |n| puts "\t#{[n['path'],n['url']].join(' ')}" end
26
+ puts "\n"
27
+ end
28
+
29
+ if(ARGV.length != 1)
30
+ puts HELP
31
+ exit 1
32
+ else
33
+ file = ARGV[0]
34
+ manifest = JSON.parse(File.read(file))
35
+ root = File.dirname(File.expand_path(file))
36
+ good, bad = audit_content_directory(root, manifest)
37
+ report(good, bad)
38
+
39
+ valid = []
40
+ invalid = []
41
+ manifest['groups'].each do |group|
42
+ group['items'].each do |item|
43
+ path = "#{root}/#{item['path']}"
44
+ # puts "#{path}"
45
+ if item['validation']
46
+ if item['validation']['passed']
47
+ valid << item
48
+ else
49
+ invalid << item
50
+ end
51
+ end
52
+ end
53
+ end
54
+ puts "Declared as valid: #{valid.count}"
55
+ puts "Declared as invalid: #{invalid.count}"
56
+
57
+ exit 0
58
+ end
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'pp'
5
+ require 'open3'
6
+ require 'byebug'
7
+
8
+ require_relative '../lib/common'
9
+ include Repository
10
+
11
+ HELP = <<EOF.freeze
12
+
13
+ Performs validation against all artifacts. Provide the manifest file as the only argument. XML schema validation on composites is non-recursive. This may take a few minutes. Be patient! If the existing and new manifest files are the same, the existing file will be *overwriten* in place. Be sure you have a backup handy if you're doing this!
14
+
15
+ The --update or --no-update is required, indicating whether existing passed validations should be re-run or not, respectively.
16
+
17
+ USAGE: #{__FILE__} <manifest.json> --(no-)update <new_manifest.json>
18
+
19
+ EXAMPLE:
20
+ #{__FILE__} manifest.json manifest-new.json
21
+
22
+ EOF
23
+
24
+ # SCRIPT_DIR = File.dirname(File.expand_path(__FILE__))
25
+ ARTIFACT_SCHEMA = 'doc/schema/1.3-revised/knowledgeartifact/knowledgedocument.xsd'.freeze
26
+ COMPOSITE_SCHEMA = 'doc/schema/composite-draft/knowledgeartifact/compositeknowledgedocument.xsd'.freeze
27
+
28
+ UPDATE_EXISTING_VALIDATIONS = false
29
+
30
+ def should_validate(old)
31
+ UPDATE_EXISTING_VALIDATIONS || old.nil? || !old.dig('passed')
32
+ end
33
+
34
+ def validate_composites(root, manifest)
35
+ manifest['groups'].each do |group|
36
+ group['items'].each do |item|
37
+ path = "#{root}/#{item['path']}"
38
+ # puts "#{path}"
39
+ old = item['validation']
40
+ if (COMPOSITE_MIME_TYPES.include? item['mimeType']) && should_validate(old)
41
+ item['validation'] = validate(root + '/' + item['path'], root + '/' + COMPOSITE_SCHEMA)
42
+ pp item
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def validate_individuals(root, manifest)
49
+ # list = Dir.glob("#{root}/**/*KRprt*.xml", File::FNM_CASEFOLD)
50
+ individuals = []
51
+ manifest['groups'].each do |group|
52
+ group['items'].each do |item|
53
+ path = "#{root}/#{item['path']}"
54
+ # puts "#{path}"
55
+ old = item['validation']
56
+ if (KNART_MIME_TYPES.include? item['mimeType']) && should_validate(old)
57
+ individuals << item
58
+ item['validation'] = validate(root + '/' + item['path'], root + '/' + ARTIFACT_SCHEMA)
59
+ pp item
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def validate(_file, _schema)
66
+ cmd = "xmllint --noout --schema #{_schema} #{_file}"
67
+ stdout, stderr, status = Open3.capture3(cmd)
68
+ validation = {
69
+ passed: status.exitstatus == 0,
70
+ code: status.exitstatus,
71
+ display: xmllint_status_code(status.exitstatus),
72
+ errors: nil,
73
+ run_at: Time.now
74
+ }
75
+ validation[:errors] = stderr unless validation[:passed]
76
+ validation
77
+ end
78
+
79
+ def xmllint_status_code(code)
80
+ case code
81
+ when 0
82
+ 'valid!'
83
+ when 1
84
+ 'Unclassified'
85
+ when 2
86
+ 'Error in DTD'
87
+ when 3
88
+ 'Validation error'
89
+ when 4
90
+ 'Validation error'
91
+ when 5
92
+ 'Error in schema compilation'
93
+ when 6
94
+ 'Error writing output'
95
+ when 7
96
+ 'Error in pattern (generated when --pattern option is used)'
97
+ when 8
98
+ 'Error in Reader registration (generated when --chkregister option is used)'
99
+ when 9
100
+ 'Out of memory error'
101
+ else
102
+ 'Unknown :-/'
103
+ end
104
+ end
105
+
106
+ if ARGV.length != 3
107
+ puts HELP
108
+ exit 1
109
+ else
110
+ file = ARGV[0]
111
+ manifest = JSON.parse(File.read(file))
112
+ root = File.dirname(File.expand_path(file))
113
+
114
+ if ARGV[1] == '--update'
115
+ UPDATE_EXISTING_VALIDATIONS = true
116
+ end
117
+
118
+ # Open the output first so we don't waste time if it's not writable.
119
+ out = File.open(ARGV[2], 'w')
120
+
121
+ validate_composites(root, manifest)
122
+ validate_individuals(root, manifest)
123
+
124
+ out.write(JSON.pretty_generate(manifest))
125
+ out.close
126
+
127
+ exit 0
128
+ end
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'pp'
5
+ require 'byebug'
6
+ require 'nokogiri'
7
+ # require 'slim'
8
+ require 'erb'
9
+ require 'digest/sha1'
10
+ require 'open3'
11
+
12
+ require_relative "../lib/common"
13
+ include Repository
14
+
15
+ HELP = <<EOF
16
+
17
+ Generates SVG diagrams for all composite KNARTS in the provided in the directory.
18
+
19
+ USAGE: #{__FILE__} <directory>
20
+
21
+ EXAMPLE:
22
+ #{__FILE__} content
23
+
24
+ EOF
25
+
26
+
27
+ if(ARGV.length != 1)
28
+ puts HELP
29
+ exit 1
30
+ else
31
+ # path = File.expand_path(ARGV[0])
32
+ items = content_directory_to_item_tree(ARGV[0])
33
+ items.each do |i|
34
+ # puts "#{i['path']} #{i['mimeType']}"
35
+ # puts i['mimeType']
36
+ # byebug
37
+ if COMPOSITE_MIME_TYPES.include?(i['mimeType'].to_s)
38
+ path = i['path'].to_s
39
+ doc = Nokogiri::XML(File.open(path))
40
+ dot = generate_dot(doc)
41
+ svg = "#{path}.svg"
42
+ stdout_str, error_str, status = Open3.capture3('dot', '-Tsvg', '-o', svg, stdin_data: dot)
43
+ end
44
+ end
45
+
46
+ exit 0
47
+ end
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'pp'
5
+ require 'byebug'
6
+ require 'nokogiri'
7
+ # require 'slim'
8
+ require 'erb'
9
+ require 'digest/sha1'
10
+
11
+ require_relative "../lib/common"
12
+ include Repository
13
+
14
+ HELP = <<EOF
15
+
16
+ Transform the event/trigger structure of a composite knowledge artifact into a visual representation using DOT syntax. Rendering may be done using `graphviz` or any number of existing tools.
17
+
18
+ USAGE: #{__FILE__} <composite.xml>
19
+
20
+ EXAMPLE:
21
+ #{__FILE__} my_composite_knart.xml |dot -Tsvg -o graph.svg
22
+
23
+ EOF
24
+
25
+
26
+ if(ARGV.length != 1)
27
+ puts HELP
28
+ exit 1
29
+ else
30
+ path = File.expand_path(ARGV[0])
31
+ doc = Nokogiri::XML(File.open(path))
32
+ puts generate_dot(doc)
33
+ exit 0
34
+ end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Example KNART compilation.
4
+ #
5
+
6
+ require 'httparty'
7
+ require 'nokogiri'
8
+
9
+ require_relative '../lib/knartform'
10
+
11
+ if ARGV.length != 2
12
+ puts "\n\tUsage: #{__FILE__} <input-knowledge-document.xml> <output.html>\n\n"
13
+ exit 1
14
+ end
15
+ path = ARGV[0]
16
+ if File.exists? ARGV[0]
17
+ knart = Knartform::Knart.new File.new(ARGV[0])
18
+ out = File.open(ARGV[1], 'w')
19
+ out.write knart.to_html
20
+ out.close
21
+ else
22
+ puts "File doesn't exist."
23
+ end
24
+
25
+ puts "Done."
26
+ exit 0
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'pp'
5
+ require 'byebug'
6
+ require 'nokogiri'
7
+ require 'slim'
8
+
9
+ require_relative "../lib/common"
10
+ include Repository
11
+
12
+ HELP = <<EOF
13
+
14
+ Converts an existing manifest.json file to the expanded structure required for import into CDS Connect.
15
+
16
+ USAGE: #{__FILE__} <manifest.json> <connect.json>
17
+
18
+ EXAMPLE:
19
+ #{__FILE__} manifest.json connect.json
20
+
21
+ EOF
22
+
23
+ def extract_metadata(doc)
24
+ metadata = {
25
+ name: doc.xpath("///xmlns:metadata/xmlns:title/@value").first.to_s,
26
+ description: doc.xpath("///xmlns:metadata/xmlns:description/@value").first.to_s,
27
+ artifactType: doc.xpath("//xmlns:artifactType/@value").first.to_s,
28
+ identifier: doc.xpath("///xmlns:metadata/xmlns:identifiers/xmlns:identifier/@extension").select{|n| n.to_s.length == 36}.first.to_s,
29
+ status: doc.xpath("///xmlns:metadata/xmlns:status/@value").first.to_s,
30
+ experimental: true,
31
+ license: 'Apache 2.0',
32
+ copyright: 'Copyright © 2018 Veterans Health Administration, Department of Veterans Affairs. All rights reserved. Contributions from external parties are property of respective copyright holders.',
33
+ steward: 'Veterans Health Administration',
34
+ publisher: 'Veterans Health Administration',
35
+ ip_attestation: true,
36
+ knowledge_level: 3,
37
+ pilot_experience: 'None',
38
+ date_reviewed: doc.xpath("//xmlns:eventHistory/xmlns:artifactLifeCycleEvent[xmlns:eventType/@value = 'Reviewed']/xmlns:eventDateTime/@value").first.to_s,
39
+ date_published: doc.xpath("//xmlns:eventHistory/xmlns:artifactLifeCycleEvent[xmlns:eventType/@value = 'Published']/xmlns:eventDateTime/@value").first.to_s,
40
+ date_created: doc.xpath("//xmlns:eventHistory/xmlns:artifactLifeCycleEvent[xmlns:eventType/@value = 'Created']/xmlns:eventDateTime/@value").first.to_s,
41
+ date_approved: nil,
42
+ date_expired: nil
43
+ }
44
+ metadata[:date_reviewed] = nil if metadata[:date_reviewed].empty?
45
+ metadata[:date_published] = nil if metadata[:date_published].empty?
46
+ metadata[:date_created] = nil if metadata[:date_created].empty?
47
+ # byebug
48
+ metadata[:contributions] = ''
49
+ doc.xpath("//xmlns:contributions//xmlns:contributor").each do |c|
50
+ first = c.xpath('./xmlns:name/dt:part[@type = "GIV"]/@value').first.to_s
51
+ last = c.xpath('./xmlns:name/dt:part[@type = "FAM"]/@value').first.to_s
52
+ title = c.xpath('./xmlns:name/dt:part[@type = "TITLE"]/@value').first.to_s
53
+ affiliation = c.xpath('./xmlns:affiliation/xmlns:name/@value').first.to_s
54
+ metadata[:contributions] += "<div>" +
55
+ "<b><span>#{first}</span> <span>#{last}</span></b>" +
56
+ ", <span>#{title}</span>, <i><span>#{affiliation}</span></i>" +
57
+ "</div>"
58
+ end
59
+
60
+ metadata[:references] = '<dl>'
61
+ doc.xpath("//xmlns:relatedResources/xmlns:relatedResource").each do |r|
62
+ rel = r.xpath('./xmlns:relationship/@value').first.to_s.split(/(?=[A-Z])/).join(' ')
63
+ title = r.xpath('./xmlns:resources/xmlns:resource/xmlns:title/@value').first.to_s
64
+ description = r.xpath('./xmlns:resources/xmlns:resource/xmlns:description/@value').first.to_s
65
+ metadata[:references] += "<dt>#{rel}</dt><dd>#{title}</dd>"
66
+ # #{description}
67
+ end
68
+ metadata[:references] += '</dl>'
69
+
70
+ metadata[:cautions] = '<p>THE CLINICAL CONTENT IN THIS DOCUMENT IS NOT DIRECTLY SUITABLE FOR CLINICAL USE AS PRESENTED SINCE IT HAS NOT BEEN TESTED IN A CLINICAL ENVIRONMENT. IT HAS NEVER BEEN PILOTED, NEVER 100% REPRESENTS THE INTENT OF THE CLINICAL SUBJECT MATTER EXPERTS, AND MAY CAUSE HARM IF APPLIED AS-IS. THIS WORK IS MADE AVAILABLE WITHOUT ANY WARRANTY WHATSOEVER, AND WITHOUT CLAIMS, EXPRESSED OR IMPLIED, OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.</p>'
71
+ metadata[:usage] = '<dl>'
72
+ doc.xpath("//xmlns:applicability/xmlns:coverage").each do |c|
73
+ focus = c.xpath('./xmlns:focus/@value').first.to_s.split(/(?=[A-Z])/).join(' ')
74
+ description = c.xpath('./xmlns:description/@value').first.to_s
75
+ code = c.xpath('./xmlns:value/@code').first.to_s
76
+ # metadata[:usage] += "#{focus}: #{description}"
77
+ # metadata[:usage] += " (#{code})" if code.length > 0
78
+ # metadata[:usage] += "\n"
79
+ metadata[:usage] += "<dt>#{focus}</dt>" +
80
+ # "<dd>#{description} #{code.length > 0 ? ('(' + code + ')') : ''}</dd>"
81
+ "<dd>#{description}</dd>"
82
+ end
83
+ metadata[:usage] += '</dl>'
84
+
85
+ metadata[:decision_notes] = "<p>See attached resource files for additional details. Department of Veterans Affairs, Veterans Health Administration, Office of Informatics and Information Governance, Clinical Decision Support (CDS) Content and Health Level 7 (HL7)-Compliant Knowledge Artifacts (KNARTs) Eye Care Clinical Content White Paper, VA118-16-D-1008, Task Order VA-118-16-F-1008-0007, June, 2018.</p>"
86
+ metadata[:decision_notes] += '<dl>'
87
+ doc.xpath("//xmlns:supportingEvidence//xmlns:resource").each do |c|
88
+ title = c.xpath('./xmlns:title/@value').first.to_s
89
+ location = c.xpath('./xmlns:location/@value').first.to_s
90
+ citation = c.xpath('./xmlns:citation/@value').first.to_s
91
+ metadata[:decision_notes] += "<dt>#{citation}</dt><dd><a href=\"#{location}\">#{location}</a></dd>"
92
+ # #{citation}
93
+ end
94
+ metadata[:decision_notes] += '</dl>'
95
+
96
+ # metadata[:bindings] = []
97
+ metadata
98
+ end
99
+
100
+ def extract_knart_metadata(doc)
101
+ # We'll cover the basics first.
102
+ metadata = extract_metadata(doc)
103
+ # Global ELM stuff.
104
+ exps = doc.xpath("/xmlns:knowledgeDocument/xmlns:expressions/xmlns:def")
105
+
106
+ metadata[:expressions] = exps.collect{|e| {
107
+ name: e.xpath("@name").to_s,
108
+ type: e.xpath('elm:expression/@xsi:type').to_s
109
+ }}
110
+ # puts metadata
111
+ metadata
112
+ end
113
+
114
+ def extract_composite_metadata(doc)
115
+ metadata = extract_metadata(doc)
116
+ # metadata[:artifacts] =
117
+ xml = doc.xpath('//xmlns:containedArtifacts').to_s
118
+ # byebug
119
+ # puts xml
120
+ metadata[:artifacts] = Hash.from_xml(xml)[:containedArtifacts][:artifact]
121
+ # puts metadata
122
+ metadata
123
+ end
124
+
125
+ def group_by_tag(root, manifest)
126
+ connect = {}
127
+ manifest['groups'].each do |group|
128
+ group['items'].each do |item|
129
+ item['tags'].each do |tag|
130
+ if !connect[tag]
131
+ connect[tag] = {
132
+ technical_files: [],
133
+ miscellaneous_files: []
134
+ }
135
+ end
136
+ # puts connect.to_json
137
+ # puts connect[tag][:items]
138
+ #
139
+ if KNART_MIME_TYPES.include? item['mimeType']
140
+ doc = Nokogiri::XML(File.open(item['path']))
141
+ connect[tag].merge! extract_knart_metadata(doc)
142
+ connect[tag][:technical_files] << item
143
+ elsif COMPOSITE_MIME_TYPES.include? item['mimeType']
144
+ doc = Nokogiri::XML(File.open(item['path']))
145
+ connect[tag].merge! extract_composite_metadata(doc)
146
+ connect[tag][:technical_files] << item
147
+ else
148
+ connect[tag][:miscellaneous_files] << item
149
+ end
150
+ end
151
+ end
152
+ end
153
+ connect
154
+ end
155
+
156
+
157
+
158
+ def write_results(out, connect)
159
+ file = File.open(out, 'w')
160
+ file.write JSON.pretty_generate(connect)
161
+ file.close
162
+ end
163
+
164
+ if(ARGV.length != 2)
165
+ puts HELP
166
+ exit 1
167
+ else
168
+ file = ARGV[0]
169
+ manifest = JSON.parse(File.read(file))
170
+ root = File.dirname(File.expand_path(file))
171
+ connect = group_by_tag(root, manifest)
172
+ write_results ARGV[1], connect
173
+ # pp connect
174
+ exit 0
175
+ end