Coverage for crateweb/extra/salutation.py: 15%
40 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-08-27 10:34 -0500
« prev ^ index » next coverage.py v7.8.0, created at 2025-08-27 10:34 -0500
1"""
2crate_anon/crateweb/extra/salutation.py
4===============================================================================
6 Copyright (C) 2015, University of Cambridge, Department of Psychiatry.
7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
9 This file is part of CRATE.
11 CRATE 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 CRATE 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 CRATE. If not, see <https://www.gnu.org/licenses/>.
24===============================================================================
26**Converts names to salutations and parses names into component parts.**
28"""
30from typing import Optional, Tuple
33# =============================================================================
34# Salutation and other forms of name/title generation
35# =============================================================================
38def title_forename_surname(
39 title: Optional[str],
40 forename: Optional[str],
41 surname: Optional[str],
42 always_title: bool = False,
43 sex: str = "",
44 assume_dr: bool = False,
45) -> str:
46 """
47 Used when reporting names. Returns a string of the format ``Title Forename
48 Surname``, as far as we can work it out.
50 Args:
51 title: title
52 forename: forename
53 surname: surname
54 always_title: if we don't know the title, guess one?
55 sex: ``"M"`` or ``"F"`` or other/unknown
56 assume_dr: assume the person has the title "Dr"?
58 Returns:
59 str: a string of a format like
61 .. code-block:: none
63 Prof. John Smith
64 John Smith
65 Prof. Smith
67 etc.
69 """
70 if always_title and not title:
71 title = salutation_default_title(sex, assume_dr)
72 return " ".join(filter(None, [title, forename, surname]))
75def forename_surname(forename: Optional[str], surname: Optional[str]) -> str:
76 """
77 For use when reporting names.
79 Args:
80 forename: forename
81 surname: surname
83 Returns:
84 str: a string of the style ``Forename Surname``
86 """
87 return " ".join(filter(None, [forename, surname]))
90def salutation_default_title(sex: str = "", assume_dr: bool = False) -> str:
91 """
92 Returns a guess as to someone's title.
94 Args:
95 sex: ``"M"`` or ``"F"`` or other/unknown
96 assume_dr: assume the person has the title "Dr"?
98 Returns:
99 a title
101 """
102 if assume_dr:
103 return "Dr"
104 if sex.upper() == "M":
105 return "Mr"
106 if sex.upper() == "F":
107 return "Ms"
108 # really stuck now
109 # https://en.wikipedia.org/wiki/Gender_neutral_title
110 return "Mx"
113def salutation(
114 title: Optional[str],
115 forename: Optional[str],
116 surname: Optional[str],
117 sex: str = "",
118 assume_dr: bool = False,
119) -> str:
120 """
121 For salutations: "Dear ..."
123 Args:
124 title: title
125 forename: forename
126 surname: surname
127 sex: ``"M"`` or ``"F"`` or other/unknown
128 assume_dr: assume the person has the title "Dr"?
130 Returns:
131 a salutation like ``Prof. Smith``
133 """
134 if not title:
135 title = salutation_default_title(sex, assume_dr)
136 if title.lower() == "sir": # frivolous!
137 return " ".join([title, forename])
138 return " ".join([title, surname])
141# =============================================================================
142# String parsing
143# =============================================================================
146def get_initial_surname_tuple_from_string(s: str) -> Tuple[str, str]:
147 """
148 Parses a name-like string into plausible parts. Try:
150 .. code-block:: python
152 get_initial_surname_tuple_from_string("AJ VAN DEN BERG")
153 get_initial_surname_tuple_from_string("VAN DEN BERG AJ")
154 get_initial_surname_tuple_from_string("J Smith")
155 get_initial_surname_tuple_from_string("J. Smith")
156 get_initial_surname_tuple_from_string("Smith J.")
157 get_initial_surname_tuple_from_string("Smith JKC")
158 get_initial_surname_tuple_from_string("Dr Bob Smith")
159 get_initial_surname_tuple_from_string("LINTON H C (PL)")
161 Returns:
162 tuple: ``initial, surname``
163 """
164 parts = s.split() if s else []
165 nparts = len(parts)
166 if nparts == 0:
167 return "", ""
168 elif "(" in s:
169 # something v. odd like "Linton H C (PL)", for Linton Health Centre
170 # partners or similar. We can't fix it, but...
171 return "", parts[0]
172 elif nparts == 1:
173 # hmm... assume "Smith"
174 return "", parts[0]
175 elif nparts == 2:
176 if len(parts[0]) < len(parts[1]):
177 # probably "J Smith"
178 return parts[0][0], parts[1]
179 else:
180 # probably "Smith JKC"
181 return parts[1][0], parts[0]
182 else:
183 # Lots of parts.
184 if parts[0].lower() == "dr":
185 parts = parts[1:]
186 nparts -= 1
187 if len(parts[0]) < len(parts[-1]):
188 # probably "AJ VAN DEN BERG"
189 return parts[0][0], " ".join(parts[1:])
190 else:
191 # probably "VAN DEN BERG AJ"
192 return parts[-1][0], " ".join(parts[:-1])