Skip to content

Commit 9e47342

Browse files
authored
Add examples to application (#365)
1 parent a35a4c7 commit 9e47342

File tree

11 files changed

+1976
-1
lines changed

11 files changed

+1976
-1
lines changed

.github/workflows/build-hammer.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,35 @@ jobs:
131131
artifacts/hammer-linux/Hammer-linux.zip
132132
artifacts/hammer-windows/Hammer-windows.zip
133133
artifacts/hammer-macos/Hammer-macos.zip
134+
135+
validate_examples_metadata:
136+
name: Validate examples metadata file
137+
138+
runs-on: ubuntu-20.04
139+
140+
steps:
141+
- uses: actions/checkout@v2
142+
with:
143+
fetch-depth: 0
144+
145+
- name: Check if metadata or schema have changed
146+
id: changed-files-specific
147+
uses: tj-actions/changed-files@v5.3
148+
with:
149+
files: |
150+
assets/examples/.metadata.schema.json
151+
assets/examples/metadata.json
152+
153+
- name: Set up npm
154+
if: steps.changed-files-specific.outputs.any_changed == 'true'
155+
uses: actions/setup-node@v1
156+
with:
157+
node-version: 12
158+
159+
- name: Install ajv
160+
if: steps.changed-files-specific.outputs.any_changed == 'true'
161+
run: npm install -g ajv-cli
162+
163+
- name: Validate metadata
164+
if: steps.changed-files-specific.outputs.any_changed == 'true'
165+
run: ajv -s assets/examples/.metadata.schema.json -d assets/examples/metadata.json

Hammer.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
<ItemGroup>
118118
<!-- only copy on release because the file is already present then. Couldn't get msbuild to download on its own -->
119119
<Content Include="org.hl7.fhir.validator.jar" Condition=" '$(Configuration)' == 'Release' " CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
120+
<Content Include="assets/examples/*" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
120121
<Content Include="*.qml" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
121122
<Content Include="assets/run-hammer.sh" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
122123
<Content Include="assets/run-hammer.bat" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />

Main.qml

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,66 @@ ApplicationWindow {
164164
}
165165
}
166166
}
167+
}
168+
169+
Row {
170+
id: examplesRow
171+
y: parent.height - 300
172+
anchors.horizontalCenter: parent.horizontalCenter
173+
spacing: 20
174+
visible: textArea.state === "MINIMAL"
167175

176+
Label {
177+
id: samplesLabel
178+
text: qsTr("Examples to get you started with:")
179+
font.bold: true
180+
opacity: 0.6
181+
font.pointSize: 16
182+
bottomPadding: 10
183+
}
184+
}
185+
186+
187+
ListView {
188+
id: examplesView
189+
anchors.top: examplesRow.bottom
190+
anchors.horizontalCenter: parent.horizontalCenter
191+
implicitWidth: contentItem.childrenRect.width
192+
implicitHeight: contentItem.childrenRect.height
193+
visible: textArea.state === "MINIMAL"
194+
195+
196+
Connections {
197+
target: appmodel
198+
function onExamplesLoaded () {
199+
200+
examplesView.model = Net.toListModel(appmodel.examples)
201+
}
202+
}
203+
204+
delegate: Button {
205+
id: control
206+
text: `${modelData.title}<br><small>${modelData.description}</small>`
207+
anchors { horizontalCenter: parent.horizontalCenter; }
208+
flat: true
209+
onClicked: appmodel.loadResourceFile(modelData.filepath)
210+
211+
contentItem: Label {
212+
text: control.text
213+
textFormat: Text.RichText
214+
opacity: control.hovered ? 1.0 : 0.3
215+
horizontalAlignment: Text.AlignHCenter
216+
verticalAlignment: Text.AlignVCenter
217+
elide: Text.ElideRight
218+
}
219+
220+
MouseArea {
221+
id: mouseArea
222+
anchors.fill: parent
223+
cursorShape: Qt.PointingHandCursor
224+
acceptedButtons: Qt.NoButton
225+
}
226+
}
168227
}
169228

170229
Text {
@@ -175,7 +234,7 @@ ApplicationWindow {
175234
enabled: false
176235
z: 0
177236
rotation: -45
178-
opacity: 0.1
237+
opacity: 0.03
179238
font.pixelSize: 96
180239
}
181240

Program.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
class Program
3737
{
3838
[Signal("validationStarted")]
39+
[Signal("examplesLoaded")]
3940
[Signal("updateAvailable", NetVariantType.String)]
4041
public class AppModel : IDisposable
4142
{
@@ -245,6 +246,23 @@ public List<Issue> DotnetIssues
245246
set => this.SetProperty(ref _dotnetIssues, value);
246247
}
247248

249+
// exposes examples to QML
250+
private List<Example> _examples = new List<Example>();
251+
[NotifySignal]
252+
public List<Example> Examples
253+
{
254+
get => _examples;
255+
set => this.SetProperty(ref _examples, value);
256+
}
257+
258+
// loads examples from disk
259+
public class DiskExample
260+
{
261+
public string Filename { get; set; }
262+
public string Title { get; set; }
263+
public string Description { get; set; }
264+
}
265+
248266
private bool _javaValidationCrashed;
249267
[NotifySignal]
250268
public bool JavaValidationCrashed
@@ -378,6 +396,33 @@ public int LinePosition
378396
}
379397
}
380398

399+
public class Example
400+
{
401+
private string _filepath;
402+
[NotifySignal]
403+
public string Filepath
404+
{
405+
get => _filepath;
406+
set => this.SetProperty(ref _filepath, value);
407+
}
408+
409+
private string _title;
410+
[NotifySignal]
411+
public string Title
412+
{
413+
get => _title;
414+
set => this.SetProperty(ref _title, value);
415+
}
416+
417+
private string _description;
418+
[NotifySignal]
419+
public string Description
420+
{
421+
get => _description;
422+
set => this.SetProperty(ref _description, value);
423+
}
424+
}
425+
381426
private class MarkdownIssue
382427
{
383428
public string Severity;
@@ -987,6 +1032,27 @@ public async void StartValidation()
9871032
}
9881033
}
9891034

1035+
// load examples. This should be done syncronously: https://ux.stackexchange.com/q/138673/108628
1036+
// but if it can be made quicker, then even better
1037+
public void LoadExamples()
1038+
{
1039+
JArray jsonExamples = JArray.Parse(File.ReadAllText(Path.Combine(AppModel.Extensions.GetApplicationLocation(), "assets", "examples", "metadata.json")));
1040+
1041+
Examples = new List<Example>();
1042+
foreach (JToken jsonExample in jsonExamples)
1043+
{
1044+
var diskExample = jsonExample.ToObject<DiskExample>();
1045+
var example = new Example {
1046+
Filepath = $"file://{Path.Combine(AppModel.Extensions.GetApplicationLocation(), "assets", "examples", diskExample.Filename)}",
1047+
Title = diskExample.Title,
1048+
Description = diskExample.Description,
1049+
1050+
};
1051+
Examples.Add(example);
1052+
}
1053+
this.ActivateSignal("examplesLoaded");
1054+
}
1055+
9901056
public void CancelValidation()
9911057
{
9921058
// Signal the CancellationToken in the tasks that we want to cancel.
@@ -1215,6 +1281,12 @@ static int Main(string[] args)
12151281
// instance.
12161282
cliParser.Process();
12171283

1284+
// do this async
1285+
Stopwatch sw = Stopwatch.StartNew();
1286+
AppModel.Instance.LoadExamples();
1287+
sw.Stop();
1288+
Console.WriteLine("LoadExamples time taken: {0}ms", sw.Elapsed.TotalMilliseconds);
1289+
12181290
AppModel.Instance.CheckForUpdates();
12191291

12201292
return app.Exec();
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$id": "https://github.com/health-validator/Hammer/examples.json",
4+
"type": "array",
5+
"title": "JSON Schema for Hammer metadata examples",
6+
"description": "This schema validates the metadata.json file that's included with Hammer (health validator)",
7+
"default": [],
8+
"examples": [
9+
[
10+
{
11+
"filename": "patient.json",
12+
"title": "Sample patient",
13+
"description": "Valid Patient resource (JSON)"
14+
}
15+
]
16+
],
17+
"items": {
18+
"$id": "#/items",
19+
"anyOf": [
20+
{
21+
"$id": "#/items/anyOf/0",
22+
"type": "object",
23+
"title": "A single file with a single resource inside",
24+
"description": "File can contain a Bundle",
25+
"default": {},
26+
"examples": [
27+
{
28+
"filename": "patient.json",
29+
"title": "Sample patient",
30+
"description": "Valid Patient resource (JSON)"
31+
}
32+
],
33+
"required": [
34+
"filename",
35+
"title",
36+
"description"
37+
],
38+
"properties": {
39+
"filename": {
40+
"$id": "#/items/anyOf/0/properties/filename",
41+
"type": "string",
42+
"title": "File name in the examples directory",
43+
"default": "",
44+
"examples": [
45+
"patient.json"
46+
]
47+
},
48+
"title": {
49+
"$id": "#/items/anyOf/0/properties/title",
50+
"type": "string",
51+
"title": "Title of the example",
52+
"description": "Title will be shown in the UI",
53+
"default": "",
54+
"examples": [
55+
"Sample patient"
56+
]
57+
},
58+
"description": {
59+
"$id": "#/items/anyOf/0/properties/description",
60+
"type": "string",
61+
"title": "Description of the resource",
62+
"description": "Description to show beneath the title",
63+
"default": "",
64+
"examples": [
65+
"Valid Patient resource (JSON)"
66+
]
67+
}
68+
},
69+
"additionalProperties": true
70+
}
71+
]
72+
}
73+
}

assets/examples/condition.xml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="UTF-8"?><Condition xmlns="http://hl7.org/fhir">
2+
<id value="example"/>
3+
<text>
4+
<status value="generated"/>
5+
<div xmlns="http://www.w3.org/1999/xhtml">Severe burn of left ear (Date: 24-May 2012)</div>
6+
</text>
7+
<clinicalStatus value="PRESENT"/>
8+
<verificationStatus value="confirmed"/>
9+
<category>
10+
<coding>
11+
<system value="http://terminology.hl7.org/CodeSystem/condition-category"/>
12+
<code value="encounter-diagnosis"/>
13+
<display value="Encounter Diagnosis"/>
14+
</coding>
15+
<!-- and also a SNOMED CT coding -->
16+
<coding>
17+
<system value="http://snomed.info/sct"/>
18+
<code value="439401001"/>
19+
<display value="Diagnosis"/>
20+
</coding>
21+
</category>
22+
<severity>
23+
<coding>
24+
<system value="http://snomed.info/sct"/>
25+
<code value="24484000"/>
26+
<display value="Severe"/>
27+
</coding>
28+
</severity>
29+
<code>
30+
<coding>
31+
<system value="http://snomed.info/sct"/>
32+
<code value="39065001"/>
33+
<display value="Burn of ear"/>
34+
</coding>
35+
<text value="Burnt Ear"/>
36+
</code>
37+
<bodySite>
38+
<coding>
39+
<system value="http://snomed.info/sct"/>
40+
<code value="49521004"/>
41+
<display value="Left external ear structure"/>
42+
</coding>
43+
<text value="Left Ear"/>
44+
</bodySite>
45+
<subject>
46+
<reference value="Patient/example"/>
47+
</subject>
48+
<onsetDateTime value="2012-05-24"/>
49+
<abatementBoolean value="true"/>
50+
<stage>
51+
<extension url="https://example.org/fhir/fake-extension">
52+
<valueString value="a random value"/>
53+
</extension>
54+
</stage>
55+
</Condition>

assets/examples/metadata.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[
2+
{
3+
"filename": "patient.json",
4+
"title": "Patient",
5+
"description": "Valid Patient resource (json)"
6+
},
7+
{
8+
"filename": "observation.json",
9+
"title": "Observation",
10+
"description": "Invalid Observation resource - decimal datatype too big (json)"
11+
},
12+
{
13+
"filename": "condition.xml",
14+
"title": "Condition",
15+
"description": "Invalid Condition resource - bad clinicalStatus code, and the stage is missing elements (xml)"
16+
},
17+
{
18+
"filename": "questionnaire.json",
19+
"title": "Questionnaire",
20+
"description": "Invalid Questionnaire resource (json)"
21+
},
22+
{
23+
"filename": "organization.xml",
24+
"title": "Organization",
25+
"description": "Invalid Organization - address.use is missing a closing tag, and the org home is of type 'home' (xml)"
26+
}
27+
]

0 commit comments

Comments
 (0)