Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/cc_modules/cc_idnumdef.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

12 CamCOPS is free software: you can redistribute it and/or modify 

13 it under the terms of the GNU General Public License as published by 

14 the Free Software Foundation, either version 3 of the License, or 

15 (at your option) any later version. 

16 

17 CamCOPS is distributed in the hope that it will be useful, 

18 but WITHOUT ANY WARRANTY; without even the implied warranty of 

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

22 You should have received a copy of the GNU General Public License 

23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>. 

24 

25=============================================================================== 

26 

27**ID number definitions.** 

28 

29""" 

30 

31import logging 

32from typing import List, Optional, Tuple, TYPE_CHECKING 

33 

34from cardinal_pythonlib.logs import BraceStyleAdapter 

35from cardinal_pythonlib.nhs import is_valid_nhs_number 

36from cardinal_pythonlib.reprfunc import simple_repr 

37from sqlalchemy.orm import Session as SqlASession 

38from sqlalchemy.sql.schema import Column 

39from sqlalchemy.sql.sqltypes import Integer, String 

40 

41from camcops_server.cc_modules.cc_sqla_coltypes import ( 

42 HL7AssigningAuthorityType, 

43 HL7IdTypeType, 

44 IdDescriptorColType, 

45) 

46from camcops_server.cc_modules.cc_sqlalchemy import Base 

47 

48if TYPE_CHECKING: 

49 from camcops_server.cc_modules.cc_request import CamcopsRequest 

50 

51log = BraceStyleAdapter(logging.getLogger(__name__)) 

52 

53 

54# ============================================================================= 

55# ID number validation 

56# ============================================================================= 

57 

58ID_NUM_VALIDATION_METHOD_MAX_LEN = 50 

59 

60 

61class IdNumValidationMethod(object): 

62 """ 

63 Constants representing ways that CamCOPS knows to validate ID numbers. 

64 """ 

65 NONE = "" # special 

66 UK_NHS_NUMBER = "uk_nhs_number" 

67 

68 

69ID_NUM_VALIDATION_METHOD_CHOICES = ( 

70 # for HTML forms: value, description 

71 (IdNumValidationMethod.NONE, "None"), 

72 (IdNumValidationMethod.UK_NHS_NUMBER, "UK NHS number"), 

73) 

74 

75 

76def validate_id_number(req: "CamcopsRequest", 

77 idnum: Optional[int], method: str) -> Tuple[bool, str]: 

78 """ 

79 Validates an ID number according to a method (as per 

80 :class:`IdNumValidationMethod`). 

81 

82 If the number is ``None``, that's valid (that's an ID policy failure, not 

83 a number validation failure). If ``method`` is falsy, that's also valid 

84 (no constraints). 

85 

86 Args: 

87 req: a :class:`camcops_server.cc_modules.cc_request.CamcopsRequest` 

88 idnum: the ID number, or ``None`` 

89 method: 

90 

91 Returns: 

92 tuple: ``valid, why_invalid`` where ``valid`` is ``bool`` and 

93 ``why_invalid`` is ``str``. 

94 

95 """ 

96 _ = req.gettext 

97 if idnum is None or not method: 

98 return True, "" 

99 if not isinstance(idnum, int): 

100 return False, _("Not an integer") 

101 if method == IdNumValidationMethod.UK_NHS_NUMBER: 

102 if is_valid_nhs_number(idnum): 

103 return True, "" 

104 else: 

105 return False, _("Invalid UK NHS number") 

106 return False, _("Unknown validation method") 

107 

108 

109# ============================================================================= 

110# IdNumDefinition 

111# ============================================================================= 

112 

113class IdNumDefinition(Base): 

114 """ 

115 Represents an ID number definition. 

116 """ 

117 __tablename__ = "_idnum_definitions" 

118 

119 which_idnum = Column( 

120 "which_idnum", Integer, primary_key=True, index=True, 

121 comment="Which of the server's ID numbers is this?" 

122 ) 

123 description = Column( 

124 "description", IdDescriptorColType, 

125 comment="Full description of the ID number" 

126 ) 

127 short_description = Column( 

128 "short_description", IdDescriptorColType, 

129 comment="Short description of the ID number" 

130 ) 

131 hl7_id_type = Column( 

132 "hl7_id_type", HL7IdTypeType, 

133 comment="HL7: Identifier Type code: 'a code corresponding to the type " 

134 "of identifier. In some cases, this code may be used as a " 

135 "qualifier to the \"Assigning Authority\" component.'" 

136 ) 

137 hl7_assigning_authority = Column( 

138 "hl7_assigning_authority", HL7AssigningAuthorityType, 

139 comment="HL7: Assigning Authority for ID number (unique name of the " 

140 "system/organization/agency/department that creates the data)." 

141 ) 

142 validation_method = Column( 

143 "validation_method", String(length=ID_NUM_VALIDATION_METHOD_MAX_LEN), 

144 comment="Optional validation method" 

145 ) 

146 

147 def __init__(self, 

148 which_idnum: int = None, 

149 description: str = "", 

150 short_description: str = "", 

151 hl7_id_type: str = "", 

152 hl7_assigning_authority: str = "", 

153 validation_method: str = ""): 

154 # We permit a "blank" constructor for automatic copying, e.g. merge_db. 

155 self.which_idnum = which_idnum 

156 self.description = description 

157 self.short_description = short_description 

158 self.hl7_id_type = hl7_id_type 

159 self.hl7_assigning_authority = hl7_assigning_authority 

160 self.validation_method = validation_method 

161 

162 def __repr__(self) -> str: 

163 return simple_repr(self, 

164 ["which_idnum", "description", "short_description"], 

165 with_addr=False) 

166 

167 

168# ============================================================================= 

169# Retrieving all IdNumDefinition objects 

170# ============================================================================= 

171 

172def get_idnum_definitions(dbsession: SqlASession) -> List[IdNumDefinition]: 

173 """ 

174 Get all ID number definitions from the database, in order. 

175 """ 

176 return list( 

177 dbsession.query(IdNumDefinition) 

178 .order_by(IdNumDefinition.which_idnum) 

179 )