Skip to content

Commit 8680c0e

Browse files
authored
Merge branch 'samply:main' into rennkater-patch-1
2 parents e204028 + 8c6113c commit 8680c0e

File tree

12 files changed

+370
-33
lines changed

12 files changed

+370
-33
lines changed

.github/workflows/build.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
go-version-file: go.mod
4242

4343
- name: Initialize CodeQL
44-
uses: github/codeql-action/init@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4
44+
uses: github/codeql-action/init@014f16e7ab1402f30e7c3329d33797e7948572db # v4
4545
with:
4646
languages: go
4747

@@ -60,7 +60,7 @@ jobs:
6060
run: go build .
6161

6262
- name: Perform CodeQL Analysis
63-
uses: github/codeql-action/analyze@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4
63+
uses: github/codeql-action/analyze@014f16e7ab1402f30e7c3329d33797e7948572db # v4
6464

6565
- name: Upload Binary
6666
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
@@ -122,7 +122,7 @@ jobs:
122122
run: go build .
123123

124124
- name: Generate SBOM
125-
uses: anchore/sbom-action@f8bdd1d8ac5e901a77a92f111440fdb1b593736b # v0
125+
uses: anchore/sbom-action@8e94d75ddd33f69f691467e42275782e4bfefe84 # v0
126126
with:
127127
syft-version: 'v1.29.0'
128128
artifact-name: blazectl-${{ matrix.os }}-${{ matrix.arch }}-sbom.spdx.json
@@ -156,8 +156,9 @@ jobs:
156156
run: zip -q "blazectl-$VERSION-$OS-$ARCH.zip" blazectl.exe
157157

158158
- name: Release
159-
uses: softprops/action-gh-release@aec2ec56f94eb8180ceec724245f64ef008b89f5 # v2
159+
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2
160160
with:
161+
draft: true
161162
files: blazectl-*
162163

163164
integration-test:

.github/workflows/scorecard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,6 @@ jobs:
5858
# Upload the results to GitHub's code scanning dashboard (optional).
5959
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
6060
- name: "Upload to code-scanning"
61-
uses: github/codeql-action/upload-sarif@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4
61+
uses: github/codeql-action/upload-sarif@014f16e7ab1402f30e7c3329d33797e7948572db # v4
6262
with:
6363
sarif_file: results.sarif

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Available Commands:
4545
download-history Download history in NDJSON format
4646
evaluate-measure Evaluates a Measure
4747
help Help about any command
48+
render-report Renders a MeasureReport
4849
upload Upload transaction bundles
4950
5051
Flags:
@@ -191,11 +192,19 @@ Given a measure in YAML form, creates the required FHIR resources, evaluates tha
191192
You can run:
192193

193194
```sh
194-
blazectl evaluate-measure --server "http://localhost:8080/fhir" stratifier-condition-code.yml
195+
blazectl evaluate-measure --server "http://localhost:8080/fhir" query.yml
195196
```
196197

197198
More comprehensive documentation can be found in the [Blaze CQL Queries Documentation][9].
198199

200+
### Render Report
201+
202+
Renders a FHIR MeasureReport resource as simple, standalone HTML file. The idea is to combine two `blazectl` calls, first the `evaluate-measure` call and second the `render-report` call.
203+
204+
```sh
205+
blazectl evaluate-measure --server "http://localhost:8080/fhir" query.yml | blazectl render-report > query.html
206+
```
207+
199208
## GitHub Attestations
200209

201210
To ensure trust and security in the software supply chain, GitHub [attestations][11] are available for all `blazectl` binaries. To verify the attestations, please install the [GitHub CLI][10] tool and run:

cmd/compact.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ var compactCmd = &cobra.Command{
8888
return err
8989
}
9090

91+
fmt.Printf("Start compacting column family `%s` in database `%s`...\n", args[1], args[0])
9192
req, err := client.NewPostSystemOperationRequest("compact", true, createParameters(args[0], args[1]))
9293
if err != nil {
9394
return err

cmd/evaluateMeasure.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,20 @@ import (
66
"encoding/json"
77
"errors"
88
"fmt"
9+
"io"
10+
"net/http"
11+
"net/url"
12+
"os"
13+
"os/signal"
14+
"time"
15+
916
"github.com/goccy/go-yaml"
1017
"github.com/google/uuid"
1118
"github.com/samply/blazectl/data"
1219
"github.com/samply/blazectl/fhir"
1320
"github.com/samply/blazectl/util"
1421
fm "github.com/samply/golang-fhir-models/fhir-models/fhir"
1522
"github.com/spf13/cobra"
16-
"io"
17-
"net/http"
18-
"net/url"
19-
"os"
20-
"os/signal"
21-
"time"
2223
)
2324

2425
var forceSync bool
@@ -69,6 +70,12 @@ func createMeasureGroup(g data.Group) (*fm.MeasureGroup, error) {
6970
},
7071
}
7172
}
73+
if g.Code != "" {
74+
group.Code = &fm.CodeableConcept{Text: &g.Code}
75+
}
76+
if g.Description != "" {
77+
group.Description = &g.Description
78+
}
7279
for i, population := range g.Population {
7380
p, err := createMeasureGroupPopulation(population)
7481
if err != nil {
@@ -103,22 +110,26 @@ func createMeasureGroupPopulation(population data.Population) (*fm.MeasureGroupP
103110
}, nil
104111
}
105112

106-
func createMeasureGroupStratifier(stratifier data.Stratifier) (*fm.MeasureGroupStratifier, error) {
107-
if stratifier.Code == "" {
113+
func createMeasureGroupStratifier(s data.Stratifier) (*fm.MeasureGroupStratifier, error) {
114+
if s.Code == "" {
108115
return nil, fmt.Errorf("missing code")
109116
}
110-
if stratifier.Expression == "" {
117+
if s.Expression == "" {
111118
return nil, fmt.Errorf("missing expression name")
112119
}
113-
return &fm.MeasureGroupStratifier{
120+
stratifier := fm.MeasureGroupStratifier{
114121
Code: &fm.CodeableConcept{
115-
Text: &stratifier.Code,
122+
Text: &s.Code,
116123
},
117124
Criteria: &fm.Expression{
118125
Language: "text/cql-identifier",
119-
Expression: &stratifier.Expression,
126+
Expression: &s.Expression,
120127
},
121-
}, nil
128+
}
129+
if s.Description != "" {
130+
stratifier.Description = &s.Description
131+
}
132+
return &stratifier, nil
122133
}
123134

124135
func createCoding(system string, code string) fm.Coding {

cmd/evalueMeasure_test.go

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7-
"github.com/samply/blazectl/data"
8-
"github.com/samply/blazectl/fhir"
9-
fm "github.com/samply/golang-fhir-models/fhir-models/fhir"
10-
"github.com/stretchr/testify/assert"
117
"net/http"
128
"net/http/httptest"
139
"net/url"
1410
"testing"
11+
12+
"github.com/samply/blazectl/data"
13+
"github.com/samply/blazectl/fhir"
14+
fm "github.com/samply/golang-fhir-models/fhir-models/fhir"
15+
"github.com/stretchr/testify/assert"
1516
)
1617

1718
func TestCreateMeasureResource(t *testing.T) {
@@ -106,6 +107,52 @@ func TestCreateMeasureResource(t *testing.T) {
106107
assert.Equal(t, "InInitialPopulation", *resource.Group[0].Population[0].Criteria.Expression)
107108
})
108109

110+
t.Run("with one group with code and one population", func(t *testing.T) {
111+
m := data.Measure{
112+
Group: []data.Group{
113+
{
114+
Code: "observation",
115+
Population: []data.Population{
116+
{
117+
Expression: "InInitialPopulation",
118+
},
119+
},
120+
},
121+
},
122+
}
123+
124+
resource, err := CreateMeasureResource(m, measureUrl, libraryUrl)
125+
if err != nil {
126+
t.Fatalf("error while generating the measure resource: %v", err)
127+
}
128+
129+
assert.Equal(t, 1, len(resource.Group))
130+
assert.Equal(t, "observation", *resource.Group[0].Code.Text)
131+
})
132+
133+
t.Run("with one group with description and one population", func(t *testing.T) {
134+
m := data.Measure{
135+
Group: []data.Group{
136+
{
137+
Description: "all the observations",
138+
Population: []data.Population{
139+
{
140+
Expression: "InInitialPopulation",
141+
},
142+
},
143+
},
144+
},
145+
}
146+
147+
resource, err := CreateMeasureResource(m, measureUrl, libraryUrl)
148+
if err != nil {
149+
t.Fatalf("error while generating the measure resource: %v", err)
150+
}
151+
152+
assert.Equal(t, 1, len(resource.Group))
153+
assert.Equal(t, "all the observations", *resource.Group[0].Description)
154+
})
155+
109156
t.Run("with one group and one population and one empty stratifier", func(t *testing.T) {
110157
m := data.Measure{
111158
Group: []data.Group{
@@ -185,6 +232,36 @@ func TestCreateMeasureResource(t *testing.T) {
185232
assert.Equal(t, "foo", *resource.Group[0].Stratifier[0].Code.Text)
186233
})
187234

235+
t.Run("with one group and one population and one stratifier with description", func(t *testing.T) {
236+
m := data.Measure{
237+
Group: []data.Group{
238+
{
239+
Population: []data.Population{
240+
{
241+
Expression: "InInitialPopulation",
242+
},
243+
},
244+
Stratifier: []data.Stratifier{
245+
{
246+
Code: "foo",
247+
Description: "the foo stratifier",
248+
Expression: "Foo",
249+
},
250+
},
251+
},
252+
},
253+
}
254+
255+
resource, err := CreateMeasureResource(m, measureUrl, libraryUrl)
256+
if err != nil {
257+
t.Fatalf("error while generating the measure resource: %v", err)
258+
}
259+
260+
assert.Equal(t, 1, len(resource.Group))
261+
assert.Equal(t, 1, len(resource.Group[0].Stratifier))
262+
assert.Equal(t, "the foo stratifier", *resource.Group[0].Stratifier[0].Description)
263+
})
264+
188265
t.Run("with one Condition group", func(t *testing.T) {
189266
m := data.Measure{
190267
Group: []data.Group{

cmd/renderReport.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2019 - 2025 The Samply Community
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cmd
16+
17+
import (
18+
_ "embed"
19+
"encoding/json"
20+
"io"
21+
"os"
22+
23+
"html/template"
24+
25+
fm "github.com/samply/golang-fhir-models/fhir-models/fhir"
26+
"github.com/spf13/cobra"
27+
)
28+
29+
//go:embed report-template.gohtml
30+
var reportTemplate string
31+
32+
var renderReportCmd = &cobra.Command{
33+
Use: "render-report",
34+
Short: "Renders a MeasureReport",
35+
RunE: func(cmd *cobra.Command, args []string) error {
36+
data, err := io.ReadAll(os.Stdin)
37+
if err != nil {
38+
return err
39+
}
40+
41+
var report fm.MeasureReport
42+
if err := json.Unmarshal(data, &report); err != nil {
43+
return err
44+
}
45+
46+
funcMap := template.FuncMap{
47+
"inc": func(i int) int {
48+
return i + 1
49+
},
50+
"ratio": func(n int, d int) float32 {
51+
return float32(n*100) / float32(d)
52+
},
53+
"isNullString": func(s *string) bool {
54+
return s == nil || *s == "null"
55+
},
56+
}
57+
58+
tmpl := template.Must(template.New("report").Funcs(funcMap).Parse(reportTemplate))
59+
60+
if err := tmpl.Execute(os.Stdout, report); err != nil {
61+
return err
62+
}
63+
64+
return nil
65+
},
66+
}
67+
68+
func init() {
69+
rootCmd.AddCommand(renderReportCmd)
70+
}

0 commit comments

Comments
 (0)