-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Include task-shifting #1280
base: master
Are you sure you want to change the base?
Include task-shifting #1280
Changes from 3 commits
dae3cd9
c8f70ca
d5f1de6
a2490a4
1dea058
6445c9b
6e8357b
77963be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -563,6 +563,10 @@ class HealthSystem(Module): | |||||||||||||
Types.BOOL, "Decide whether to scale HR capabilities by population size every year. Can be used as well as" | ||||||||||||||
" the dynamic_HR_scaling_factor" | ||||||||||||||
), | ||||||||||||||
|
||||||||||||||
'include_task_shifting': Parameter( | ||||||||||||||
Types.BOOL, "Decide whether to allow for task-shifting in mode 2" | ||||||||||||||
), | ||||||||||||||
|
||||||||||||||
'tclose_overwrite': Parameter( | ||||||||||||||
Types.INT, "Decide whether to overwrite tclose variables assigned by disease modules"), | ||||||||||||||
|
@@ -599,6 +603,7 @@ def __init__( | |||||||||||||
beds_availability: Optional[str] = None, | ||||||||||||||
randomise_queue: bool = True, | ||||||||||||||
ignore_priority: bool = False, | ||||||||||||||
include_task_shifting: bool = False, | ||||||||||||||
policy_name: Optional[str] = None, | ||||||||||||||
capabilities_coefficient: Optional[float] = None, | ||||||||||||||
use_funded_or_actual_staffing: Optional[str] = None, | ||||||||||||||
|
@@ -625,6 +630,8 @@ def __init__( | |||||||||||||
and priority | ||||||||||||||
:param ignore_priority: If ``True`` do not use the priority information in HSI | ||||||||||||||
event to schedule | ||||||||||||||
:param include_task_shifting: If ``True`` when in mode 2 consider task-shifting of officers if one originally | ||||||||||||||
required is not available | ||||||||||||||
:param policy_name: Name of priority policy that will be adopted if any | ||||||||||||||
:param capabilities_coefficient: Multiplier for the capabilities of health | ||||||||||||||
officers, if ``None`` set to ratio of initial population to estimated 2010 | ||||||||||||||
|
@@ -658,6 +665,14 @@ def __init__( | |||||||||||||
assert not (ignore_priority and policy_name is not None), ( | ||||||||||||||
'Cannot adopt a priority policy if the priority will be then ignored' | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
# Global task-shifting options. The key in the dictionary refers to the officer | ||||||||||||||
# eligible for task shifting, while the values refer to the officers that can take | ||||||||||||||
# over the officer's tasks. The numbers refer to the factor by which appt time will | ||||||||||||||
# have to be scaled if task is performed by alternative officer. | ||||||||||||||
self.global_task_shifting = { | ||||||||||||||
'Pharmacy': (['Nursing_and_Midwifery', 'Clinical'], [1.5,1]), | ||||||||||||||
} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think more pythonic to write as we don;t then rely on ordering of these two lists, and the relationship between the numbers (1.5, 1.0) and strings ('Nursing', 'Clinical') is made explicit. (Also when it's used you're using zip to iterate through these things together, but if it were a dict, you could just use
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also - shouldn't think be read in from a .csv ResourceFile, so that we can edit this in different simulations? Structure of .csv file:
This would actually made the Parameter (include_task_shifting) redundant, because if we didn't want to allow any (global) task shifting rules, then this file would be blank. |
||||||||||||||
|
||||||||||||||
self.disable = disable | ||||||||||||||
self.disable_and_reject_all = disable_and_reject_all | ||||||||||||||
|
@@ -673,6 +688,8 @@ def __init__( | |||||||||||||
self.randomise_queue = randomise_queue | ||||||||||||||
|
||||||||||||||
self.ignore_priority = ignore_priority | ||||||||||||||
|
||||||||||||||
self.include_task_shifting = include_task_shifting | ||||||||||||||
|
||||||||||||||
# This default value will be overwritten if assumed policy is not None | ||||||||||||||
self.lowest_priority_considered = 2 | ||||||||||||||
|
@@ -2367,7 +2384,7 @@ def process_events_mode_0_and_1(self, hold_over: List[HSIEventQueueItem]) -> Non | |||||||||||||
hold_over.extend(_to_be_held_over) | ||||||||||||||
|
||||||||||||||
def process_events_mode_2(self, hold_over: List[HSIEventQueueItem]) -> None: | ||||||||||||||
|
||||||||||||||
capabilities_monitor = Counter(self.module.capabilities_today.to_dict()) | ||||||||||||||
set_capabilities_still_available = {k for k, v in capabilities_monitor.items() if v > 0.0} | ||||||||||||||
|
||||||||||||||
|
@@ -2445,17 +2462,58 @@ def process_events_mode_2(self, hold_over: List[HSIEventQueueItem]) -> None: | |||||||||||||
# based on queue information, and we assume no squeeze ever takes place. | ||||||||||||||
squeeze_factor = 0. | ||||||||||||||
|
||||||||||||||
# Check if any of the officers required have run out. | ||||||||||||||
# Check if any of the officers required have run out. If including task-shifting, | ||||||||||||||
# this will involve checking if alternative capabilities are available for officers | ||||||||||||||
# that are no longer available. | ||||||||||||||
out_of_resources = False | ||||||||||||||
|
||||||||||||||
# This dictionary stores all task-shifting officers considered for this appointment | ||||||||||||||
task_shifting_adopted = {} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this dict isn't logged or used. To allow logging, we could save a flag onto the HSI Event itself (e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is adopted, and it must be used to know which officers were assigned what tasks (line 2506) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see it being written to but not read from (?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lines 2593 to 2596 |
||||||||||||||
for officer, call in original_call.items(): | ||||||||||||||
# If any of the officers are not available, then out of resources | ||||||||||||||
# If any of the officers are not available, then out of resources, unless these can be | ||||||||||||||
# task-shifted | ||||||||||||||
if officer not in set_capabilities_still_available: | ||||||||||||||
|
||||||||||||||
# Set this to True for now, however if: | ||||||||||||||
# 1. Task-shifting is included, | ||||||||||||||
# 2. Task-shifting is available for this officer/treatment, | ||||||||||||||
# 3. Alternative officers are still available | ||||||||||||||
# then will reset to False | ||||||||||||||
out_of_resources = True | ||||||||||||||
|
||||||||||||||
if self.sim.modules['HealthSystem'].include_task_shifting: | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
|
||||||||||||||
# Get officer type only | ||||||||||||||
officer_no_facility = officer.split("Officer_")[1] | ||||||||||||||
|
||||||||||||||
# Extract task-shifting options for this officer | ||||||||||||||
task_shift_options_for_officer = self.sim.modules['HealthSystem'].global_task_shifting.get(officer_no_facility) | ||||||||||||||
|
||||||||||||||
# Check if possible alternatives to officer are available | ||||||||||||||
# If they are, must register that we'll be using alternative. | ||||||||||||||
if task_shift_options_for_officer: | ||||||||||||||
|
||||||||||||||
# Unpack values | ||||||||||||||
new_officer_no_facility, time_scaling = task_shift_options_for_officer | ||||||||||||||
Comment on lines
+2653
to
+2654
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why unpack only to zip in the following line? I think we can skip this line. |
||||||||||||||
|
||||||||||||||
for new_officer_no_facility, time_scaling in zip(new_officer_no_facility, time_scaling): | ||||||||||||||
# Get the task-shifting officer | ||||||||||||||
shift_target_officer = officer.replace(officer_no_facility, new_officer_no_facility) | ||||||||||||||
|
||||||||||||||
# Check if this task-shifting officer is available, and save task_shift | ||||||||||||||
if shift_target_officer in set_capabilities_still_available: | ||||||||||||||
# Record task-shifting | ||||||||||||||
task_shifting_adopted[officer] = (shift_target_officer, time_scaling) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As noted above, this dict of record isn't used anywhere. |
||||||||||||||
out_of_resources = False | ||||||||||||||
|
||||||||||||||
# Once we've found available officer to replace, no need to go through other | ||||||||||||||
# options | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
break | ||||||||||||||
|
||||||||||||||
# If officers still available, run event. Note: in current logic, a little | ||||||||||||||
# overtime is allowed to run last event of the day. This seems more realistic | ||||||||||||||
# than medical staff leaving earlier than | ||||||||||||||
# planned if seeing another patient would take them into overtime. | ||||||||||||||
|
||||||||||||||
if out_of_resources: | ||||||||||||||
|
||||||||||||||
# Do not run, | ||||||||||||||
|
@@ -2499,6 +2557,7 @@ def process_events_mode_2(self, hold_over: List[HSIEventQueueItem]) -> None: | |||||||||||||
f"Cannot run HSI {event.TREATMENT_ID} without facility_info being defined." | ||||||||||||||
|
||||||||||||||
# Expected appt footprint before running event | ||||||||||||||
# NOTE-TO-ADD: This appt footprint needs to reflect that a different officer was used | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this could be accomplished by saving the flag onto the HSI Event as per above? |
||||||||||||||
_appt_footprint_before_running = event.EXPECTED_APPT_FOOTPRINT | ||||||||||||||
# Run event & get actual footprint | ||||||||||||||
actual_appt_footprint = event.run(squeeze_factor=squeeze_factor) | ||||||||||||||
|
@@ -2521,7 +2580,40 @@ def process_events_mode_2(self, hold_over: List[HSIEventQueueItem]) -> None: | |||||||||||||
# Recalculate call on officers based on squeeze factor. | ||||||||||||||
for k in updated_call.keys(): | ||||||||||||||
updated_call[k] = updated_call[k]/(squeeze_factor + 1.) | ||||||||||||||
|
||||||||||||||
|
||||||||||||||
# Recalculate call on officers including task shifting, which may result in | ||||||||||||||
# a change to required time | ||||||||||||||
if task_shifting_adopted: | ||||||||||||||
updated_call_inc_task_shift = {} | ||||||||||||||
# Go over all officers in updated_call | ||||||||||||||
for k in updated_call.keys(): | ||||||||||||||
|
||||||||||||||
# If task-shifting was requested for this officer, change name | ||||||||||||||
# of officer and rescale original task by relevant factor | ||||||||||||||
if k in task_shifting_adopted.keys(): | ||||||||||||||
|
||||||||||||||
task_for_officer = updated_call[k]*task_shifting_adopted[k][1] | ||||||||||||||
j = task_shifting_adopted[k][0] | ||||||||||||||
|
||||||||||||||
if j in updated_call_inc_task_shift.keys(): | ||||||||||||||
# If officer is already included in updated_call_inc_task_shift | ||||||||||||||
# (e.g. because it was already performing own tasks as well as | ||||||||||||||
# taking over that of officer not available) add to original task | ||||||||||||||
updated_call_inc_task_shift[j] += task_for_officer | ||||||||||||||
else: | ||||||||||||||
updated_call_inc_task_shift[j] = task_for_officer | ||||||||||||||
|
||||||||||||||
# Else simply add original requirement to new call | ||||||||||||||
else: | ||||||||||||||
if k in updated_call_inc_task_shift.keys(): | ||||||||||||||
# Ensure that if this officer already present in dictionary | ||||||||||||||
# this task is added, not overwritten | ||||||||||||||
updated_call_inc_task_shift[k] += updated_call[k] | ||||||||||||||
else: | ||||||||||||||
updated_call_inc_task_shift[k] = updated_call[k] | ||||||||||||||
|
||||||||||||||
updated_call = updated_call_inc_task_shift | ||||||||||||||
|
||||||||||||||
tbhallett marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
# Subtract this from capabilities used so-far today | ||||||||||||||
capabilities_monitor.subtract(updated_call) | ||||||||||||||
|
||||||||||||||
|
@@ -2543,13 +2635,16 @@ def process_events_mode_2(self, hold_over: List[HSIEventQueueItem]) -> None: | |||||||||||||
self.module.running_total_footprint += updated_call | ||||||||||||||
|
||||||||||||||
# Write to the log | ||||||||||||||
# WARNING: the logged appt footprint does not contain information | ||||||||||||||
# on whether task-shifting was performed or not. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indeed, this would be another reason to let the HSI return actual footprint that its provided with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See reply to comment above |
||||||||||||||
self.module.record_hsi_event( | ||||||||||||||
hsi_event=event, | ||||||||||||||
actual_appt_footprint=actual_appt_footprint, | ||||||||||||||
squeeze_factor=squeeze_factor, | ||||||||||||||
did_run=True, | ||||||||||||||
priority=_priority | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
|
||||||||||||||
# Don't have any capabilities at all left for today, no | ||||||||||||||
# point in going through the queue to check what's left to do today. | ||||||||||||||
|
@@ -2827,6 +2922,7 @@ class HealthSystemChangeParameters(Event, PopulationScopeEventMixin): | |||||||||||||
"""Event that causes certain internal parameters of the HealthSystem to be changed; specifically: | ||||||||||||||
* `mode_appt_constraints` | ||||||||||||||
* `ignore_priority` | ||||||||||||||
* `include_task_shifting` | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as above, it seems duplicative to me to provide two ways to modify this behaviour. I think it should just be through Parameters. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes you are right, the reason this wasn't optimised at this stage is that we're still not sure whether we wanted to allow for a global and HSI-specific task-shifting approach (in which case e.g. include_task_shifting could be categorical - None, Global-level, HSI-level and we would have an additional resource file in case of global taskshifting, one for HSI-based on etc) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's always the case that we should modify the behaviour of the module through Parametees rather than kwargs though, as that it has the Scenario class expects to pass through changes. |
||||||||||||||
* `capabilities_coefficient` | ||||||||||||||
* `cons_availability` | ||||||||||||||
* `beds_availability` | ||||||||||||||
|
@@ -2843,6 +2939,9 @@ def apply(self, population): | |||||||||||||
|
||||||||||||||
if 'ignore_priority' in self._parameters: | ||||||||||||||
self.module.ignore_priority = self._parameters['ignore_priority'] | ||||||||||||||
|
||||||||||||||
if 'include_task_shifting' in self._parameters: | ||||||||||||||
self.module.include_task_shifting = self._parameters['include_task_shifting'] | ||||||||||||||
|
||||||||||||||
if 'capabilities_coefficient' in self._parameters: | ||||||||||||||
self.module.capabilities_coefficient = self._parameters['capabilities_coefficient'] | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import heapq as hp | ||
import os | ||
import re | ||
from pathlib import Path | ||
from typing import Set, Tuple | ||
|
||
|
@@ -1257,13 +1258,15 @@ def test_HealthSystemChangeParameters(seed, tmpdir): | |
initial_parameters = { | ||
'mode_appt_constraints': 0, | ||
'ignore_priority': False, | ||
'include_task_shifting': False, | ||
'capabilities_coefficient': 0.5, | ||
'cons_availability': 'all', | ||
'beds_availability': 'default', | ||
} | ||
new_parameters = { | ||
'mode_appt_constraints': 2, | ||
'ignore_priority': True, | ||
'include_task_shifting': True, | ||
'capabilities_coefficient': 1.0, | ||
'cons_availability': 'none', | ||
'beds_availability': 'none', | ||
|
@@ -1279,6 +1282,7 @@ def apply(self, population): | |
_params = dict() | ||
_params['mode_appt_constraints'] = hs.mode_appt_constraints | ||
_params['ignore_priority'] = hs.ignore_priority | ||
_params['include_task_shifting'] = hs.include_task_shifting | ||
_params['capabilities_coefficient'] = hs.capabilities_coefficient | ||
_params['cons_availability'] = hs.consumables.cons_availability | ||
_params['beds_availability'] = hs.bed_days.availability | ||
|
@@ -1952,6 +1956,128 @@ def apply(self, person_id, squeeze_factor): | |
assert (Nran_w_priority2 == int(tot_population/4)) & (Nran_w_priority3 == 0) | ||
|
||
|
||
def test_task_shifting_in_mode_2(seed, tmpdir): | ||
"""Test that in mode 2 task-shifting takes place as expected even if capabilities for | ||
required officer are no longer available, provided an alternative officer still is. | ||
By "as expected" we mean that the first alternative officer listed is chosen preferentially. | ||
""" | ||
|
||
# Create Dummy Module to host the HSI | ||
class DummyModule(Module): | ||
METADATA = {Metadata.DISEASE_MODULE, Metadata.USES_HEALTHSYSTEM} | ||
|
||
def read_parameters(self, data_folder): | ||
pass | ||
|
||
def initialise_population(self, population): | ||
pass | ||
|
||
def initialise_simulation(self, sim): | ||
pass | ||
|
||
# Create a dummy HSI event class | ||
class DummyHSIEvent(HSI_Event, IndividualScopeEventMixin): | ||
def __init__(self, module, person_id, appt_type, level): | ||
super().__init__(module, person_id=person_id) | ||
self.TREATMENT_ID = 'DummyHSIEvent' | ||
self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({appt_type: 1}) | ||
self.ACCEPTED_FACILITY_LEVEL = level | ||
|
||
self.this_hsi_event_ran = False | ||
|
||
def apply(self, person_id, squeeze_factor): | ||
self.this_hsi_event_ran = True | ||
|
||
log_config = { | ||
"filename": "log", | ||
"directory": tmpdir, | ||
"custom_levels": {"tlo.methods.healthsystem": logging.DEBUG}, | ||
} | ||
sim = Simulation(start_date=start_date, seed=seed, log_config=log_config) | ||
|
||
# Register the core modules and simulate for 0 days | ||
sim.register(demography.Demography(resourcefilepath=resourcefilepath), | ||
healthsystem.HealthSystem(resourcefilepath=resourcefilepath, | ||
capabilities_coefficient=1.0, | ||
mode_appt_constraints=2, | ||
include_task_shifting=True, | ||
ignore_priority=False, | ||
randomise_queue=True, | ||
policy_name="", | ||
use_funded_or_actual_staffing='funded_plus'), | ||
DummyModule() | ||
) | ||
|
||
tot_population = 100 | ||
sim.make_initial_population(n=tot_population) | ||
sim.simulate(end_date=sim.start_date) | ||
|
||
# Get pointer to the HealthSystemScheduler event | ||
healthsystemscheduler = sim.modules['HealthSystem'].healthsystemscheduler | ||
|
||
# Force entire population in one district (keys_district[0]) and get facility ID | ||
person_for_district = {d: i for i, d in enumerate(sim.population.props['district_of_residence'].cat.categories)} | ||
keys_district = list(person_for_district.keys()) | ||
|
||
for i in range(0, int(tot_population)): | ||
sim.population.props.at[i, 'district_of_residence'] = keys_district[0] | ||
tbhallett marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
# Schedule an identical appointment for all individuals | ||
for i in range(0, tot_population): | ||
|
||
hsi = DummyHSIEvent(module=sim.modules['DummyModule'], | ||
person_id=i, | ||
appt_type='MinorSurg', | ||
level='1a') | ||
|
||
sim.modules['HealthSystem'].schedule_hsi_event( | ||
hsi, | ||
topen=sim.date, | ||
tclose=sim.date + pd.DateOffset(days=1), | ||
# Assign equal priority | ||
priority=0 | ||
) | ||
|
||
hsi1 = DummyHSIEvent(module=sim.modules['DummyModule'], | ||
person_id=0, # Ensures call is on officers in first district | ||
appt_type='MinorSurg', | ||
level='1a') | ||
hsi1.initialise() | ||
|
||
# Get facility ID | ||
facID = int((re.search(r'\d+', next(iter(hsi1.expected_time_requests)))).group()) | ||
|
||
pharmacy_task_time = hsi1.expected_time_requests['FacilityID_' + str(facID) + '_Officer_Pharmacy'] | ||
nursing_task_time = hsi1.expected_time_requests['FacilityID_' + str(facID) + '_Officer_Nursing_and_Midwifery'] | ||
clinical_task_time = hsi1.expected_time_requests['FacilityID_' + str(facID) + '_Officer_Clinical'] | ||
|
||
# Check that first choice of task-shifting officer for Pharmacy is Nursing_and_Midwifery, and get their factor | ||
assert 'Nursing_and_Midwifery' == sim.modules['HealthSystem'].global_task_shifting.get('Pharmacy')[0][0] | ||
nursing_task_shift_factor = sim.modules['HealthSystem'].global_task_shifting.get('Pharmacy')[1][0] | ||
|
||
# Number of appts that want to see delivered if pharmacy time is set to zero, | ||
# clinical time is set to perform 50 appts, and nursing time is set to perform 50 appts including | ||
# both nursing and pharmacy tasks. | ||
Ntarget = 50 | ||
|
||
sim.modules['HealthSystem']._daily_capabilities['FacilityID_' + str(facID) + '_Officer_Pharmacy'] = 0.0 | ||
sim.modules['HealthSystem']._daily_capabilities['FacilityID_' + str(facID) + '_Officer_Clinical'] = Ntarget*(clinical_task_time) | ||
sim.modules['HealthSystem']._daily_capabilities['FacilityID_' + str(facID) + '_Officer_Nursing_and_Midwifery'] = Ntarget*(nursing_task_time + nursing_task_shift_factor*pharmacy_task_time) | ||
|
||
# Run healthsystemscheduler | ||
healthsystemscheduler.apply(sim.population) | ||
|
||
# read the results | ||
output = parse_log_file(sim.log_filepath, level=logging.DEBUG) | ||
hs_output = output['tlo.methods.healthsystem']['HSI_Event'] | ||
|
||
# Check that all events could run, even if Pharmacy capabilities were set to zero, | ||
# and that when task-shifting first option (nurses) where always preferentially chosen over second | ||
# one (clinicians) | ||
assert hs_output['did_run'].sum() == Ntarget | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To strengthen the test and see the impact of task-shifting versus no task-shifting, I think it would be good to repeat this for with/without global task-shifting so we can see the difference. |
||
|
||
@pytest.mark.slow | ||
def test_which_hsi_can_run(seed): | ||
"""This test confirms whether, and how, HSI with each Appointment Type can run at each facility, under the | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why a module keyword argument and a module Parameter? I think best to stick to it being ONLY a Parameter, if possible.