Skip to content

Commit 2ccb5e9

Browse files
committed
Merge branch 'main' into feature/BCSS-20586-selenium-to-playwright-organisation
# Conflicts: # .gitleaksignore
2 parents 556c9ad + 145171b commit 2ccb5e9

File tree

13 files changed

+6065
-13
lines changed

13 files changed

+6065
-13
lines changed

.gitleaksignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,6 @@ cd9c0efec38c5d63053dd865e5d4e207c0760d91:docs/guides/Perform_static_analysis.md:
221221
751ddbd178e91293c20282df62e8d3fe1086310a:utils/resources/axe.js:ipv4:32080
222222
751ddbd178e91293c20282df62e8d3fe1086310a:utils/resources/axe.js:ipv4:32089
223223
751ddbd178e91293c20282df62e8d3fe1086310a:utils/resources/axe.js:ipv4:32103
224-
cae84544e2852202f5d0abb9ad18f9d83a0c6d80:subject_criteria_builder/criteria.json:generic-api-key:4210 203cd8811be75d33859eb7c7cc8a48beb5a2b8b2:subject_criteria_builder/criteria.json:generic-api-key:4210 60468a7d6f3ff3259616757be85d6f07e62d00b6:subject_criteria_builder/criteria.json:generic-api-key:4210
224+
cae84544e2852202f5d0abb9ad18f9d83a0c6d80:subject_criteria_builder/criteria.json:generic-api-key:4210
225+
203cd8811be75d33859eb7c7cc8a48beb5a2b8b2:subject_criteria_builder/criteria.json:generic-api-key:4210
226+
60468a7d6f3ff3259616757be85d6f07e62d00b6:subject_criteria_builder/criteria.json:generic-api-key:4210

.idea/.gitignore

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# Subject Criteria Builder Application
2+
3+
This application is a Streamlit-based tool for interactively building subject selection criteria for the NHS BCSS system. It allows users to search, select, and configure criteria keys, view descriptions, choose from allowed values, and copy the resulting criteria as JSON. It also generates the corresponding SQL query and, if required, allows you to select a user or enter a subject's NHS number to populate the query context.
4+
5+
---
6+
7+
## Table of Contents
8+
9+
- [Subject Criteria Builder Application](#subject-criteria-builder-application)
10+
- [Table of Contents](#table-of-contents)
11+
- [How to Use the Application](#how-to-use-the-application)
12+
- [Pre-requisites](#pre-requisites)
13+
- [Launching the App](#launching-the-app)
14+
- [Searching for Criteria](#searching-for-criteria)
15+
- [Selecting and Configuring a Criterion](#selecting-and-configuring-a-criterion)
16+
- [User and Subject Dependencies](#user-and-subject-dependencies)
17+
- [Copying the Criteria](#copying-the-criteria)
18+
- [Resetting the Builder and Hiding Criteria](#resetting-the-builder-and-hiding-criteria)
19+
- [Maintaining the Application](#maintaining-the-application)
20+
- [Updating `criteria.json`](#updating-criteriajson)
21+
- [To add a new criterion](#to-add-a-new-criterion)
22+
- [To edit a criterion](#to-edit-a-criterion)
23+
- [To remove a criterion](#to-remove-a-criterion)
24+
- [About the `dependencies` field](#about-the-dependencies-field)
25+
- [Adding New Criteria Keys](#adding-new-criteria-keys)
26+
- [Editing Allowed Values or Descriptions](#editing-allowed-values-or-descriptions)
27+
- [Troubleshooting](#troubleshooting)
28+
- [Example Entry in `criteria.json`](#example-entry-in-criteriajson)
29+
- [Summary](#summary)
30+
31+
---
32+
33+
## How to Use the Application
34+
35+
### Pre-requisites
36+
37+
Before running the application, you must create a `local.env` file containing your Oracle database credentials.<br>
38+
You can generate a template for this file by running:
39+
40+
```bash
41+
python setup_env_file.py
42+
```
43+
44+
After running the script, open the generated `local.env` file and populate the following fields with your Oracle credentials:
45+
46+
```env
47+
ORACLE_USERNAME=your_oracle_username
48+
ORACLE_DB=your_oracle_db
49+
ORACLE_PASS=your_oracle_password
50+
```
51+
52+
These credentials are required for the application to connect to the Oracle database and populate user and subject objects.
53+
54+
---
55+
56+
### Launching the App
57+
58+
1. **Open a terminal** in the project root directory.
59+
2. Run the following command: `streamlit run subject_criteria_builder.py`
60+
3. The app will open in your default web browser.
61+
4. You can also copy and paste the `Local URL` into a browser to access the app.
62+
63+
---
64+
65+
### Searching for Criteria
66+
67+
- Use the **search bar** at the top to filter criteria keys by name or description.
68+
- The list updates as you type, showing only matching criteria.
69+
- You can hide or show the search bar and criteria list using the "Hide Criteria/Search" and "Show Criteria/Search" buttons at the top.
70+
71+
---
72+
73+
### Selecting and Configuring a Criterion
74+
75+
1. **Expand a criterion** by clicking the ➕ button next to its name.
76+
2. The expanded section shows:
77+
- **Description:** An explanation of what the key is and what input is expected.
78+
- **Dropdown:** If the key has a set of allowed values (from `criteria.json`), a dropdown will appear for you to select from.
79+
- **Text Input:** You can always enter a custom value, even if a dropdown is present.
80+
3. **Collapse** the criterion by clicking the ✖ button.
81+
82+
---
83+
84+
### User and Subject Dependencies
85+
86+
Some criteria require additional context, such as a User or Subject object.<br>
87+
If you select a criterion that has a dependency on "User" or "Subject" (as defined in `criteria.json`):
88+
89+
- **User Dependency:**
90+
- A section will appear allowing you to select a user from the list defined in `users.json`.
91+
- Once selected, the user's name and username are displayed, and a populated user object is created for use in the SQL query.
92+
93+
- **Subject Dependency:**
94+
- A section will appear allowing you to enter a subject's NHS number.
95+
- Once entered, the application will attempt to populate a subject object using this NHS number for use in the SQL query.
96+
97+
These sections will remain visible as long as any selected criteria require them.
98+
99+
---
100+
101+
### Copying the Criteria
102+
103+
- As you configure criteria, the **Final Criteria Dictionary** at the bottom updates in real time.
104+
- To copy the criteria you can either select the whole code block manually or click on the copy button that is at the top right of this code block.
105+
106+
---
107+
108+
### Resetting the Builder and Hiding Criteria
109+
110+
- Click the **🔄 Reset Criteria Builder** button at the top to clear all selections and start over.
111+
- Use the **🙈 Hide Criteria/Search** and **👁️ Show Criteria/Search** buttons to hide or show the search bar and criteria list.
112+
113+
---
114+
115+
## Maintaining the Application
116+
117+
### Updating `criteria.json`
118+
119+
The file `subject_criteria_builder/criteria.json` defines all available criteria keys, their descriptions, allowed values, and dependencies.
120+
121+
Each entry in the file looks like this:
122+
123+
```json
124+
{
125+
"key": "NHS_NUMBER",
126+
"value_source": "",
127+
"notes": "Enter the 10-digit NHS Number of the subject you want to search for."
128+
}
129+
```
130+
131+
Or, with allowed values and dependencies:
132+
133+
```json
134+
{
135+
"key": "SUBJECT_HUB_CODE",
136+
"value_source": "SubjectHubCode.by_description",
137+
"notes": "Select the hub or organisation for the subject.",
138+
"allowed_values": [
139+
"user's hub",
140+
"user's organisation"
141+
],
142+
"dependencies": [
143+
"User"
144+
]
145+
}
146+
```
147+
148+
- **`key`:** The unique identifier for the criterion (must match the code).
149+
- **`value_source`:** (Optional) The source of the value, can be left blank. This refers to the class + method used in the subject selection query builder. This is not used by the code but is there to allow easier tracking/mapping.
150+
- **`notes`:** A clear, user-focused description of what the key is and what the user should input.
151+
- **`allowed_values`:** (Optional) An array of allowed values for the dropdown selection.
152+
- **`dependencies`:** (Optional) An array that can include `"User"` and/or `"Subject"`. If present, the app will prompt for a user selection or subject NHS number as needed.
153+
154+
#### To add a new criterion
155+
156+
1. Add a new object to the JSON array with the following fields:
157+
- `"key"`: The `Enum` member name (must match the code).
158+
- `"value_source"`: (Optional) The value source, can be left blank.
159+
- `"notes"`: A clear, user-focused description of what the key is and what the user should input.
160+
- `"allowed_values"`: (Optional) An array of allowed values for the dropdown selection.
161+
- `"dependencies"`: (Optional) An array containing `"User"` and/or `"Subject"` if the criterion requires additional context.
162+
163+
2. Save the file. The app will automatically pick up the new key on reload.
164+
165+
#### To edit a criterion
166+
167+
- Find the object with the matching `"key"` and update the `"notes"`, `"allowed_values"`, or `"dependencies"` as needed.
168+
- Save the file and reload the app.
169+
170+
#### To remove a criterion
171+
172+
- Delete the object from the JSON array.
173+
- Save the file and reload the app.
174+
175+
#### About the `dependencies` field
176+
177+
- If a criterion requires a User or Subject context, add a `"dependencies"` array to its entry, e.g.:
178+
179+
```json
180+
"dependencies": ["User"]
181+
```
182+
183+
or
184+
185+
```json
186+
"dependencies": ["Subject"]
187+
```
188+
189+
or both:
190+
191+
```json
192+
"dependencies": ["User", "Subject"]
193+
```
194+
195+
- The application will automatically show the relevant input sections when any selected criteria require these dependencies.
196+
197+
---
198+
199+
### Adding New Criteria Keys
200+
201+
If you add new keys to the `SubjectSelectionCriteriaKey` `Enum` in your code, you should also add a corresponding entry in `criteria.json` with a description (`notes`), and (optionally) allowed values and dependencies.
202+
203+
---
204+
205+
### Editing Allowed Values or Descriptions
206+
207+
- **Allowed Values:** Update the `"allowed_values"` array for any key to change the dropdown options shown to users.
208+
- **Descriptions:** Update the `"notes"` field to clarify what the key is for or what input is expected.
209+
- **Dependencies:** Update the `"dependencies"` array to control when the app prompts for user or subject context.
210+
211+
---
212+
213+
### Troubleshooting
214+
215+
- **A key does not appear in the UI:**
216+
Ensure it exists in both the `SubjectSelectionCriteriaKey` `Enum` and in `criteria.json`.
217+
218+
- **Dropdown is missing for a key:**
219+
Add or update the `"allowed_values"` array for that key in `criteria.json`.
220+
221+
- **Descriptions are unclear or missing:**
222+
Update the `"notes"` field for the relevant key in `criteria.json`.
223+
224+
- **App does not reload changes:**
225+
Save your changes and refresh the `Streamlit` app in your browser.
226+
227+
- **Copy to clipboard does not work:**
228+
Some browsers may not support auto-copy. Manually select and copy the JSON from the code block.
229+
230+
- **Oracle DB errors:**
231+
Ensure your `local.env` file is present and contains valid values for `ORACLE_USERNAME`, `ORACLE_DB`, and `ORACLE_PASS`.
232+
233+
---
234+
235+
## Example Entry in `criteria.json`
236+
237+
```json
238+
{
239+
"key": "SCREENING_STATUS",
240+
"value_source": "ScreeningStatusType.by_description_case_insensitive",
241+
"notes": "Select the current screening status for the subject.",
242+
"allowed_values": [
243+
"Call",
244+
"Inactive",
245+
"Opt-in",
246+
"Recall",
247+
"Self-referral",
248+
"Surveillance",
249+
"Ceased"
250+
],
251+
"dependencies": ["Subject"]
252+
}
253+
```
254+
255+
---
256+
257+
## Summary
258+
259+
- **All configuration is driven by `criteria.json`.**
260+
- **Descriptions, allowed values, and dependencies are fully customizable.**
261+
- **No code changes are needed for most updates—just edit the JSON file.**
262+
- **For new `Enum` keys, add them to both the `SubjectSelectionCriteriaKey` `Enum` and the JSON.**
263+
- **For criteria that require user or subject context, use the `dependencies` field.**

pages/base_page.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def assert_dialog_text(self, expected_text: str, accept: bool = False) -> None:
256256
If no dialog appears, logs an error.
257257
Args:
258258
expected_text (str): The text that should be present in the dialog.
259-
accept (bool): Set to True if you want to accept the dialog, by deafult is is set to False.
259+
accept (bool): Set to True if you want to accept the dialog, by default is is set to False.
260260
"""
261261
self._dialog_assertion_error = None
262262

@@ -265,7 +265,7 @@ def handle_dialog(dialog: Dialog, accept: bool = False):
265265
Handles the dialog and asserts that the dialog contains the expected text.
266266
Args:
267267
dialog (Dialog): the playwright dialog object
268-
accept (bool): Set to True if you want to accept the dialog, by deafult is is set to False.
268+
accept (bool): Set to True if you want to accept the dialog, by default is is set to False.
269269
"""
270270
logging.info(f"Dialog appeared with message: {dialog.message}")
271271
actual_text = dialog.message

pages/screening_practitioner_appointments/appointment_detail_page.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def __init__(self, page: Page):
1515
self.calendar_button = self.page.get_by_role("button", name="Calendar")
1616
self.save_button = self.page.get_by_role("button", name="Save")
1717
self.cancel_radio = self.page.get_by_role("radio", name="Cancel")
18-
self.reason_for_cancellation_dropwdown = self.page.get_by_label(
18+
self.reason_for_cancellation_dropdown = self.page.get_by_label(
1919
"Reason for Cancellation"
2020
)
2121

@@ -84,7 +84,7 @@ def select_reason_for_cancellation_option(self, option: str) -> None:
8484
option: The reason for cancellation to select.
8585
The options are in the ReasonForCancellationOptions class
8686
"""
87-
self.reason_for_cancellation_dropwdown.select_option(value=option)
87+
self.reason_for_cancellation_dropdown.select_option(value=option)
8888

8989

9090
class ReasonForCancellationOptions(StrEnum):
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from pages.base_page import BasePage
2+
from playwright.sync_api import Page
3+
4+
5+
class CloseFobtScreeningEpisodePage(BasePage):
6+
"""Close FOBT Screening Episode Page locators, and methods for interacting with the page."""
7+
8+
def __init__(self, page: Page):
9+
super().__init__(page)
10+
self.page = page
11+
self.close_fobt_screening_episode_button = self.page.get_by_role(
12+
"button", name="Close FOBT Screening Episode"
13+
)
14+
self.reason_dropdown = self.page.locator("#CLOSE_REASON")
15+
self.notes_textarea = self.page.locator("#UI_NOTES_TEXT")
16+
self.final_close_button = self.page.locator("#UI_BUTTON_CLOSE")
17+
18+
def close_fobt_screening_episode(self, reason_text: str) -> None:
19+
"""
20+
Complete the process of closing a FOBT screening episode.
21+
Args:
22+
reason_text (str): The visible text of the reason to select from the dropdown.
23+
"""
24+
# Step 1: Trigger the dialog and accept it
25+
self.safe_accept_dialog(self.close_fobt_screening_episode_button)
26+
27+
# Step 2: Select reason from dropdown
28+
self.reason_dropdown.select_option(label=reason_text)
29+
30+
# Step 3: Enter note
31+
self.notes_textarea.fill("automation test note")
32+
33+
# Step 4: Click final 'Close Episode' button
34+
self.safe_accept_dialog(self.final_close_button)

pages/screening_subject_search/record_diagnosis_date_page.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ class RecordDiagnosisDatePage(BasePage):
1010
def __init__(self, page: Page):
1111
super().__init__(page)
1212
self.page = page
13+
1314
# Record Diagnosis Date - page locators
1415
self.diagnosis_date_field = self.page.locator("#diagnosisDate")
16+
self.reason_dropdown = self.page.locator("#reason")
1517
self.save_button = self.page.get_by_role("button", name="Save")
1618

1719
def enter_date_in_diagnosis_date_field(self, date: datetime) -> None:
@@ -41,3 +43,22 @@ def get_alert_message(self) -> str:
4143
return self.alert_message.inner_text()
4244
else:
4345
return ""
46+
47+
def record_diagnosis_reason(self, reason_text: str) -> None:
48+
"""
49+
Selects a diagnosis reason from the dropdown and saves the form.
50+
51+
Args:
52+
reason_text (str): The visible text of the reason to select.
53+
Valid options include:
54+
- "Patient declined all appointments"
55+
- "2 DNAs of colonoscopy assessment appt"
56+
- "Patient emigrated"
57+
- "Patient deceased"
58+
- "Patient choice"
59+
- "Patient could not be contacted"
60+
- "Reopened old episode, date unknown"
61+
- "Other"
62+
"""
63+
self.reason_dropdown.select_option(label=reason_text)
64+
self.safe_accept_dialog(self.save_button)

0 commit comments

Comments
 (0)