Coverage for cc_modules/cc_idnumdef.py : 73%

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
3"""
4camcops_server/cc_modules/cc_idnumdef.py
6===============================================================================
8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com).
10 This file is part of CamCOPS.
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.
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.
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/>.
25===============================================================================
27**ID number definitions.**
29"""
31import logging
32from typing import List, Optional, Tuple, TYPE_CHECKING
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
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
48if TYPE_CHECKING:
49 from camcops_server.cc_modules.cc_request import CamcopsRequest
51log = BraceStyleAdapter(logging.getLogger(__name__))
54# =============================================================================
55# ID number validation
56# =============================================================================
58ID_NUM_VALIDATION_METHOD_MAX_LEN = 50
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"
69ID_NUM_VALIDATION_METHOD_CHOICES = (
70 # for HTML forms: value, description
71 (IdNumValidationMethod.NONE, "None"),
72 (IdNumValidationMethod.UK_NHS_NUMBER, "UK NHS number"),
73)
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`).
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).
86 Args:
87 req: a :class:`camcops_server.cc_modules.cc_request.CamcopsRequest`
88 idnum: the ID number, or ``None``
89 method:
91 Returns:
92 tuple: ``valid, why_invalid`` where ``valid`` is ``bool`` and
93 ``why_invalid`` is ``str``.
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")
109# =============================================================================
110# IdNumDefinition
111# =============================================================================
113class IdNumDefinition(Base):
114 """
115 Represents an ID number definition.
116 """
117 __tablename__ = "_idnum_definitions"
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 )
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
162 def __repr__(self) -> str:
163 return simple_repr(self,
164 ["which_idnum", "description", "short_description"],
165 with_addr=False)
168# =============================================================================
169# Retrieving all IdNumDefinition objects
170# =============================================================================
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 )