-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/demo/src/ccp.css b/packages/demo/src/ccp.css
index 17bcbad3..6bbe58ab 100644
--- a/packages/demo/src/ccp.css
+++ b/packages/demo/src/ccp.css
@@ -275,6 +275,12 @@ lens-search-button::part(lens-search-button) {
background-color: var(--light-blue);
}
+lens-search-button::part(lens-search-button):hover {background-color: #006adb}
+
+lens-search-button::part(lens-search-button):active {
+ background-color: #003d7e;
+ transform: translateX(1px);
+}
/**
Catalogue
@@ -372,4 +378,24 @@ lens-info-button::part(info-button-icon) {
.catalogue lens-info-button::part(info-button-dialogue){
max-width: 300px;
text-align: left;
+}
+
+lens-info-button::part(info-button-dialogue):hover {background-color: #b8bfb8}
+
+lens-info-button::part(info-button):hover {background-color: #b8bfb8}
+
+lens-info-button::part(info-button):active {
+ background-color: #585958;
+ transform: translateX(1px);
+}
+
+lens-info-button::part(info-button-dialogue):active {
+ background-color: #585958;
+ transform: translateX(1px);
+}
+
+.result-table-hint-text {
+ padding-top: 20px;
+ display: flex;
+ align-items: end;
}
\ No newline at end of file
diff --git a/packages/demo/src/main.ts b/packages/demo/src/main.ts
index 226be0ab..1548c4d2 100644
--- a/packages/demo/src/main.ts
+++ b/packages/demo/src/main.ts
@@ -1,8 +1,8 @@
-// import './fragment-development.css'
-// import App from './AppFragmentDevelopment.svelte'
+// import "./fragment-development.css";
+// import App from "./AppFragmentDevelopment.svelte";
-import './ccp.css'
-import App from './AppCCP.svelte'
+import "./ccp.css";
+import App from "./AppCCP.svelte";
// import App from './AppBBMRI.svelte'
// import './bbmri.css'
@@ -10,9 +10,8 @@ import App from './AppCCP.svelte'
// import App from "./AppGBA.svelte"
// import './gba.css'
-
const app = new App({
- target: document.getElementById('app')
-})
+ target: document.getElementById("app"),
+});
-export default app
+export default app;
diff --git a/packages/demo/src/measures.ts b/packages/demo/src/measures.ts
index 4791be30..cb24a233 100644
--- a/packages/demo/src/measures.ts
+++ b/packages/demo/src/measures.ts
@@ -832,9 +832,11 @@ export const dktkPatientsMeasure = {
],
},
cql: `
+
DKTK_STRAT_GENDER_STRATIFIER
-DKTK_STRAT_AGE_STRATIFIER
+DKTK_STRAT_PRIMARY_DIAGNOSIS_STRATIFIER
+DKTK_STRAT_AGE_CLASS_STRATIFIER
DKTK_STRAT_DECEASED_STRATIFIER
`,
@@ -1023,3 +1025,49 @@ export const dktkMedicationStatementsMeasure = {
DKTK_STRAT_MEDICATION_STRATIFIER
`,
};
+
+export const dktkHistologyMeasure = {
+ key: 'Histo',
+ measure: {
+ code: {
+ text: 'Histo',
+ },
+ extension: [
+ {
+ url: 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis',
+ valueCode: 'Observation',
+ },
+ ],
+ population: [
+ {
+ code: {
+ coding: [
+ {
+ system:
+ 'http://terminology.hl7.org/CodeSystem/measure-population',
+ code: 'initial-population',
+ },
+ ],
+ },
+ criteria: {
+ language: 'text/cql-identifier',
+ expression: 'Histo',
+ },
+ },
+ ],
+ stratifier: [
+ {
+ code: {
+ text: 'Histlogoies',
+ },
+ criteria: {
+ language: 'text/cql-identifier',
+ expression: 'Histlogoy',
+ },
+ },
+ ],
+ },
+ cql: `
+ DKTK_STRAT_HISTOLOGY_STRATIFIER
+`,
+};
diff --git a/packages/demo/src/styles/default/result-table.css b/packages/demo/src/styles/default/result-table.css
index dffbc82c..511b713f 100644
--- a/packages/demo/src/styles/default/result-table.css
+++ b/packages/demo/src/styles/default/result-table.css
@@ -2,7 +2,7 @@
lens-result-table {
background-color: var(--white);
display: grid;
- grid-template-rows: auto auto 1fr;
+ grid-template-rows: auto auto 1fr auto auto;
}
lens-result-table::part(result-table-title) {
@@ -54,10 +54,20 @@ lens-result-table {
}
lens-result-table::part(table-pagination-button) {
- border: solid 1px var(--gray);
+ border: solid 1px var(--blue);
background: var(--white);
border-radius: var(--border-radius-small);
padding: var(--gap-xxs) var(--gap-s);
+ cursor: pointer;
+ }
+
+
+ lens-result-table::part(pagination-button-disabled) {
+ border: solid 1px var(--light-gray);
+ background: var(--white);
+ border-radius: var(--border-radius-small);
+ padding: var(--gap-xxs) var(--gap-s);
+ cursor: auto;
}
lens-result-table::part(info-button) {
diff --git a/packages/demo/src/styles/default/results-overview.css b/packages/demo/src/styles/default/results-overview.css
index 8b8b3cfd..9b99b574 100644
--- a/packages/demo/src/styles/default/results-overview.css
+++ b/packages/demo/src/styles/default/results-overview.css
@@ -16,4 +16,34 @@ lens-result-summary {
align-items: center;
justify-content: space-around;
gap: 10px
- }
\ No newline at end of file
+ }
+
+ lens-result-summary::part(info-button) {
+ position: relative;
+ cursor: pointer;
+ height: 100%;
+ border: none;
+ background-color: var(--white);
+ border-radius: var(--border-radius-small);
+}
+ lens-result-summary::part(info-button-icon) {
+ width: 16px;
+}
+
+lens-result-summary::part(info-button-title) {
+ font-family: var(--font-family);
+}
+
+lens-result-summary::part(info-button-dialogue) {
+ position: absolute;
+ border: none;
+ background-color: var(--white);
+ width: max-content;
+ max-width: 30vw;
+ z-index: 100;
+ padding: var(--gap-s);
+ top: 40px;
+ left: 0px;
+ border: solid 1px var(--light-blue);
+ border-radius: var(--border-radius-small);
+}
diff --git a/packages/lib/index.ts b/packages/lib/index.ts
index eb632858..97269fe9 100644
--- a/packages/lib/index.ts
+++ b/packages/lib/index.ts
@@ -1,13 +1,13 @@
-export { default as Catalogue } from './src/components/catalogue/Catalogue.wc.svelte';
-export { default as QueryDisplayComponent } from './src/components/query-display/QueryDisplayComponent.wc.svelte';
-export { default as SearchBarComponent } from './src/components/search-bar/SearchBarComponent.wc.svelte';
-export { default as StateDisplayComponent } from './src/components/testing-components/StateDisplayComponent.wc.svelte';
-export { default as SearchButtonComponent } from './src/components/buttons/SearchButtonComponenet.wc.svelte'
-export { default as ChartComponent } from './src/components/results/ChartComponent.wc.svelte'
-export { default as ResutSummaryComponent } from './src/components/results/ResultSummaryComponent.wc.svelte'
-export { default as ResultTableComponent } from './src/components/results/ResultTableComponent.wc.svelte'
-export { default as SearchBarMultipleComponent } from './src/components/search-bar/SearchBarMultipleComponent.wc.svelte'
-export { default as NegotiateButtonComponent } from './src/components/buttons/NegotiateButtonComponent.wc.svelte'
-export { default as InfoButton } from './src/components/buttons/InfoButtonComponent.wc.svelte'
-export { default as lensOptions } from './src/components/Options.wc.svelte'
-export { default as ModifiedSearchComponent } from './src/components/informational/ModifiedSearchComponent.wc.svelte'
\ No newline at end of file
+export { default as Catalogue } from "./src/components/catalogue/Catalogue.wc.svelte";
+export { default as SearchBarComponent } from "./src/components/search-bar/SearchBarComponent.wc.svelte";
+export { default as StateDisplayComponent } from "./src/components/testing-components/StateDisplayComponent.wc.svelte";
+export { default as SearchButtonComponent } from "./src/components/buttons/SearchButtonComponenet.wc.svelte";
+export { default as ChartComponent } from "./src/components/results/ChartComponent.wc.svelte";
+export { default as ResutSummaryComponent } from "./src/components/results/ResultSummaryComponent.wc.svelte";
+export { default as ResultTableComponent } from "./src/components/results/ResultTableComponent.wc.svelte";
+export { default as SearchBarMultipleComponent } from "./src/components/search-bar/SearchBarMultipleComponent.wc.svelte";
+export { default as NegotiateButtonComponent } from "./src/components/buttons/NegotiateButtonComponent.wc.svelte";
+export { default as InfoButton } from "./src/components/buttons/InfoButtonComponent.wc.svelte";
+export { default as lensOptions } from "./src/components/Options.wc.svelte";
+export { default as DataPasser } from "./src/components/DataPasser.wc.svelte";
+export { default as ModifiedSearchComponent } from "./src/components/informational/ModifiedSearchComponent.wc.svelte";
diff --git a/packages/lib/src/classes/blaze.ts b/packages/lib/src/classes/blaze.ts
index befde59b..a475acd7 100644
--- a/packages/lib/src/classes/blaze.ts
+++ b/packages/lib/src/classes/blaze.ts
@@ -3,84 +3,107 @@ import { responseStore } from "../stores/response";
import type { Site } from "../types/response";
import { measureStore } from "../stores/measures";
-let measureDefinitions
+let measureDefinitions;
-measureStore.subscribe(store => {
- measureDefinitions = store.map((measure) => measure.measure)
-})
+measureStore.subscribe((store) => {
+ measureDefinitions = store.map((measure) => measure.measure);
+});
export class Blaze {
-
constructor(
private url: URL,
private name: string,
private auth: string = "",
- ) {
- }
+ ) {}
- async send(cql: string, controller?: AbortController) {
+ /**
+ * sends the query to beam and updates the store with the results
+ * @param cql the query as cql string
+ * @param controller the abort controller to cancel the request
+ */
+ async send(cql: string, controller?: AbortController): Promise
{
try {
responseStore.update((store) => {
store.set(this.name, { status: "claimed", data: null });
return store;
});
- let libraryResponse = await fetch(
- new URL(`${this.url}/Library`), {
- method: "POST",
- headers: {
- "Content-Type": "application/json"
+ const libraryResponse = await fetch(
+ new URL(`${this.url}/Library`),
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(buildLibrary(cql)),
+ signal: controller?.signal,
},
- body: JSON.stringify(buildLibrary(cql)),
- signal: controller.signal
- }
- )
+ );
if (!libraryResponse.ok) {
- this.handleError(`Couldn't create Library in Blaze`, libraryResponse);
+ this.handleError(
+ `Couldn't create Library in Blaze`,
+ libraryResponse,
+ );
}
const library = await libraryResponse.json();
const measureResponse = await fetch(
- new URL(`${this.url}/Measure`), {
- method: "POST",
- headers: {
- "Content-Type": "application/json"
+ new URL(`${this.url}/Measure`),
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(
+ buildMeasure(library.url, measureDefinitions),
+ ),
+ signal: controller.signal,
},
- body: JSON.stringify(buildMeasure(library.url, measureDefinitions)),
- signal: controller.signal
- })
+ );
if (!measureResponse.ok) {
- this.handleError(`Couldn't create Measure in Blaze`, measureResponse)
+ this.handleError(
+ `Couldn't create Measure in Blaze`,
+ measureResponse,
+ );
}
const measure = await measureResponse.json();
const dataResponse = await fetch(
- new URL(`${this.url}/Measure/$evaluate-measure?measure=${measure.url}&periodStart=2000&periodEnd=2030`),
+ new URL(
+ `${this.url}/Measure/$evaluate-measure?measure=${measure.url}&periodStart=2000&periodEnd=2030`,
+ ),
{
- signal: controller.signal
- }
- )
+ signal: controller.signal,
+ },
+ );
if (!dataResponse.ok) {
- this.handleError(`Couldn't evaluate Measure in Blaze`, dataResponse)
+ this.handleError(
+ `Couldn't evaluate Measure in Blaze`,
+ dataResponse,
+ );
}
- const blazeResponse: Site = await dataResponse.json()
+ const blazeResponse: Site = await dataResponse.json();
responseStore.update((store) => {
- store.set(this.name, { status: 'succeeded', data: blazeResponse })
+ store.set(this.name, {
+ status: "succeeded",
+ data: blazeResponse,
+ });
return store;
- })
+ });
} catch (err) {
if (err.name === "AbortError") {
- console.log(`Aborting former blaze request.`)
+ console.log(`Aborting former blaze request.`);
} else {
- console.error(err)
+ console.error(err);
}
}
}
- async handleError(message: string, response: Response) {
- const errorMessage = await response.text()
- console.debug(`${message}. Received error ${response.status} with message ${errorMessage}`)
+ async handleError(message: string, response: Response): Promise {
+ const errorMessage = await response.text();
+ console.debug(
+ `${message}. Received error ${response.status} with message ${errorMessage}`,
+ );
responseStore.update((store) => {
- store.set(this.name, {status: 'permfailed', data: null})
+ store.set(this.name, { status: "permfailed", data: null });
return store;
- })
+ });
}
-
}
diff --git a/packages/lib/src/classes/spot.ts b/packages/lib/src/classes/spot.ts
index 30edefa7..f4725106 100644
--- a/packages/lib/src/classes/spot.ts
+++ b/packages/lib/src/classes/spot.ts
@@ -2,77 +2,91 @@
* TODO: document this class
*/
-import { responseStore } from "../stores/response"
+import { responseStore } from "../stores/response";
import type { ResponseStore } from "../types/backend";
-import type { Site, SiteData, Status } from "../types/response"
+import type { Site, SiteData, Status } from "../types/response";
type BeamResult = {
- body: string,
- from: string,
- metadata: string,
- status: Status,
- task: string,
- to: string[]
-}
+ body: string;
+ from: string;
+ metadata: string;
+ status: Status;
+ task: string;
+ to: string[];
+};
export class Spot {
-
- private storeCache: ResponseStore;
- private currentTask: string;
+ private storeCache!: ResponseStore;
+ private currentTask!: string;
constructor(
private url: URL,
private sites: Array,
) {
- responseStore.subscribe(store => this.storeCache = store)
+ responseStore.subscribe(
+ (store: ResponseStore) => (this.storeCache = store),
+ );
}
- async send(query: string, controller?: AbortController) {
+ /**
+ * sends the query to beam and updates the store with the results
+ * @param query the query as base64 encoded string
+ * @param controller the abort controller to cancel the request
+ */
+ async send(query: string, controller?: AbortController): Promise {
try {
const beamTaskResponse = await fetch(
`${this.url}tasks?sites=${this.sites.toString()}`,
{
- method: 'POST',
- credentials: (import.meta.env.PROD) ? "include" : "omit",
+ method: "POST",
+ credentials: import.meta.env.PROD ? "include" : "omit",
body: query,
- signal: controller.signal
- }
- )
+ signal: controller?.signal,
+ },
+ );
if (!beamTaskResponse.ok) {
- const error = await beamTaskResponse.text()
- console.debug(`Received ${beamTaskResponse.status} with message ${error}`)
- throw new Error(`Unable to create new beam task.`)
+ const error = await beamTaskResponse.text();
+ console.debug(
+ `Received ${beamTaskResponse.status} with message ${error}`,
+ );
+ throw new Error(`Unable to create new beam task.`);
}
this.currentTask = (await beamTaskResponse.json()).id;
- let responseCount: number = 0
- let continueRequests: boolean = false;
+ let responseCount: number = 0;
do {
-
const beamResponses: Response = await fetch(
`${this.url}tasks/${this.currentTask}?wait_count=${responseCount + 1}`,
{
- credentials: (import.meta.env.PROD) ? "include" : "omit",
- signal: controller.signal
- }
- )
+ credentials: import.meta.env.PROD ? "include" : "omit",
+ signal: controller?.signal,
+ },
+ );
if (!beamResponses.ok) {
- const error: string = await beamResponses.text()
- console.debug(`Received ${beamResponses.status} with message ${error}`)
- throw new Error(`Error then retrieving responses from Beam. Abborting requests ...`)
+ const error: string = await beamResponses.text();
+ console.debug(
+ `Received ${beamResponses.status} with message ${error}`,
+ );
+ throw new Error(
+ `Error then retrieving responses from Beam. Abborting requests ...`,
+ );
}
- const beamResponseData: Array = await beamResponses.json();
+ const beamResponseData: Array =
+ await beamResponses.json();
- let changes = new Map();
+ const changes = new Map();
beamResponseData.forEach((response: BeamResult) => {
- if (response.task !== this.currentTask) return
- let site: string = response.from.split(".")[1]
- let status: Status = response.status
- let body: SiteData = (status === "succeeded") ? JSON.parse(atob(response.body)) : null;
+ if (response.task !== this.currentTask) return;
+ const site: string = response.from.split(".")[1];
+ const status: Status = response.status;
+ const body: SiteData =
+ status === "succeeded"
+ ? JSON.parse(atob(response.body))
+ : null;
// if the site is already in the store and the status is claimed, don't update the store
if (this.storeCache.get(site)?.status === status) return;
@@ -80,35 +94,35 @@ export class Spot {
changes.set(site, { status: status, data: body });
});
if (changes.size > 0) {
- responseStore.update((store: ResponseStore): ResponseStore => {
- changes.forEach((value, key) => {
- store.set(key, value)
- })
- return store;
- })
+ responseStore.update(
+ (store: ResponseStore): ResponseStore => {
+ changes.forEach((value, key) => {
+ store.set(key, value);
+ });
+ return store;
+ },
+ );
}
responseCount = beamResponseData.length;
- let realResponseCount = beamResponseData.filter(response => response.status !== "claimed").length;
+ const realResponseCount = beamResponseData.filter(
+ (response) => response.status !== "claimed",
+ ).length;
if (
- (beamResponses.status === 200 || beamResponses.status === 206)
- && realResponseCount !== this.sites.length
+ (beamResponses.status !== 200 &&
+ beamResponses.status !== 206) ||
+ realResponseCount === this.sites.length
) {
- continueRequests = true;
- } else {
- continueRequests = false;
break;
}
-
- } while (true)
+ } while (true);
} catch (err) {
- if (err.name === "AbortError") {
- console.log(`Aborting request ${this.currentTask}`)
+ if (err instanceof Error && err.name === "AbortError") {
+ console.log(`Aborting request ${this.currentTask}`);
} else {
- console.error(err)
+ console.error(err);
}
}
}
-
}
diff --git a/packages/lib/src/components/DataPasser.wc.svelte b/packages/lib/src/components/DataPasser.wc.svelte
new file mode 100644
index 00000000..5d9676a4
--- /dev/null
+++ b/packages/lib/src/components/DataPasser.wc.svelte
@@ -0,0 +1,116 @@
+
+
+
+
diff --git a/packages/lib/src/components/Options.wc.svelte b/packages/lib/src/components/Options.wc.svelte
index 0181c203..a3db079a 100644
--- a/packages/lib/src/components/Options.wc.svelte
+++ b/packages/lib/src/components/Options.wc.svelte
@@ -2,7 +2,7 @@
customElement={{
tag: "lens-options",
props: {
- options: {type: 'Object'},
+ options: { type: "Object" },
catalogueData: { type: "Object" },
},
}}
@@ -12,15 +12,14 @@
/**
* this component takes the catalogue and all options set from the project and passes them to the appropriate store
* TODO: refactor all mappings and configurations to be passed in here
- */
- import {lensOptions} from "../stores/options";
+ */
+ import { lensOptions } from "../stores/options";
import { catalogue } from "../stores/catalogue";
import type { Criteria } from "../types/treeData";
export let options: object = {};
export let catalogueData: Criteria[] = [];
-
+
$: $lensOptions = options;
$: $catalogue = catalogueData;
-
-
\ No newline at end of file
+
diff --git a/packages/lib/src/components/buttons/InfoButtonComponent.wc.svelte b/packages/lib/src/components/buttons/InfoButtonComponent.wc.svelte
index 98bd0358..c6d21602 100644
--- a/packages/lib/src/components/buttons/InfoButtonComponent.wc.svelte
+++ b/packages/lib/src/components/buttons/InfoButtonComponent.wc.svelte
@@ -2,7 +2,7 @@
customElement={{
tag: "lens-info-button",
props: {
- showQuery: {type: "Boolean"},
+ showQuery: { type: "Boolean" },
},
}}
/>
@@ -10,14 +10,14 @@