Skip to content

Commit fac75f2

Browse files
committed
Added further documentation for connectionManager, executeCql, and normalizeCqlExecution
1 parent 5632f68 commit fac75f2

3 files changed

Lines changed: 273 additions & 23 deletions

File tree

src/connectionManager.ts

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,127 @@ export interface Connection {
1717
}
1818

1919
/**
20-
* Manages connections and their associated contexts.
20+
* Manages multiple FHIR data connections and their associated contexts for CQL evaluations.
21+
*
22+
* The **Connection Manager** allows for managing multiple data connections, but only one connection can be active at a time.
23+
* It currently supports FHIR server connections and local file system connections. It provides full CRUD capabilities and
24+
* ensures that connections and contexts persist across sessions using VS Code's global state storage.
25+
*
26+
*
27+
* **Quick Example:**
28+
*
29+
* - **Creating a New Connection**:
30+
* ```typescript
31+
* const connection: Connection = {
32+
* name: "Local Connection",
33+
* endpoint: "file:///Users/username/test-dir",
34+
* contexts: {
35+
* "Patient/123": {
36+
* resourceID: "123",
37+
* resourceType: "Patient",
38+
* resourceDisplay: "John Doe"
39+
* }
40+
* }
41+
* };
42+
* ConnectionManager.getManager().upsertConnection(connection);
43+
* ```
44+
*
45+
*
46+
* - **Setting the Active Connection**:
47+
* ```typescript
48+
* ConnectionManager.getManager().setCurrentConnection("Local Connection");
49+
* ```
50+
*
51+
* **Key Operations:**
52+
*
53+
* - **Read**: Retrieve all stored connections and their associated contexts.
54+
* - **Upsert**: Add or update connections and contexts.
55+
* - **Delete**: Remove connections and contexts.
56+
*
57+
* **Further Examples:**
58+
*
59+
* - **Retrieving the Current Active Connection**:
60+
* ```typescript
61+
* const currentConnection = ConnectionManager.getManager().getCurrentConnection();
62+
* console.log(currentConnection);
63+
* ```
64+
*
65+
* - **Deleting a Connection**:
66+
* ```typescript
67+
* ConnectionManager.getManager().deleteConnection("Remote Connection");
68+
* ```
69+
*
70+
* **CRUD Operations:**
71+
*
72+
* - **Create or Update a Connection**:
73+
* Use `upsertConnection` to add a new connection or update an existing one.
74+
* ```typescript
75+
* ConnectionManager.getManager().upsertConnection(connection);
76+
* ```
77+
*
78+
* - **Read Connections**:
79+
* Retrieve all connections or get the current active connection.
80+
* ```typescript
81+
* const allConnections = ConnectionManager.getManager().getAllConnections();
82+
* const currentConnection = ConnectionManager.getManager().getCurrentConnection();
83+
* ```
84+
*
85+
* - **Delete a Connection**:
86+
* Remove a connection by name.
87+
* ```typescript
88+
* ConnectionManager.getManager().deleteConnection("Local Connection");
89+
* ```
90+
*
91+
* - **Set the Current Active Connection**:
92+
* Choose which connection will be the active one for CQL evaluations.
93+
* ```typescript
94+
* ConnectionManager.getManager().setCurrentConnection("Remote Connection");
95+
* ```
96+
*
97+
* **Context Management**:
98+
*
99+
* - **Adding or Updating a Context**:
100+
* Add or update a context within a specific connection. Contexts represent specific resources used in CQL evaluations (e.g., Patients).
101+
* ```typescript
102+
* const patientContext: Context = { resourceID: "123", resourceType: "Patient", resourceDisplay: "John Doe" };
103+
* ConnectionManager.getManager().upsertContext("Local Connection", patientContext);
104+
* ```
105+
*
106+
* - **Retrieving Contexts for the Current Connection**:
107+
* ```typescript
108+
* const contexts = ConnectionManager.getManager().getCurrentContexts();
109+
* console.log(contexts);
110+
* ```
111+
*
112+
* - **Deleting a Context**:
113+
* ```typescript
114+
* ConnectionManager.getManager().deleteContext("Local Connection", "Patient/123");
115+
* ```
116+
*
117+
* **Important Notes:**
118+
*
119+
* - **Context Parameters**:
120+
* Contexts represent specific resource types such as Patients, Encounters, or Organizations. Each connection may have multiple contexts,
121+
* but currently, only "Patient" is supported. Future updates will expand this to include other resource types.
122+
*
123+
* - **Single Active Connection**:
124+
* Only one connection can be active at a time. Switching between connections is managed by the `setCurrentConnection` method.
125+
*
126+
* **Considerations and Limitations:**
127+
*
128+
* - **FHIR-Only**: The connection manager is currently limited to managing FHIR-based data connections.
129+
* - **Single Active Connection**: Only one connection may be active at a time for CQL evaluations.
130+
* - **Context Limitations**: Only the "Patient" context parameter is currently supported. Other contexts like "Encounters" or "Organizations"
131+
* are expected to be supported in future updates.
132+
*
133+
* **Future Enhancements:**
134+
*
135+
* - **Additional Context Support**: Expand support to include other context types, such as Encounters and Organizations.
136+
* - **Terminology Repositories**: Add support for managing connections to external terminology repositories.
137+
* - **Artifact Repositories**: Include support for managing repositories of CQL artifacts (e.g., Libraries, PlanDefinitions, Measures).
138+
* - **Multiple Active Connections**: Enable the possibility of managing and utilizing multiple active connections for more complex workflows.
139+
*
140+
* @param {ExtensionContext} ec - The extension context used to initialize the ConnectionManager.
21141
*/
22142
export class ConnectionManager {
23143
private static connectionManager: ConnectionManager;

src/executeCql.ts

Lines changed: 93 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,74 @@
1-
/**
2-
* This is a stub description for now.
3-
*/
41
import * as fs from 'fs';
52
import { Position, TextEditor, Uri, commands, window, workspace } from 'vscode';
63
import { EvaluationParameters } from './buildParameters';
74
import { Commands } from './commands';
85

96
/**
10-
* Inserts a line of text at the end of the document in the given text editor.
11-
* @param {TextEditor} textEditor - The text editor where the text should be inserted.
12-
* @param {string} text - The text to insert.
13-
*/
14-
async function insertLineAtEnd(textEditor: TextEditor, text: string) {
15-
const document = textEditor.document;
16-
await textEditor.edit(editBuilder => {
17-
editBuilder.insert(new Position(textEditor.document.lineCount, 0), text + '\n');
18-
});
19-
}
20-
21-
/**
22-
* Executes CQL (Clinical Quality Language) based on the provided evaluation parameters.
23-
* Outputs results to a text document specified by the evaluation parameters.
24-
* @param {EvaluationParameters} evaluationParams - The parameters required for executing CQL.
7+
* Executes Clinical Quality Language (CQL) operations based on the provided parameters.
8+
*
9+
* This function manages the execution of CQL commands by sending operation parameters to the CQL Language Server
10+
* and capturing the results in a text file. The output includes messages that describe the execution environment
11+
* (CQL file location, terminology, test cases, and context values), along with the results for each context.
12+
*
13+
* **Quick Example:**
14+
*
15+
* - **Executing CQL with Parameters**:
16+
* ```typescript
17+
* const evaluationParams: EvaluationParameters = {
18+
* operationArgs: ['-lu=/path/to/cql', '-t=/path/to/terminology', '-mu=/path/to/test'],
19+
* outputPath: Uri.parse("file:///path/to/output.txt"),
20+
* testPath: Uri.parse("file:///path/to/test")
21+
* };
22+
*
23+
* await executeCQL(evaluationParams);
24+
* // Outputs the execution result to the specified output file.
25+
* ```
26+
*
27+
* **Purpose:**
28+
*
29+
* - **CQL Execution**: Interacts with the CQL Language Server to execute a CQL library or expression.
30+
* - **Results Logging**: Manages and formats the execution output into a text file, displaying key context-related data and results.
31+
* - **Timing**: Logs the elapsed time of the execution for performance analysis.
32+
*
33+
* **Execution Breakdown:**
34+
*
35+
* 1. **CQL Message**: Captures the CQL file's location using the `-lu=` argument.
36+
* 2. **Terminology Message**: Captures terminology data location using the `-t=` argument.
37+
* 3. **Test Data and Contexts**: Logs the test cases and context values found in the provided data.
38+
*
39+
* **Example Output in the Resulting Text File:**
40+
*
41+
* ```
42+
* CQL: path/to/cql
43+
* Terminology: /path/to/terminology
44+
* Test cases:
45+
* path/to/test - 123
46+
* path/to/test - 456
47+
*
48+
* Patient 123:
49+
* ... [execution result] ...
50+
* Patient 456:
51+
* ... [execution result] ...
52+
* elapsed: 3.45 seconds
53+
* ```
54+
*
55+
* **Functionality:**
56+
*
57+
* - Executes the CQL using `Commands.EXECUTE_WORKSPACE_COMMAND` and `Commands.EXECUTE_CQL`.
58+
* - Inserts important messages and results into the output text file.
59+
* - Interleaves context-related information with the results, enhancing readability.
60+
*
61+
* **Future Enhancements:**
62+
*
63+
* - Improve result formatting for better readability (e.g., JSON or other structured formats).
64+
* - Add support for more complex result handling, such as different output file formats (e.g., CSV, XML).
65+
*
66+
* **Considerations:**
67+
*
68+
* - **Context-Related Execution**: The function supports multiple contexts and interleaves their corresponding results.
69+
* - **File Output**: Currently, results are only saved in a text file format.
70+
*
71+
* @param {EvaluationParameters} evaluationParams - The parameters required for executing CQL. See {@link EvaluationParameters}.
2572
*/
2673
export async function executeCQL({ operationArgs, testPath, outputPath }: EvaluationParameters) {
2774
let cqlMessage = '';
@@ -30,6 +77,7 @@ export async function executeCQL({ operationArgs, testPath, outputPath }: Evalua
3077
let foundTest = false;
3178
const contextValues: string[] = [];
3279

80+
// Loop through the operation arguments to build messages and find test data
3381
for (let i = 0; i < operationArgs?.length!; i++) {
3482
if (operationArgs![i].startsWith('-lu=')) {
3583
cqlMessage = `CQL: ${operationArgs![i].substring(4)}`;
@@ -49,17 +97,20 @@ export async function executeCQL({ operationArgs, testPath, outputPath }: Evalua
4997
}
5098
}
5199

100+
// Handle the case when no test data is found
52101
if (!foundTest) {
53102
testMessage = `No data found at path ${testPath!}. Evaluation may fail if data is required.`;
54103
}
55104

105+
// Open the output file and start inserting information
56106
const textDocument = await workspace.openTextDocument(outputPath!);
57107
const textEditor = await window.showTextDocument(textDocument);
58108

59109
await insertLineAtEnd(textEditor, `${cqlMessage}`);
60110
await insertLineAtEnd(textEditor, `${terminologyMessage}`);
61111
await insertLineAtEnd(textEditor, `${testMessage}`);
62112

113+
// Start timing the execution
63114
const startExecution = Date.now();
64115
const result: string | undefined = await commands.executeCommand(
65116
Commands.EXECUTE_WORKSPACE_COMMAND,
@@ -68,9 +119,11 @@ export async function executeCQL({ operationArgs, testPath, outputPath }: Evalua
68119
);
69120
const endExecution = Date.now();
70121

122+
// Interleave results with context
71123
const expression = operationArgs?.find(arg => arg.startsWith('-e='))?.replace('-e=', '');
72124
await insertLineAtEnd(textEditor, attemptInterleave(expression, contextValues, result || ''));
73125

126+
// Log the execution time
74127
await insertLineAtEnd(
75128
textEditor,
76129
`elapsed: ${((endExecution - startExecution) / 1000).toString()} seconds`,
@@ -80,6 +133,16 @@ export async function executeCQL({ operationArgs, testPath, outputPath }: Evalua
80133
/**
81134
* Attempts to interleave test case names with result expressions for better readability.
82135
* Returns the result unmodified if unable to interleave.
136+
*
137+
* **Example of Interleaving Output**:
138+
*
139+
* ```
140+
* Patient: 123
141+
* ExpressionResult: ...
142+
* Patient: 456
143+
* ExpressionResult: ...
144+
* ```
145+
*
83146
* @param {string | undefined} expression - The CQL expression being evaluated.
84147
* @param {string[]} contextValues - The context values to interleave with the results.
85148
* @param {string} result - The result string to be interleaved.
@@ -115,3 +178,15 @@ const attemptInterleave = (
115178
return result;
116179
}
117180
};
181+
182+
/**
183+
* Inserts a line of text at the end of the document in the given text editor.
184+
* @param {TextEditor} textEditor - The text editor where the text should be inserted.
185+
* @param {string} text - The text to insert.
186+
*/
187+
async function insertLineAtEnd(textEditor: TextEditor, text: string) {
188+
const document = textEditor.document;
189+
await textEditor.edit(editBuilder => {
190+
editBuilder.insert(new Position(textEditor.document.lineCount, 0), text + '\n');
191+
});
192+
}

src/normalizeCqlExecution.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,72 @@
1-
/**
2-
* This is a stub description for now.
3-
*/
41
import { Uri, window } from 'vscode';
52
import { buildParameters } from './buildParameters';
63
import { executeCQL } from './executeCql';
74

85
/**
9-
* Normalizes the execution of CQL based on the type of operation ('file' or 'expression').
6+
* Normalizes the execution of CQL operations based on the type of operation ('file' or 'expression').
7+
*
8+
* This function serves as a **Gatekeeper** by validating the workspace environment and **Normalizing** the data
9+
* required for execution. It takes VS Code-specific details, determines the type of operation to be executed
10+
* (such as [$cql](https://build.fhir.org/ig/HL7/cql-ig/OperationDefinition-cql-cql.html), [$evaluate-measure](https://hl7.org/fhir/R4/measure-operation-evaluate-measure.html), [$apply](https://hl7.org/fhir/R4/plandefinition-operation-apply.html) for PlanDefinitions, or [$evaluate](https://build.fhir.org/ig/HL7/cql-ig/OperationDefinition-cql-library-evaluate.html) for Libraries), and standardizes
11+
* this data into strings or URIs. These are then passed on to {@link buildParameters} for the construction of the
12+
* necessary execution parameters, which are finally handed off to {@link executeCQL} to perform the execution.
13+
*
14+
* **Purpose:**
15+
*
16+
* - **Gatekeeper**: Validates the current state, ensuring the correct file type (CQL) and operation type (file or expression).
17+
* - **Normalizer**: Takes workspace-specific details and standardizes them into a format suitable for execution.
18+
* - **Executor**: Delegates the actual execution to the appropriate functions after normalizing the input data.
19+
*
20+
* **Quick Examples:**
21+
*
22+
* - **Executing a Full CQL File**:
23+
* ```typescript
24+
* const uri = URI.parse("file:///Users/developer/vscode-project/input/cql/my-library.cql");
25+
* await normalizeCqlExecution(uri, 'file');
26+
* // Executes the entire CQL file specified by the URI.
27+
* ```
28+
*
29+
* - **Executing a Single CQL Expression**:
30+
* ```typescript
31+
* const uri = URI.parse("file:///Users/developer/vscode-project/input/cql/my-library.cql");
32+
* await normalizeCqlExecution(uri, 'expression');
33+
* // Executes a single expression based on the current cursor position in the active editor.
34+
* ```
35+
*
36+
* **Dependencies:**
37+
*
38+
* - **Active Text Editor**: The function depends on the active text editor being open with the CQL file.
39+
* - **CQL File Requirement**: The function currently only supports CQL files. In the future, support for Measure, PlanDefinition, and Questionnaire files may be added.
40+
* - **Helper Functions**:
41+
* - **`{@link buildParameters}(uri, expression)`**: Collects all parameters required for execution.
42+
* - **`{@link executeCQL}(operationArgs)`**: Handles the actual execution of the CQL.
43+
*
44+
* **Considerations and Limitations:**
45+
*
46+
* - **Expression Execution**:
47+
* - The function relies on the presence of a valid CQL `define` statement on the current cursor line when executing a single expression.
48+
* - If no valid definition is found, an error message is displayed, and execution is halted.
49+
*
50+
* - **File Type Limitation**:
51+
* - Currently limited to `.cql` files. Future enhancements may include support for [Measure](https://hl7.org/fhir/R4/measure.html), [PlanDefinition](https://hl7.org/fhir/R4/plandefinition.html), and [Questionnaire](https://hl7.org/fhir/R4/questionnaire.html) files.
52+
*
53+
* **Future Enhancements:**
54+
*
55+
* - **Measurement Period**: Incorporate support for Measure files.
56+
* - **PlanDefinition and Questionnaire Support**: Expand the function to handle these additional FHIR resources.
57+
* - **Flexible Contexts**: Enable the function to handle more complex execution contexts beyond CQL, such as PlanDefinitions or other FHIR artifacts.
58+
*
59+
* **Error Handling**:
60+
*
61+
* - **No Active Editor**: Displays a message if no active text editor is found.
62+
* - **Invalid File Type**: Displays a message if the current file is not a CQL file.
63+
* - **Missing Definition**: Displays an error if no valid CQL definition is found on the selected line during expression execution.
1064
*
1165
* @param {Uri} uri - The URI of the file or resource.
1266
* @param {'file' | 'expression'} type - The type of operation: 'file' for full file execution, 'expression' for single expression execution.
1367
* @returns {Promise<void>} A promise that resolves when the execution is complete.
1468
*/
69+
1570
export async function normalizeCqlExecution(uri: Uri, type: 'file' | 'expression'): Promise<void> {
1671
const editor = window.activeTextEditor;
1772
if (!editor) {

0 commit comments

Comments
 (0)