Coverage for cc_modules/cc_testproviders.py: 94%
52 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 15:51 +0100
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 15:51 +0100
1"""
2camcops_server/cc_modules/cc_testproviders.py
4===============================================================================
6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
9 This file is part of CamCOPS.
11 CamCOPS is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 CamCOPS is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
24===============================================================================
26**Faker test data providers.**
28There may be some interest in a Faker Medical community provider if we felt it
29was worth the effort.
31https://github.com/joke2k/faker/issues/1142
33See also duplicate functionality in CRATE: crate_anon/testing/providers.py
35"""
37import datetime
39from cardinal_pythonlib.nhs import generate_random_nhs_number
40from faker import Faker
41from faker.providers import BaseProvider
42import pendulum
43from pendulum import DateTime as Pendulum
44from typing import Any, List
47class NhsNumberProvider(BaseProvider):
48 @staticmethod
49 def nhs_number() -> int:
50 return generate_random_nhs_number()
53class ChoiceProvider(BaseProvider):
54 def random_choice(self, choices: List, **kwargs: Any) -> Any:
55 """
56 Given a list of choices return a random value
57 """
58 choices = self.generator.random.choices(choices, **kwargs)
60 return choices[0]
63# No one is born after this
64_max_birth_datetime = Pendulum(year=2000, month=1, day=1, hour=9)
67class ConsistentDateOfBirthProvider(BaseProvider):
68 """
69 Faker date_of_birth calculates from the current time so gives different
70 results on different days.
71 """
73 def consistent_date_of_birth(self) -> datetime.datetime:
74 return self.generator.date_between_dates(
75 date_start=pendulum.date(1900, 1, 1),
76 date_end=_max_birth_datetime,
77 )
80class ForenameProvider(BaseProvider):
81 """
82 Return a forename given the sex of the person
83 """
85 def forename(self, sex: str) -> str:
86 if sex == "M":
87 return self.generator.first_name_male()
89 if sex == "F":
90 return self.generator.first_name_female()
92 return self.generator.first_name()[:1]
95class HeightProvider(BaseProvider):
96 def height_m(self) -> float:
97 """
98 Return a random patient height in metres
99 """
101 return float(self.generator.random_int(min=145, max=191) / 100.0)
104class MassProvider(BaseProvider):
105 def mass_kg(self) -> float:
106 """
107 Return a random patient mass in kilograms
108 """
110 return float(self.generator.random_int(min=400, max=1000) / 10.0)
113class SexProvider(ChoiceProvider):
114 """
115 Return a random sex, with realistic distribution.
116 """
118 def sex(self) -> str:
119 return self.random_choice(["M", "F", "X"], weights=[49.8, 49.8, 0.4])
122class ValidPhoneNumberProvider(BaseProvider):
123 """
124 Return a random mobile phone number
125 """
127 # The default Faker phone_number provider for en_GB uses
128 # https://www.ofcom.org.uk/phones-telecoms-and-internet/information-for-industry/numbering/numbers-for-drama # noqa: E501
129 # 07700 900000 to 900999 reserved for TV and Radio drama purposes
130 # but unfortunately the phonenumbers library considers these invalid.
131 def valid_phone_number(self) -> str:
132 number = self.generator.random_int(min=7000000000, max=7999999999)
134 return f"+44{number}"
137class WaistProvider(BaseProvider):
138 """
139 Return a random waist circumference in centimetres
140 """
142 def waist_cm(self) -> float:
143 return float(self.generator.random_int(min=40, max=130))
146def register_all_providers(fake: Faker) -> None:
147 fake.add_provider(ChoiceProvider)
148 fake.add_provider(ConsistentDateOfBirthProvider)
149 fake.add_provider(ForenameProvider)
150 fake.add_provider(HeightProvider)
151 fake.add_provider(MassProvider)
152 fake.add_provider(NhsNumberProvider)
153 fake.add_provider(ValidPhoneNumberProvider)
154 fake.add_provider(WaistProvider)
155 fake.add_provider(SexProvider)