Coverage for cc_modules/cc_testproviders.py: 94%

1"""

2 camcops_server/cc_modules/cc_testproviders.py

3

4===============================================================================

5

6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.

7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

8

9 This file is part of CamCOPS.

10

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.

15

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.

20

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/>.

23

24===============================================================================

25

26**Faker test data providers.**

27

28 There may be some interest in a Faker Medical community provider if we felt it

29 was worth the effort.

30

31 https://github.com/joke2k/faker/issues/1142

32

33 See also duplicate functionality in CRATE: crate_anon/testing/providers.py

34

35"""

36

37 import datetime

38

39 from cardinal_pythonlib.nhs import generate_random_nhs_number

40 from faker import Faker

41 from faker.providers import BaseProvider

42 import pendulum

43 from pendulum import DateTime as Pendulum

44 from typing import Any, List

45

46

47 class NhsNumberProvider(BaseProvider):

48 @staticmethod

49 def nhs_number() -> int:

50 return generate_random_nhs_number()

51

52

53 class 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)

59

60 return choices[0]

61

62

63# No one is born after this

64 _max_birth_datetime = Pendulum(year=2000, month=1, day=1, hour=9)

65

66

67 class ConsistentDateOfBirthProvider(BaseProvider):

68 """

69 Faker date_of_birth calculates from the current time so gives different

70 results on different days.

71 """

72

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 )

78

79

80 class ForenameProvider(BaseProvider):

81 """

82 Return a forename given the sex of the person

83 """

84

85 def forename(self, sex: str) -> str:

86 if sex == "M":

87 return self.generator.first_name_male()

88

89 if sex == "F":

90 return self.generator.first_name_female()

91

92 return self.generator.first_name()[:1]

93

94

95 class HeightProvider(BaseProvider):

96 def height_m(self) -> float:

97 """

98 Return a random patient height in metres

99 """

100

101 return float(self.generator.random_int(min=145, max=191) / 100.0)

102

103

104 class MassProvider(BaseProvider):

105 def mass_kg(self) -> float:

106 """

107 Return a random patient mass in kilograms

108 """

109

110 return float(self.generator.random_int(min=400, max=1000) / 10.0)

111

112

113 class SexProvider(ChoiceProvider):

114 """

115 Return a random sex, with realistic distribution.

116 """

117

118 def sex(self) -> str:

119 return self.random_choice(["M", "F", "X"], weights=[49.8, 49.8, 0.4])

120

121

122 class ValidPhoneNumberProvider(BaseProvider):

123 """

124 Return a random mobile phone number

125 """

126

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)

133

134 return f"+44{number}"

135

136

137 class WaistProvider(BaseProvider):

138 """

139 Return a random waist circumference in centimetres

140 """

141

142 def waist_cm(self) -> float:

143 return float(self.generator.random_int(min=40, max=130))

144

145

146 def 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)