conceptql 0.6.0 → 0.7.0

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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +176 -0
  3. data/README.md +4 -2
  4. data/assets/diagram/diagram.css +331 -0
  5. data/assets/diagram/fonts/OFL.txt +93 -0
  6. data/assets/diagram/fonts/Roboto-400.ttf +0 -0
  7. data/assets/diagram/fonts/Roboto-700.ttf +0 -0
  8. data/assets/diagram/fonts/Roboto-Italic-400.ttf +0 -0
  9. data/assets/diagram/fonts/Roboto-Italic-Latin.woff2 +0 -0
  10. data/assets/diagram/fonts/Roboto-Normal-Latin.woff2 +0 -0
  11. data/assets/diagram/operators.svg +224 -0
  12. data/config/vocabularies.csv +85 -85
  13. data/docs/spec/README.md +2624 -0
  14. data/docs/spec/examples/after-operator.json +562 -0
  15. data/docs/spec/examples/any-overlap-operator.json +106 -0
  16. data/docs/spec/examples/before-operator.json +437 -0
  17. data/docs/spec/examples/co-reported-operator.json +168 -0
  18. data/docs/spec/examples/contains-operator.json +97 -0
  19. data/docs/spec/examples/date-range-operator.json +275 -0
  20. data/docs/spec/examples/death-operator.json +103 -0
  21. data/docs/spec/examples/during-operator.json +178 -0
  22. data/docs/spec/examples/episode-operator.json +80 -0
  23. data/docs/spec/examples/equal-operator.json +79 -0
  24. data/docs/spec/examples/ethnicity-operator.json +86 -0
  25. data/docs/spec/examples/except-operator.json +536 -0
  26. data/docs/spec/examples/filter-operator.json +218 -0
  27. data/docs/spec/examples/first-operator.json +148 -0
  28. data/docs/spec/examples/gender-operator.json +257 -0
  29. data/docs/spec/examples/hospice-operator.json +40 -0
  30. data/docs/spec/examples/hospitalization-operator.json +131 -0
  31. data/docs/spec/examples/information-periods-operator.json +131 -0
  32. data/docs/spec/examples/last-operator.json +148 -0
  33. data/docs/spec/examples/match-operator.json +186 -0
  34. data/docs/spec/examples/nth-occurrence-operator.json +300 -0
  35. data/docs/spec/examples/numeric-filter-operator.json +77 -0
  36. data/docs/spec/examples/one-in-two-out-operator.json +158 -0
  37. data/docs/spec/examples/person-filter-operator.json +919 -0
  38. data/docs/spec/examples/person-operator.json +274 -0
  39. data/docs/spec/examples/place-of-service-filter-operator.json +57 -0
  40. data/docs/spec/examples/provenance-operator.json +148 -0
  41. data/docs/spec/examples/provider-filter-operator.json +77 -0
  42. data/docs/spec/examples/race-operator.json +131 -0
  43. data/docs/spec/examples/recall-operator.json +289 -0
  44. data/docs/spec/examples/snf-operator.json +40 -0
  45. data/docs/spec/examples/time-window-operator.json +736 -0
  46. data/docs/spec/examples/trim-date-end-operator.json +201 -0
  47. data/docs/spec/examples/trim-date-start-operator.json +201 -0
  48. data/docs/spec/examples/union-operator.json +547 -0
  49. data/docs/spec/examples/vocabulary-operator.json +232 -0
  50. data/lib/conceptql/behaviors/metadatable.rb +68 -2
  51. data/lib/conceptql/behaviors/provenanceable.rb +4 -1
  52. data/lib/conceptql/behaviors/utilizable.rb +3 -0
  53. data/lib/conceptql/code_case_mismatch_error.rb +50 -0
  54. data/lib/conceptql/data_model/gdm.rb +23 -2
  55. data/lib/conceptql/diagram_renderer/app.rb +59 -0
  56. data/lib/conceptql/diagram_renderer/configuration.rb +87 -0
  57. data/lib/conceptql/diagram_renderer/diagram_data.rb +80 -0
  58. data/lib/conceptql/diagram_renderer/drawing.rb +255 -0
  59. data/lib/conceptql/diagram_renderer/geometry.rb +380 -0
  60. data/lib/conceptql/diagram_renderer/input_error.rb +11 -0
  61. data/lib/conceptql/diagram_renderer/js_format.rb +112 -0
  62. data/lib/conceptql/diagram_renderer/resvg.rb +120 -0
  63. data/lib/conceptql/diagram_renderer/sprite.rb +73 -0
  64. data/lib/conceptql/diagram_renderer/static.rb +70 -0
  65. data/lib/conceptql/diagram_renderer/svg.rb +73 -0
  66. data/lib/conceptql/diagram_renderer/tree_layout.rb +287 -0
  67. data/lib/conceptql/diagram_renderer.rb +82 -0
  68. data/lib/conceptql/lexicon/lexicon_strategy.rb +16 -0
  69. data/lib/conceptql/lexicon.rb +63 -11
  70. data/lib/conceptql/nodifier.rb +5 -3
  71. data/lib/conceptql/operators/after.rb +15 -1
  72. data/lib/conceptql/operators/any_overlap.rb +6 -1
  73. data/lib/conceptql/operators/before.rb +11 -1
  74. data/lib/conceptql/operators/binary_operator_operator.rb +12 -2
  75. data/lib/conceptql/operators/co_reported.rb +9 -4
  76. data/lib/conceptql/operators/contains.rb +6 -1
  77. data/lib/conceptql/operators/count.rb +2 -0
  78. data/lib/conceptql/operators/date_range.rb +15 -3
  79. data/lib/conceptql/operators/death.rb +5 -1
  80. data/lib/conceptql/operators/dedup.rb +2 -0
  81. data/lib/conceptql/operators/during.rb +6 -1
  82. data/lib/conceptql/operators/episode.rb +13 -2
  83. data/lib/conceptql/operators/equal.rb +11 -1
  84. data/lib/conceptql/operators/ethnicity.rb +19 -7
  85. data/lib/conceptql/operators/except.rb +10 -1
  86. data/lib/conceptql/operators/filter.rb +6 -1
  87. data/lib/conceptql/operators/first.rb +19 -9
  88. data/lib/conceptql/operators/from.rb +3 -0
  89. data/lib/conceptql/operators/gender.rb +10 -2
  90. data/lib/conceptql/operators/hospice.rb +7 -1
  91. data/lib/conceptql/operators/hospitalization.rb +6 -1
  92. data/lib/conceptql/operators/information_periods.rb +26 -7
  93. data/lib/conceptql/operators/invalid.rb +4 -0
  94. data/lib/conceptql/operators/last.rb +19 -9
  95. data/lib/conceptql/operators/match.rb +6 -1
  96. data/lib/conceptql/operators/multiple_vocabularies.rb +17 -3
  97. data/lib/conceptql/operators/numeric.rb +4 -0
  98. data/lib/conceptql/operators/numeric_filter.rb +13 -3
  99. data/lib/conceptql/operators/occurrence.rb +33 -12
  100. data/lib/conceptql/operators/one_in_two_out.rb +22 -5
  101. data/lib/conceptql/operators/operator.rb +8 -4
  102. data/lib/conceptql/operators/person.rb +5 -1
  103. data/lib/conceptql/operators/person_filter.rb +14 -1
  104. data/lib/conceptql/operators/place_of_service_filter.rb +14 -3
  105. data/lib/conceptql/operators/provenance.rb +17 -8
  106. data/lib/conceptql/operators/provider_filter.rb +18 -6
  107. data/lib/conceptql/operators/race.rb +14 -9
  108. data/lib/conceptql/operators/read.rb +11 -1
  109. data/lib/conceptql/operators/recall.rb +34 -2
  110. data/lib/conceptql/operators/snf.rb +7 -1
  111. data/lib/conceptql/operators/sum.rb +4 -0
  112. data/lib/conceptql/operators/temporal_operator.rb +4 -2
  113. data/lib/conceptql/operators/time_window.rb +27 -4
  114. data/lib/conceptql/operators/trim_date.rb +6 -14
  115. data/lib/conceptql/operators/trim_date_end.rb +9 -3
  116. data/lib/conceptql/operators/trim_date_start.rb +9 -3
  117. data/lib/conceptql/operators/union.rb +13 -1
  118. data/lib/conceptql/operators/vocabulary.rb +80 -6
  119. data/lib/conceptql/paths.rb +9 -1
  120. data/lib/conceptql/version.rb +1 -1
  121. data/lib/conceptql/vocabularies/drg_systems.rb +3 -2
  122. data/lib/conceptql/vocabularies/entry.rb +14 -11
  123. data/lib/conceptql/vocabularies/vocabulary_class_factory.rb +2 -1
  124. data/lib/conceptql.rb +3 -0
  125. metadata +75 -2
  126. data/doc/spec.md +0 -2091
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03af33e5bd8d7515c78256e11c98fe9aab5bd2501f56fea7db2960acf55c3ea6
4
- data.tar.gz: 27e02e2f1da7e8acfe3bda3a2a7787327f386716a1d8ddaa16ca371e2798bd1a
3
+ metadata.gz: cf5cfc7db1200e2f00e6006a54f138e6837c1d05483330c7dccb5f9b8a93a907
4
+ data.tar.gz: 8095c96f350258266df92fcd521d99460a9ff48af70657b6b980294a29fa6a47
5
5
  SHA512:
6
- metadata.gz: 209474ec28b2fa409fa6d0bde640c411d6beaf5bff0c67c346483fc25b59e5465f2ca2fb444376397b1080d492d4a037d21314f0cd206837551807c016058748
7
- data.tar.gz: 46dca2627d4085ee7c3ad42b821b03c685536cd933ea63987061cbad58bec28c01c07e9dcad48845fe66d5343265d289599f87ef2a6c67a92d61818624f446c3
6
+ metadata.gz: 3a04d687527d5d808446f3fef305805a6915679c1c079dc1edf98afdb9fa7c650525e929b89ee227fe0e9066d58c81585e8c027ff18efc5a793c30bece1454cd
7
+ data.tar.gz: c4302550620621d1dc241c60a0a47fe4fe31b9ee0dc143d4bd467fb52a775aff5f46eeac815a1470ef661c0103e716e518610f04d55f8ec4b4975a6c4ba9d3f4
data/CHANGELOG.md CHANGED
@@ -4,6 +4,182 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.7.0] - 2026-11-09
8
+
9
+ ### Added
10
+
11
+ - `ConceptQL::DiagramRenderer`, the diagram renderer of the Jigsaw diagram editor, moved into
12
+ the gem. It draws a diagram tree (`ConceptQL::Diagram.render`) as a standalone static SVG
13
+ (`render_static`), a PNG (`png`) or the in-app SVG (`render_app`), with the same drawing as in
14
+ the editor. Its SVG output differs from the editor's only in the text of the `<style>` element,
15
+ and its PNG output is byte-identical. The gem ships its assets under `assets/diagram/`: the
16
+ operator icons, the diagram CSS, and the Roboto fonts with their license (SIL Open Font
17
+ License 1.1). The gemspec lists both licenses, `MIT` and `OFL-1.1`. The configuration
18
+ points at them by default, so a host needs no configure call. `require "conceptql"` loads
19
+ none of the renderer until its first use. `render_static` and `png` run the resvg program,
20
+ version 0.33.0. `just test-diagram` runs the renderer's tests against the
21
+ editor's JavaScript references, and `just ci` and GitHub CI run it. The release workflow
22
+ checks that the built gem contains every file under `assets/diagram/`, and it draws one
23
+ diagram with the installed gem. See `docs/diagram_renderer.md`.
24
+ - The renderer's diagram CSS (`assets/diagram/diagram.css`) holds only the rules of the
25
+ editor's stylesheet that can match an element of a diagram. The editor's page rules (for
26
+ `html`, `body`, `h1`, `.sidebar` and the like) are left out. In a page, the `<style>` of the
27
+ in-app SVG applies to the whole page, so a page that used those rules must supply them.
28
+ - The first `render_static` or `png` call in a process runs `resvg --version`. When resvg
29
+ reports another version than 0.33.0, the call writes one warning to stderr that names both
30
+ versions. Then it draws the diagram anyway, and later calls do not check again.
31
+ - An operator class can say `hidden`. A hidden operator still runs, but `ConceptQL.metadata`
32
+ leaves it out. An operator is not hidden unless it says so, and a subclass does not inherit
33
+ the mark. `invalid`, `from`, `numeric`, `dedup`, `sum` and `count` are hidden. Before,
34
+ `ConceptQL.metadata` left out each operator without a category, and these six were the only
35
+ such operators. Now every operator has a category from `ConceptQL.categories`. The metadata
36
+ has no `hidden` key, and it publishes the same 119 operators on `gdm` as before.
37
+ - Operator reference documentation in the operator classes. `ConceptQL.metadata` serves two new
38
+ keys for each operator: `returns`, a text that says which records the operator returns, and
39
+ `examples`, a list of `{title, statement, help}`. An option or an argument can have a
40
+ `meaning` key, a text that says what it does. `instructions` stays the hint for typing a
41
+ value. The `label` option and the `left` and `right` options have a `meaning`. The Metadatable
42
+ DSL has `returns`, `example` and `option_meaning`, which replaces the meaning of one inherited
43
+ option. A subclass does not inherit `returns` or the examples. The vocabulary operators serve
44
+ the `returns` text and the examples of `ConceptQL::Operators::Vocabulary`, each operator
45
+ only the examples whose statement starts with its own name. All texts are plain text.
46
+ - `ConceptQL.spec_examples_path`, the directory `docs/spec/examples/` in the gem. It holds one
47
+ file, `<spec_id>.json`, for each spec_id of the operator reference. The file has the
48
+ examples of that section, each with its `title`, `statement`, `help`, the `columns` and the
49
+ first 10 `rows` of its result on the test data, and its diagram `tree`
50
+ (`conceptql-diagram/v1`, with counts). An application can show an operator's help from the
51
+ file of that operator, without a database. `just spec` writes the files, and
52
+ `just spec-check` fails when one of them would change.
53
+ - `information_periods` declares its argument, Information Types (a comma-separated list), so
54
+ an editor can offer the field. The argument is optional: without it, the operator still
55
+ selects every information period. Each value is a concept ID or a concept code of the
56
+ `GDM_INFORMATION_TYPE` vocabulary, such as `PARTD`, and a statement can mix both. An unknown
57
+ code gets the warning `unknown code(s)` and selects no period. Before, every argument went
58
+ through `to_i`, so a code silently became concept ID 0.
59
+ - `cpt` and `hcpcs` warn when the data has no record of their vocabulary but has CPT4_HCPCS
60
+ records, and the lexicon carries CPT4_HCPCS. Production ETL data stores CPT-4 and HCPCS codes
61
+ only as CPT4_HCPCS, so on that data the two operators select nothing (known issue, bead
62
+ `conceptql-rpy3`). The warning is "the data has no records of this vocabulary", and it says
63
+ to use `cpt_or_hcpcs`, which also reads CPT4_HCPCS. `cpt_or_hcpcs` does not give it. The
64
+ check runs two small existence queries on the data, and it skips a mock database as the
65
+ other code warnings do.
66
+
67
+ ### Changed
68
+
69
+ - The `inpatient_length_of_stay` and `outpatient_event_to_return` options of `one_in_two_out`
70
+ serve their text as `meaning`. Before, it was a `desc` key that the diagram editor did not
71
+ read.
72
+ - **Breaking:** a code lookup raises `ConceptQL::CodeCaseMismatchError` for a code that its
73
+ vocabulary stores only in another case. Since 0.6.0 a lookup compares codes case-exactly, so
74
+ `data_model.concept_ids(db, "GDM_PROV", ["line"])` returned `[]` without a word, because the
75
+ vocabulary stores `LINE`. t_shank made that call and lost every line row of an export. Now it
76
+ raises, and the message names the vocabulary, each such code and its stored spellings. The
77
+ error's `mismatches` attribute holds the same facts. It is an `ArgumentError`. The lookups
78
+ that take codes raise it: `concept_ids`, `known_codes`, `concepts_to_codes`,
79
+ `concepts_to_codes_in_class`, `code_meanings`, `concepts` and `concepts_ds`, on the
80
+ `Lexicon` and on the data model. `concepts` and `concepts_ds` check when they build the
81
+ dataset. A code that a lookup vocabulary stores in this case is no error, and neither is a
82
+ code that matches nothing in any case: it still finds no concept. A vocabulary whose
83
+ vocabulary_formats row declares `(?i:...)` already ignores case, so it never raises. The
84
+ check reads the concept table without a guard. When the concept table is missing,
85
+ `known_codes`, `code_meanings` and `concepts_to_codes_in_class` raise
86
+ `Sequel::DatabaseError`. 0.6.0 returned `[]`, `{}` and `nil`. Each of these lookups takes
87
+ `check_case: false`, which skips the check and keeps the 0.6.0 answer. A statement does not
88
+ raise: a vocabulary operator, `provenance` and `place_of_service_filter` pass
89
+ `check_case: false`. So `["icd10cm", "i16.5"]` still gets the warning
90
+ `improperly formatted code` and selects no rows. The utilization operators
91
+ (`hospitalization`, `hospice`, `snf`) look up their own codes with the check.
92
+ - **Breaking:** a positional upstream is a validation error (`has upstreams`) on `after`,
93
+ `any_overlap`, `before`, `contains`, `during`, `equal`, `except`, `filter`, `match`,
94
+ `person_filter`, `trim_date_end`, `trim_date_start` and `read`. The first twelve take their
95
+ operands from the `left` and `right` options, and `read` takes codes. Before, each of them
96
+ ignored a positional upstream without a word.
97
+ - **Breaking:** `occurrence`, `first` and `last` accept exactly one upstream. A second
98
+ positional upstream is a validation error (`has multiple upstreams`). Before, they used only
99
+ the first upstream and dropped the others without a word.
100
+ - **Breaking:** `equal` no longer offers the `within` option, and `first` and `last` no longer
101
+ offer `group_by_date`. The options had no effect on these operators. A statement that sets
102
+ one of them fails validation (`unsupported option`). A blank value or `false` passes, so a
103
+ cleared checkbox in the Jigsaw diagram editor still validates.
104
+ - **Breaking:** `DataModel::Gdm#information_period_where_clause` takes the database as its
105
+ first argument: `(db, arguments)`. Before, it took only `(arguments)`. It needs the database
106
+ to look up the concept codes of `information_periods`.
107
+ - The specification moved to `docs/spec/README.md`, and `ConceptQL.spec_path` returns it. Its
108
+ text comes from the conceptql_spec repository, which held the published spec, and it replaces
109
+ the copy that was `doc/spec.md`. `just spec` builds it from `docs/spec/README.cql.md` on the
110
+ DuckDB test data. It draws the diagram images with `ConceptQL::DiagramRenderer`, so it needs
111
+ resvg 0.33.0, and it needs no checkout of the Jigsaw diagram editor. `just spec-check`
112
+ draws every image again and fails when a committed image differs from its new drawing, is
113
+ missing, or is not used. The gem does not ship its diagram images.
114
+ - The `desc` of the published operators changed. The operator classes now hold the reference
115
+ documentation of the spec, so each `desc` is the summary of its operator's reference section.
116
+ `ConceptQL.metadata` serves the new texts, so a system that keeps a copy of the metadata,
117
+ such as the Jigsaw diagram editor, sees a different payload.
118
+ - The spec build writes the reference section of each published `spec_id` and the quick
119
+ reference of Appendix E from `ConceptQL.metadata` of `Database.new(nil, data_model: :gdm)`.
120
+ A section has the operator's `desc`, its upstream operators or its `left` and `right`
121
+ operands, its arguments and options with their `meaning`, its `returns` text and its examples
122
+ with their results and diagrams. `docs/spec/README.cql.md` marks the place of each generated
123
+ block with a directive, `<!-- conceptql-spec: operator <spec_id> -->`, and the spec's
124
+ Appendix D describes the directives. The hand-written copies of these texts are removed from
125
+ the spec.
126
+ - The spec lists every vocabulary operator with the full name of its vocabulary and its
127
+ domain, in a table that the build writes from `ConceptQL.metadata` at the new directive
128
+ `<!-- conceptql-spec: vocabulary-operators -->`. It marks a deprecated operator, such as
129
+ `drg`. The `Vocabulary` Operator section is now a `##` section of its own, so that it stays
130
+ under the 40,000-byte limit of a spec section. The other selection operators moved under a
131
+ new heading, Other Selection Operators. The anchors of the operator sections did not change.
132
+ - `ConceptQL.metadata` gives `read` and the multiple vocabulary operators, such as
133
+ `cpt_or_hcpcs`, a `long_name`. Before, it was empty. `read` has the long name of the Read
134
+ vocabulary in the OHDSI vocabulary table, "NHS UK Read Codes Version 2 (HSCIC)". A multiple
135
+ vocabulary operator names its vocabularies, for example "CPT4, HCPCS and CPT4_HCPCS". The
136
+ metadata of `read` also has `predominant_domains`, `["condition_occurrence"]`. Before, it was
137
+ empty.
138
+
139
+ ### Removed
140
+
141
+ - The `hidden` column of `config/vocabularies.csv`, with `Vocabularies::Entry#hidden?` and
142
+ `Vocabularies::Entry#visible?`. No code used them. The column did not hide any operator, and
143
+ it is not connected to the new `hidden` mark of an operator.
144
+ - **Breaking:** `Vocabularies::Entry#vocabulary_long_name`.
145
+ `Vocabularies::Entry#vocabulary_full_name` replaces it and reads the `vocabulary_full_name`
146
+ column. An `Entry` built from a hash takes the key `vocabulary_full_name`, not
147
+ `vocabulary_long_name`. `Entry#long_name` stays.
148
+
149
+ ### Fixed
150
+
151
+ - `min_upstreams` and `max_upstreams` in `ConceptQL.metadata` say how many positional
152
+ upstreams pass validation (99 means no limit). Before, `min_upstreams` always equalled
153
+ `max_upstreams`. `union` and `co_reported` served 99/99 and now serve 1/99. `first`, `last`
154
+ and `occurrence` served 99/99 and now serve 1/1. `person` and `death` served 1/1 and now
155
+ serve 0/1. `trim_date_end` and `trim_date_start` served 1/1 and now serve 0/0.
156
+
157
+ - The `long_name` in a vocabulary operator's metadata is the vocabulary's full name, read from
158
+ the `vocabulary_full_name` column of `config/vocabularies.csv` and the lexicons. Before, it
159
+ fell back to the short name (`atc` served "WHO ATC", not "WHO Anatomic Therapeutic Chemical
160
+ Classification") or was nil. The `short_name` does not change.
161
+
162
+ - `race`: an Unknown argument selected no patients. It now selects the patients with no race or
163
+ race concept ID 0. A race concept ID argument now also selects the more detailed races of
164
+ `config/race.yml`, as a race name does. Before, that was missed, which showed only with the
165
+ GDM vocabularies.
166
+
167
+ - `ethnicity`: Unknown now also selects the patients with no ethnicity, not only ethnicity
168
+ concept ID 0.
169
+
170
+ - Operator help texts that disagreed with the code: the input hints of `within` and `at_least`
171
+ (the temporal operators and `occurrence`), of `time_window`'s `start` and `end`, and of
172
+ `episode`'s `gap_of`; the old `desc` of the `TrimDate` base class; and the summary `desc` of
173
+ the vocabulary operators.
174
+
175
+ - A vocabulary whose domain is blank has no domain, so its operators select
176
+ `condition_occurrence` records. The GDM vocabulary table (`gdm_vocabs.vocabularies` in the
177
+ test data) gives all but one of its vocabularies the domain `''`, and the last one a NULL
178
+ domain. Before, the operators of the `''` vocabularies, such as
179
+ `cms_place_of_service`, got `criterion_domain` `''`, their predominant domain in the
180
+ metadata was `''`, and their annotation counts were keyed by `''`. The OHDSI vocabulary
181
+ tables give no domain, so they were not affected.
182
+
7
183
  ## [0.6.0] - 2026-11-02
8
184
 
9
185
  ### Added
data/README.md CHANGED
@@ -6,7 +6,9 @@ This gem interprets the ConceptQL language and translates it into SQL queries co
6
6
 
7
7
  ## Further Reading
8
8
 
9
- If you're interested in reading up on ConceptQL, a the specifications document is [available in markdown format](https://github.com/outcomesinsights/conceptql_spec).
9
+ The ConceptQL specification is [docs/spec/README.md](docs/spec/README.md).
10
+
11
+ `ConceptQL::DiagramRenderer` draws a statement's diagram as an SVG or a PNG. See [docs/diagram_renderer.md](docs/diagram_renderer.md).
10
12
 
11
13
  ## Motivation for ConceptQL
12
14
 
@@ -31,7 +33,7 @@ For instance, using ConceptQL we can take a statement that looks like this:
31
33
 
32
34
  And generate a diagram that looks like this:
33
35
 
34
- ![](doc/diagram_0.png)
36
+ ![](docs/spec/images/511539320d6cfd34954d3b092148bda1cb870caadc44211e3f189262d160f89f.svg)
35
37
 
36
38
  And generate SQL that looks like this:
37
39
 
@@ -0,0 +1,331 @@
1
+ /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
2
+
3
+ /**
4
+ * Hide the overflow in IE.
5
+ */
6
+ /* line 240, frontend/app/styles/mvcss/modules/../_01_reset.scss */
7
+ svg:not(:root) {
8
+ overflow: hidden;
9
+ }
10
+
11
+ /* line 141, frontend/app/styles/mvcss/modules/../_04_base.sass */
12
+ svg {
13
+ display: block;
14
+ }
15
+
16
+ /* line 17, frontend/app/styles/mvcss/modules/_vis.sass */
17
+ .vis {
18
+ bottom: 0;
19
+ left: 0;
20
+ position: absolute;
21
+ right: 0;
22
+ top: 0;
23
+ animation: vis 0.5s cubic-bezier(0.215, 0.61, 0.355, 1) 0.2s backwards;
24
+ background: #f4f2f3;
25
+ cursor: move;
26
+ }
27
+
28
+ /* line 30, frontend/app/styles/mvcss/modules/_vis.sass */
29
+ .vis.is-new .vis-link-new,
30
+ .vis.is-new .vis-link-text {
31
+ display: none;
32
+ }
33
+
34
+ @keyframes vis {
35
+ from {
36
+ opacity: 0;
37
+ }
38
+ }
39
+
40
+ /* line 52, frontend/app/styles/mvcss/modules/_vis.sass */
41
+ .vis-connect {
42
+ fill: none;
43
+ opacity: 0;
44
+ pointer-events: none;
45
+ stroke: rgba(96, 82, 93, 0.5);
46
+ stroke-linecap: round;
47
+ stroke-width: 2px;
48
+ transition: all 1s cubic-bezier(0.215, 0.61, 0.355, 1);
49
+ }
50
+
51
+ /* line 63, frontend/app/styles/mvcss/modules/_vis.sass */
52
+ .vis-connect.is-visible {
53
+ opacity: 1;
54
+ transform: none;
55
+ }
56
+
57
+ /* line 87, frontend/app/styles/mvcss/modules/_vis.sass */
58
+ .vis-link-new {
59
+ cursor: pointer;
60
+ }
61
+
62
+ /* line 91, frontend/app/styles/mvcss/modules/_vis.sass */
63
+ .vis-link-new:hover circle, .vis-link-new:focus circle {
64
+ fill: #60525d;
65
+ stroke: #60525d;
66
+ stroke-width: 4px;
67
+ }
68
+
69
+ /* line 95, frontend/app/styles/mvcss/modules/_vis.sass */
70
+ .vis-link-new:hover use, .vis-link-new:focus use {
71
+ color: white;
72
+ }
73
+
74
+ /* line 97, frontend/app/styles/mvcss/modules/_vis.sass */
75
+ .vis-link-new circle {
76
+ fill: #f4f2f3;
77
+ stroke: rgba(64, 55, 63, 0.2);
78
+ stroke-width: 2px;
79
+ transition: fill 0.1s cubic-bezier(0.215, 0.61, 0.355, 1), stroke 0.4s cubic-bezier(0.215, 0.61, 0.355, 1), stroke-width 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
80
+ }
81
+
82
+ /* line 102, frontend/app/styles/mvcss/modules/_vis.sass */
83
+ .vis-link-new use {
84
+ color: rgba(64, 55, 63, 0.2);
85
+ transition: color 0.1s cubic-bezier(0.215, 0.61, 0.355, 1);
86
+ }
87
+
88
+ /* line 106, frontend/app/styles/mvcss/modules/_vis.sass */
89
+ .vis-link-new.is-selectable {
90
+ display: block !important;
91
+ }
92
+
93
+ /* line 110, frontend/app/styles/mvcss/modules/_vis.sass */
94
+ .vis-link-new.is-selectable:hover circle, .vis-link-new.is-selectable:focus circle {
95
+ fill: #7193cd;
96
+ stroke: #7193cd;
97
+ }
98
+
99
+ /* line 113, frontend/app/styles/mvcss/modules/_vis.sass */
100
+ .vis-link-new.is-selectable circle {
101
+ animation: vis-link-new 1s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;
102
+ fill: rgba(113, 147, 205, 0.8);
103
+ stroke: #7193cd;
104
+ }
105
+
106
+ /* line 117, frontend/app/styles/mvcss/modules/_vis.sass */
107
+ .vis-link-new.is-selectable use {
108
+ display: none;
109
+ }
110
+
111
+ @keyframes vis-link-new {
112
+ to {
113
+ stroke: rgba(113, 147, 205, 0);
114
+ stroke-width: 8px;
115
+ }
116
+ }
117
+
118
+ /* line 127, frontend/app/styles/mvcss/modules/_vis.sass */
119
+ .vis-link-path-source {
120
+ fill: none;
121
+ stroke-linecap: round;
122
+ stroke-width: 5.5px;
123
+ transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
124
+ }
125
+
126
+ /* line 133, frontend/app/styles/mvcss/modules/_vis.sass */
127
+ .vis-link-path-target {
128
+ fill: none;
129
+ stroke-linecap: round;
130
+ stroke: #959294;
131
+ stroke-width: 5px;
132
+ transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
133
+ }
134
+
135
+ /* line 140, frontend/app/styles/mvcss/modules/_vis.sass */
136
+ .vis-link-path-target[data-color="condition_occurrence"] {
137
+ stroke: #d6758a;
138
+ }
139
+
140
+ /* line 142, frontend/app/styles/mvcss/modules/_vis.sass */
141
+ .vis-link-path-target[data-color="death"] {
142
+ stroke: #a7736f;
143
+ }
144
+
145
+ /* line 144, frontend/app/styles/mvcss/modules/_vis.sass */
146
+ .vis-link-path-target[data-color="drug_exposure"] {
147
+ stroke: #cf92d2;
148
+ }
149
+
150
+ /* line 146, frontend/app/styles/mvcss/modules/_vis.sass */
151
+ .vis-link-path-target[data-color="observation_period"] {
152
+ stroke: #a6dddc;
153
+ }
154
+
155
+ /* line 148, frontend/app/styles/mvcss/modules/_vis.sass */
156
+ .vis-link-path-target[data-color="person"] {
157
+ stroke: #a9c9e7;
158
+ }
159
+
160
+ /* line 150, frontend/app/styles/mvcss/modules/_vis.sass */
161
+ .vis-link-path-target[data-color="placeholder"] {
162
+ stroke: #f6f3f5;
163
+ }
164
+
165
+ /* line 152, frontend/app/styles/mvcss/modules/_vis.sass */
166
+ .vis-link-path-target[data-color="procedure_occurrence"] {
167
+ stroke: #a7e2ac;
168
+ }
169
+
170
+ /* line 154, frontend/app/styles/mvcss/modules/_vis.sass */
171
+ .vis-link-path-target[data-color="visit_occurrence"] {
172
+ stroke: #e8d4b3;
173
+ }
174
+
175
+ /* line 159, frontend/app/styles/mvcss/modules/_vis.sass */
176
+ .vis-link-text {
177
+ dominant-baseline: middle;
178
+ font-size: 86.7%;
179
+ font-weight: bold;
180
+ user-select: none;
181
+ }
182
+
183
+ /* line 169, frontend/app/styles/mvcss/modules/_vis.sass */
184
+ .vis-node {
185
+ cursor: pointer;
186
+ }
187
+
188
+ /* line 174, frontend/app/styles/mvcss/modules/_vis.sass */
189
+ .vis-node:hover .vis-node-warning circle, .vis-node:hover .vis-node-error circle, .vis-node:focus .vis-node-warning circle, .vis-node:focus .vis-node-error circle {
190
+ fill: #833341;
191
+ }
192
+
193
+ /* line 178, frontend/app/styles/mvcss/modules/_vis.sass */
194
+ .vis-node:hover .vis-node-point, .vis-node:focus .vis-node-point {
195
+ color: #60525d !important;
196
+ }
197
+
198
+ /* line 184, frontend/app/styles/mvcss/modules/_vis.sass */
199
+ .vis-node.is-active .vis-node-description-heading {
200
+ fill: #5f6493 !important;
201
+ }
202
+
203
+ /* line 188, frontend/app/styles/mvcss/modules/_vis.sass */
204
+ .vis-node.is-warning .vis-node-warning {
205
+ opacity: 1;
206
+ transform: translate(6px, 34px);
207
+ visibility: visible;
208
+ }
209
+
210
+ /* line 194, frontend/app/styles/mvcss/modules/_vis.sass */
211
+ .vis-node.is-error .vis-node-error {
212
+ opacity: 1;
213
+ transform: translate(6px, 34px);
214
+ visibility: visible;
215
+ }
216
+
217
+ /* line 203, frontend/app/styles/mvcss/modules/_vis.sass */
218
+ .vis-node-description {
219
+ transition: opacity 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
220
+ user-select: none;
221
+ }
222
+
223
+ /* line 206, frontend/app/styles/mvcss/modules/_vis.sass */
224
+ .vis-node-description tspan {
225
+ dominant-baseline: middle;
226
+ }
227
+
228
+ /* line 209, frontend/app/styles/mvcss/modules/_vis.sass */
229
+ .vis-node-description-heading {
230
+ font-weight: bold;
231
+ }
232
+
233
+ /* line 212, frontend/app/styles/mvcss/modules/_vis.sass */
234
+ .vis-node-description-value {
235
+ fill: #817980;
236
+ }
237
+
238
+ /* line 216, frontend/app/styles/mvcss/modules/_vis.sass */
239
+ .is-label .vis-node-description-heading + .vis-node-description-value {
240
+ fill: #afa8ad;
241
+ font-style: italic;
242
+ }
243
+
244
+ /* line 222, frontend/app/styles/mvcss/modules/_vis.sass */
245
+ .vis-node-warning, .vis-node-error {
246
+ color: white;
247
+ opacity: 0;
248
+ transform: scale(0.0001) translate(6px, 34px);
249
+ transform-origin: 6px 34px;
250
+ transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
251
+ visibility: hidden;
252
+ }
253
+
254
+ /* line 229, frontend/app/styles/mvcss/modules/_vis.sass */
255
+ .vis-node-warning circle, .vis-node-error circle {
256
+ fill: #262125;
257
+ stroke: #f4f2f3;
258
+ stroke-width: 3px;
259
+ transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
260
+ }
261
+
262
+ /* line 237, frontend/app/styles/mvcss/modules/_vis.sass */
263
+ .vis-node-icon {
264
+ color: white;
265
+ }
266
+
267
+ /* line 242, frontend/app/styles/mvcss/modules/_vis.sass */
268
+ .vis-node-label {
269
+ color: #afa8ad;
270
+ opacity: 0;
271
+ transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
272
+ visibility: hidden;
273
+ }
274
+
275
+ /* line 249, frontend/app/styles/mvcss/modules/_vis.sass */
276
+ .is-label .vis-node-label {
277
+ opacity: 1;
278
+ visibility: visible;
279
+ }
280
+
281
+ /* line 255, frontend/app/styles/mvcss/modules/_vis.sass */
282
+ .vis-node-point {
283
+ color: #959294;
284
+ transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
285
+ }
286
+
287
+ /* line 259, frontend/app/styles/mvcss/modules/_vis.sass */
288
+ .vis-node-point[data-color="condition_occurrence"] {
289
+ color: #ad4a5b;
290
+ }
291
+
292
+ /* line 261, frontend/app/styles/mvcss/modules/_vis.sass */
293
+ .vis-node-point[data-color="death"] {
294
+ color: #704a4a;
295
+ }
296
+
297
+ /* line 263, frontend/app/styles/mvcss/modules/_vis.sass */
298
+ .vis-node-point[data-color="drug_exposure"] {
299
+ color: #9f57a5;
300
+ }
301
+
302
+ /* line 265, frontend/app/styles/mvcss/modules/_vis.sass */
303
+ .vis-node-point[data-color="observation_period"] {
304
+ color: #6fbbba;
305
+ }
306
+
307
+ /* line 267, frontend/app/styles/mvcss/modules/_vis.sass */
308
+ .vis-node-point[data-color="person"] {
309
+ color: #7193cd;
310
+ }
311
+
312
+ /* line 269, frontend/app/styles/mvcss/modules/_vis.sass */
313
+ .vis-node-point[data-color="placeholder"] {
314
+ color: #f6f3f5;
315
+ }
316
+
317
+ /* line 271, frontend/app/styles/mvcss/modules/_vis.sass */
318
+ .vis-node-point[data-color="procedure_occurrence"] {
319
+ color: #90d796;
320
+ }
321
+
322
+ /* line 273, frontend/app/styles/mvcss/modules/_vis.sass */
323
+ .vis-node-point[data-color="visit_occurrence"] {
324
+ color: #d1a776;
325
+ }
326
+
327
+ /* line 276, frontend/app/styles/mvcss/modules/_vis.sass */
328
+ .vis-node-point-base {
329
+ stroke: #f4f2f3;
330
+ stroke-width: 3px;
331
+ }
@@ -0,0 +1,93 @@
1
+ Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ https://openfontlicense.org
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file
Binary file