Source code for camcops_server.cc_modules.cc_idnumdef

#!/usr/bin/env python

"""
camcops_server/cc_modules/cc_idnumdef.py

===============================================================================

    Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
    Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

    This file is part of CamCOPS.

    CamCOPS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CamCOPS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.

===============================================================================

**ID number definitions.**

"""

import logging
from typing import List, Optional, Tuple, TYPE_CHECKING

from cardinal_pythonlib.logs import BraceStyleAdapter
from cardinal_pythonlib.nhs import is_valid_nhs_number
from cardinal_pythonlib.reprfunc import simple_repr
from sqlalchemy.orm import Session as SqlASession
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.sqltypes import Integer, String

from camcops_server.cc_modules.cc_pyramid import Routes
from camcops_server.cc_modules.cc_sqla_coltypes import (
    HL7AssigningAuthorityType,
    HL7IdTypeType,
    IdDescriptorColType,
    UrlColType,
)
from camcops_server.cc_modules.cc_sqlalchemy import Base

if TYPE_CHECKING:
    from camcops_server.cc_modules.cc_request import CamcopsRequest

log = BraceStyleAdapter(logging.getLogger(__name__))


# =============================================================================
# ID number validation
# =============================================================================

ID_NUM_VALIDATION_METHOD_MAX_LEN = 50


[docs]class IdNumValidationMethod(object): """ Constants representing ways that CamCOPS knows to validate ID numbers. """ NONE = "" # special UK_NHS_NUMBER = "uk_nhs_number"
ID_NUM_VALIDATION_METHOD_CHOICES = ( # for HTML forms: value, description (IdNumValidationMethod.NONE, "None"), (IdNumValidationMethod.UK_NHS_NUMBER, "UK NHS number"), )
[docs]def validate_id_number( req: "CamcopsRequest", idnum: Optional[int], method: str ) -> Tuple[bool, str]: """ Validates an ID number according to a method (as per :class:`IdNumValidationMethod`). If the number is ``None``, that's valid (that's an ID policy failure, not a number validation failure). If ``method`` is falsy, that's also valid (no constraints). Args: req: a :class:`camcops_server.cc_modules.cc_request.CamcopsRequest` idnum: the ID number, or ``None`` method: Returns: tuple: ``valid, why_invalid`` where ``valid`` is ``bool`` and ``why_invalid`` is ``str``. """ _ = req.gettext if idnum is None or not method: return True, "" if not isinstance(idnum, int): return False, _("Not an integer") if method == IdNumValidationMethod.UK_NHS_NUMBER: if is_valid_nhs_number(idnum): return True, "" else: return False, _("Invalid UK NHS number") return False, _("Unknown validation method")
# ============================================================================= # IdNumDefinition # =============================================================================
[docs]class IdNumDefinition(Base): """ Represents an ID number definition. """ __tablename__ = "_idnum_definitions" which_idnum = Column( "which_idnum", Integer, primary_key=True, index=True, comment="Which of the server's ID numbers is this?", ) description = Column( "description", IdDescriptorColType, comment="Full description of the ID number", ) short_description = Column( "short_description", IdDescriptorColType, comment="Short description of the ID number", ) hl7_id_type = Column( "hl7_id_type", HL7IdTypeType, comment="HL7: Identifier Type code: 'a code corresponding to the type " "of identifier. In some cases, this code may be used as a " 'qualifier to the "Assigning Authority" component.\'', ) hl7_assigning_authority = Column( "hl7_assigning_authority", HL7AssigningAuthorityType, comment="HL7: Assigning Authority for ID number (unique name of the " "system/organization/agency/department that creates the data).", ) validation_method = Column( "validation_method", String(length=ID_NUM_VALIDATION_METHOD_MAX_LEN), comment="Optional validation method", ) fhir_id_system = Column( "fhir_id_system", UrlColType, comment="FHIR external ID 'system' URL" ) def __init__( self, which_idnum: int = None, description: str = "", short_description: str = "", hl7_id_type: str = "", hl7_assigning_authority: str = "", validation_method: str = "", fhir_id_system: str = "", ): # We permit a "blank" constructor for automatic copying, e.g. merge_db. self.which_idnum = which_idnum self.description = description self.short_description = short_description self.hl7_id_type = hl7_id_type self.hl7_assigning_authority = hl7_assigning_authority self.validation_method = validation_method self.fhir_id_system = fhir_id_system def __repr__(self) -> str: return simple_repr( self, ["which_idnum", "description", "short_description"], with_addr=False, ) def _camcops_default_fhir_id_system(self, req: "CamcopsRequest") -> str: """ The built-in FHIR ID system URL that we'll use if the user hasn't specified one for the selected ID number type. """ return req.route_url( Routes.FHIR_PATIENT_ID_SYSTEM, which_idnum=self.which_idnum ) # path will be e.g. /fhir_patient_id_system/3
[docs] def effective_fhir_id_system(self, req: "CamcopsRequest") -> str: """ If the user has set a FHIR ID system, return that. Otherwise, return a CamCOPS default. """ return self.fhir_id_system or self._camcops_default_fhir_id_system(req)
[docs] def verbose_fhir_id_system(self, req: "CamcopsRequest") -> str: """ Returns a human-readable description of the FHIR ID system in effect, in HTML form. """ _ = req.gettext if self.fhir_id_system: prefix = "" url = self.fhir_id_system else: prefix = _("Default:") + " " url = self._camcops_default_fhir_id_system(req) return f'{prefix} <a href="{url}">{url}</a>'
# ============================================================================= # Retrieving all IdNumDefinition objects # =============================================================================
[docs]def get_idnum_definitions(dbsession: SqlASession) -> List[IdNumDefinition]: """ Get all ID number definitions from the database, in order. """ return list( dbsession.query(IdNumDefinition).order_by(IdNumDefinition.which_idnum) )