Skip to content

Commit 465eb9d

Browse files
Merge branch 'main' of github.com:NHSDigital/bcss-playwright into feature/BCSS-20328-compartment-5-data-management
2 parents 19226b7 + 8044806 commit 465eb9d

File tree

4 files changed

+361
-16
lines changed

4 files changed

+361
-16
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Utility Guide: Fit Kit Generation
2+
3+
The Fit Kit Generation Utility provides methods to generate and manage FIT test kits for testing purposes.
4+
5+
## Table of Contents
6+
7+
- [Utility Guide: Fit Kit Generation](#utility-guide-fit-kit-generation)
8+
- [Table of Contents](#table-of-contents)
9+
- [Using the Fit Kit Generation Utility](#using-the-fit-kit-generation-utility)
10+
- [Required Arguments](#required-arguments)
11+
- [Fit Kit Generation Specific Functions](#fit-kit-generation-specific-functions)
12+
- [Example Usage](#example-usage)
13+
14+
## Using the Fit Kit Generation Utility
15+
16+
To use the Fit Kit Generation Utility, import the `fit_kit_generation` module, from the `utils` directory, into your test file and call its methods from within your tests, as required. To generate the fit kit IDs, the only method required is `create_fit_id_df`. This will return a `dataframe` with all the transformations already taking place.
17+
18+
## Required Arguments
19+
20+
The methods in this utility require specific arguments. Below is a summary of the required arguments for key methods:
21+
22+
- `generate_kit`: Requires `batch_id` (int) and `kit_type` (str).
23+
24+
## Fit Kit Generation Specific Functions
25+
26+
The Fit Kit Generation Utility includes methods for generating, validating, and managing FIT test kits. These methods are designed to streamline the process of creating test kits for various scenarios. Below are some key functions:
27+
28+
1. **`create_fit_id_df(tk_type_id: int, hub_id: int, no_of_kits: int) -> DataFrame`**
29+
Creates a DataFrame containing FIT kit IDs based on the provided parameters.
30+
- **Arguments**:
31+
- `tk_type_id` (int): The type ID of the test kit.
32+
- `hub_id` (int): The hub ID associated with the kits.
33+
- `no_of_kits` (int): The number of kits to retrieve.
34+
- **Returns**: A pandas DataFrame with the FIT kit IDs.
35+
36+
2. **`calculate_check_digit(kit_id: str) -> str`**
37+
Calculates and appends a check digit to the given kit ID.
38+
- **Arguments**:
39+
- `kit_id` (str): The kit ID to process.
40+
- **Returns**: The kit ID with the appended check digit.
41+
42+
3. **`convert_kit_id_to_fit_device_id(kit_id: str) -> str`**
43+
Converts a kit ID into a FIT Device ID.
44+
- **Arguments**:
45+
- `kit_id` (str): The kit ID to convert.
46+
- **Returns**: The corresponding FIT Device ID.
47+
48+
## Example Usage
49+
50+
```python
51+
from utils.fit_kit_generation import create_fit_id_df, calculate_check_digit, convert_kit_id_to_fit_device_id
52+
53+
def example_usage() -> None:
54+
# Example inputs
55+
tk_type_id = 1
56+
hub_id = 101
57+
no_of_kits_to_retrieve = 2
58+
59+
# Calling the fit_kit_df method will do the following:
60+
# Step 1: Retrieve and process FIT kit data
61+
# Step 2: Calculate a check digit for a single kit ID
62+
# Step 3: Convert a kit ID to a FIT Device ID
63+
64+
fit_kit_df = create_fit_id_df(tk_type_id, hub_id, no_of_kits_to_retrieve)
65+
print("Processed FIT Kit DataFrame:")
66+
print(fit_kit_df)
67+
68+
example_usage()

docs/utility-guides/TableUtil.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
# Utility Guide: Table Utility
2+
3+
The Table Utilities module provides helper functions to interact with and validate HTML tables in Playwright-based UI tests.
4+
5+
## Table of Contents
6+
7+
- [Utility Guide: Table Utility](#utility-guide-table-utility)
8+
- [Table of Contents](#table-of-contents)
9+
- [Using the Table Utility](#using-the-table-utility)
10+
- [Example usage](#example-usage)
11+
- [Get Column Index](#get-column-index)
12+
- [Required Arguments](#required-arguments)
13+
- [How This Function Works](#how-this-function-works)
14+
- [Click First Link In Column](#click-first-link-in-column)
15+
- [Required Arguments](#required-arguments-1)
16+
- [How This Function Works](#how-this-function-works-1)
17+
- [Click First Input In Column](#click-first-input-in-column)
18+
- [Required Arguments](#required-arguments-2)
19+
- [How This Function Works](#how-this-function-works-2)
20+
- [Format Inner Text](#format-inner-text)
21+
- [Required Arguments](#required-arguments-3)
22+
- [How This Function Works](#how-this-function-works-3)
23+
- [Get Table Headers](#get-table-headers)
24+
- [Required Arguments](#required-arguments-4)
25+
- [How This Function Works](#how-this-function-works-4)
26+
- [Get Row Count](#get-row-count)
27+
- [Required Arguments](#required-arguments-5)
28+
- [How This Function Works](#how-this-function-works-5)
29+
- [Pick Row](#pick-row)
30+
- [Required Arguments](#required-arguments-6)
31+
- [How This Function Works](#how-this-function-works-6)
32+
- [Pick Random Row](#pick-random-row)
33+
- [Required Arguments](#required-arguments-7)
34+
- [How This Function Works](#how-this-function-works-7)
35+
- [Pick Random Row Number](#pick-random-row-number)
36+
- [Required Arguments](#required-arguments-8)
37+
- [How This Function Works](#how-this-function-works-8)
38+
- [Get Row Data With Headers](#get-row-data-with-headers)
39+
- [Required Arguments](#required-arguments-9)
40+
- [How This Function Works](#how-this-function-works-9)
41+
- [Get Full Table With Headers](#get-full-table-with-headers)
42+
- [Required Arguments](#required-arguments-10)
43+
- [How This Function Works](#how-this-function-works-10)
44+
45+
## Using the Table Utility
46+
47+
To use the Table Utility, import the `TableUtils` class into your POM file and then define the actions using the its methods as needed.
48+
49+
## Example usage
50+
51+
Below is an example of how the TableUtils used in reports_page.py
52+
53+
from utils.table_util import TableUtils
54+
class ReportsPage(BasePage):
55+
"""Reports Page locators, and methods for interacting with the page."""
56+
57+
def __init__(self, page):
58+
super().__init__(page)
59+
self.page = page
60+
61+
# Initialize TableUtils for different tables
62+
self.failsafe_reports_sub_links_table = TableUtils(page, "#listReportDataTable")
63+
self.fail_safe_reports_screening_subjects_with_inactive_open_episodes_table = (
64+
TableUtils(page, "#subjInactiveOpenEpisodes")
65+
)
66+
def click_failsafe_reports_sub_links(self):
67+
"""Clicks the first NHS number link from the primary report table."""
68+
self.failsafe_reports_sub_links_table.click_first_link_in_column("NHS Number")
69+
70+
### Get Column Index
71+
72+
This function returns the index (1-based) of a specified column name. 1-based indexing means the first column is considered index 1 (not 0 as in Python lists).
73+
If the column is not found, the function returns -1.
74+
75+
#### Required Arguments
76+
77+
- `column_name`:
78+
- Type: `str`
79+
- The visible header text of the column to locate.
80+
81+
#### How This Function Works
82+
83+
1. Attempts to identify table headers from <thead> or <tbody>.
84+
2. Iterates through header cells and matches text with `column_name`.
85+
3. Returns the index if found, otherwise raises an error.
86+
87+
### Click First Link In Column
88+
89+
Clicks on the first hyperlink present in a specified column.
90+
91+
#### Required Arguments
92+
93+
- `column_name`:
94+
- Type: `str`
95+
- The column in which the link needs to be found.
96+
97+
#### How This Function Works
98+
99+
1. Finds the index of the specified column.
100+
2. Searches the first visible row in the column for an `<a>` tag.
101+
3. Clicks the first available link found.
102+
103+
### Click First Input In Column
104+
105+
Clicks on the first input element (e.g., checkbox/radio) in a specific column.
106+
107+
#### Required Arguments
108+
109+
- `column_name`:
110+
- Type: `str`
111+
- The name of the column containing the input element.
112+
113+
#### How This Function Works
114+
115+
1. Locates the index of the specified column.
116+
2. Checks the first visible row for an `<input>` tag in that column.
117+
3. Clicks the first input found.
118+
119+
### Format Inner Text
120+
121+
Formats inner text of a row string into a dictionary.
122+
123+
#### Required Arguments
124+
125+
- data:
126+
- Type: `str`
127+
- Raw inner text of a table row (tab-delimited).
128+
129+
#### How This Function Works
130+
131+
1. Splits the string by tab characters (\t).
132+
2. Enumerates the result and maps index to cell value.
133+
3. Returns a dictionary representing the row.
134+
135+
### Get Table Headers
136+
137+
Extracts and returns table headers.
138+
139+
#### Required Arguments
140+
141+
- None
142+
143+
#### How This Function Works
144+
145+
1. Selects the first row inside <thead> (if available).
146+
2. Captures the visible text of each <th>.
147+
3. Returns a dictionary mapping index to header text.
148+
149+
### Get Row Count
150+
151+
Returns the count of visible rows in the table.
152+
153+
#### Required Arguments
154+
155+
- None
156+
157+
#### How This Function Works
158+
159+
1. Locates all <tr> elements inside <tbody>.
160+
2. Filters to include only visible rows.
161+
3. Returns the total count.
162+
163+
### Pick Row
164+
165+
Returns a locator for a specific row.
166+
167+
#### Required Arguments
168+
169+
- `row_number`:
170+
- Type: `int`
171+
- The row index to locate (1-based).
172+
173+
#### How This Function Works
174+
175+
1. Builds a locator for the nth <tr> inside <tbody>.
176+
2. Returns the locator object.
177+
178+
### Pick Random Row
179+
180+
Picks and returns a random row locator.
181+
182+
#### Required Arguments
183+
184+
- None
185+
186+
#### How This Function Works
187+
188+
1. Gets all visible rows inside <tbody>.
189+
2. Uses a secure random generator to pick one.
190+
3. Returns the locator for that row.
191+
192+
### Pick Random Row Number
193+
194+
Returns the number of a randomly selected row.
195+
196+
#### Required Arguments
197+
198+
- None
199+
200+
#### How This Function Works
201+
202+
1. Retrieves visible rows from <tbody>.
203+
2. Randomly selects an index using secrets.choice.
204+
3. Returns the numeric index.
205+
206+
### Get Row Data With Headers
207+
208+
Returns a dictionary of header-value pairs for a given row.
209+
210+
#### Required Arguments
211+
212+
- `row_number`:
213+
- Type:int
214+
- Index of the target row (1-based).
215+
216+
#### How This Function Works
217+
218+
1. Extracts text from the specified row.
219+
2. Retrieves headers using get_table_headers.
220+
3. Maps each cell to its respective header.
221+
222+
### Get Full Table With Headers
223+
224+
Constructs a dictionary of the entire table content.
225+
226+
#### Required Arguments
227+
228+
- None
229+
230+
#### How This Function Works
231+
232+
1. Gets all visible rows.
233+
2. Retrieves headers once.
234+
3. Loops over each row and builds a dictionary using get_row_data_with_headers.
235+
4. Returns a dictionary where each key is a row number.

utils/fit_kit_generation.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,25 @@ def create_fit_id_df(
88
tk_type_id: int, hub_id: int, no_of_kits_to_retrieve: int
99
) -> pd.DataFrame:
1010
"""
11-
The first step here is to get the relevant test data for compartment 2
12-
Then it calculates the check digit for each kit id retrieved
13-
Finally it adds the final part on the end (expiry date + random characters)
11+
This function retrieves test kit data from the database for the specified compartment (using the 'get_kit_id_from_db' function from 'oracle_specific_functions.py').
12+
It then calculates a check digit for each retrieved kit ID and appends it to the kit ID.
13+
Finally, it generates a FIT Device ID by appending an expiry date and a fixed suffix to the kit ID.
14+
15+
For example:
16+
Given the following inputs:
17+
tk_type_id = 1, hub_id = 101, no_of_kits_to_retrieve = 2
18+
The function retrieves two kit IDs from the database, e.g., ["ABC123", "DEF456"].
19+
It calculates the check digit for each kit ID, resulting in ["ABC123-K", "DEF456-M"].
20+
Then, it generates the FIT Device IDs, e.g., ["ABC123-K122512345/KD00001", "DEF456-M122512345/KD00001"].
21+
22+
Args:
23+
tk_type_id (int): The type ID of the test kit.
24+
hub_id (int): The ID of the hub from which to retrieve the kits.
25+
no_of_kits_to_retrieve (int): The number of kits to retrieve from the database.
26+
27+
Returns:
28+
pd.DataFrame: A DataFrame containing the processed kit IDs, including the calculated check digit
29+
and the final formatted FIT Device ID.
1430
"""
1531
df = get_kit_id_from_db(tk_type_id, hub_id, no_of_kits_to_retrieve)
1632
df["fit_device_id"] = df["kitid"].apply(calculate_check_digit)
@@ -20,11 +36,23 @@ def create_fit_id_df(
2036

2137
def calculate_check_digit(kit_id: str) -> str:
2238
"""
23-
This function used used to calculate the check digit of a kit ID
24-
It calculates the check digit by getting the sum of the location of each character in the kit id
25-
Then it divides the sum by 43 and gets the remainder from this
26-
It then searches the string "char_string" to find the index of the remainder
27-
The character found is then the check digit
39+
Calculates the check digit for a given kit ID.
40+
41+
The check digit is determined by summing the positions of each character in the kit ID
42+
within a predefined character set. The remainder of the sum divided by 43 is used to
43+
find the corresponding character in the character set, which becomes the check digit.
44+
45+
For example:
46+
Given the kit ID "ABC123", the positions of the characters in the predefined
47+
character set are summed. If the total is 123, the remainder when divided by 43
48+
is 37. The character at position 37 in the character set is "K". The resulting
49+
kit ID with the check digit appended would be "ABC123-K".
50+
51+
Args:
52+
kit_id (str): The kit ID to calculate the check digit for.
53+
54+
Returns:
55+
str: The kit ID with the calculated check digit appended.
2856
"""
2957
logging.info(f"Calculating check digit for kit id: {kit_id}")
3058
total = 0
@@ -37,10 +65,17 @@ def calculate_check_digit(kit_id: str) -> str:
3765

3866
def convert_kit_id_to_fit_device_id(kit_id: str) -> str:
3967
"""
40-
This is used to add the expiry date to the end of the Kit ID
41-
This by setting the month to december
42-
And the year is set to 1 year in the future.
43-
E.g. if the current date is 06/24 the expiry date will be set to 12/25
68+
Converts a Kit ID into a FIT Device ID by appending an expiry date and a fixed suffix.
69+
70+
The expiry date is calculated by setting the month to December and the year to one year
71+
in the future based on the current date. For example, if the current date is June 2024,
72+
the expiry date will be set to December 2025.
73+
74+
Args:
75+
kit_id (str): The Kit ID to be converted.
76+
77+
Returns:
78+
str: The generated FIT Device ID in the format "{kit_id}12{next_year}12345/KD00001".
4479
"""
4580
logging.info(f"Generating FIT Device ID from: {kit_id}")
4681
today = datetime.now()

0 commit comments

Comments
 (0)