Coverage for tasks/contactlog.py: 78%

36 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-15 14:23 +0100

1""" 

2camcops_server/tasks/contactlog.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""" 

27 

28from typing import List, Optional 

29 

30from cardinal_pythonlib.datetimefunc import format_datetime, get_duration_h_m 

31import cardinal_pythonlib.rnc_web as ws 

32from pendulum import DateTime as Pendulum 

33from sqlalchemy.orm import Mapped, mapped_column 

34from sqlalchemy.sql.sqltypes import UnicodeText 

35 

36from camcops_server.cc_modules.cc_constants import CssClass, DateFormat 

37from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo 

38from camcops_server.cc_modules.cc_html import ( 

39 italic, 

40 get_yes_no_none, 

41 tr, 

42 tr_qa, 

43) 

44from camcops_server.cc_modules.cc_request import CamcopsRequest 

45from camcops_server.cc_modules.cc_sqla_coltypes import ( 

46 mapped_camcops_column, 

47 BIT_CHECKER, 

48 PendulumDateTimeAsIsoTextColType, 

49) 

50from camcops_server.cc_modules.cc_task import ( 

51 Task, 

52 TaskHasClinicianMixin, 

53 TaskHasPatientMixin, 

54) 

55 

56 

57# ============================================================================= 

58# ContactLog 

59# ============================================================================= 

60 

61 

62class ContactLog(TaskHasClinicianMixin, TaskHasPatientMixin, Task): # type: ignore[misc] # noqa: E501 

63 """ 

64 Server implementation of the ContactLog task. 

65 """ 

66 

67 __tablename__ = "contactlog" 

68 shortname = "ContactLog" 

69 info_filename_stem = "clinical" 

70 

71 location: Mapped[Optional[str]] = mapped_column( 

72 UnicodeText, comment="Location" 

73 ) 

74 start: Mapped[Optional[Pendulum]] = mapped_column( 

75 PendulumDateTimeAsIsoTextColType, 

76 comment="Date/time that contact started", 

77 ) 

78 end: Mapped[Optional[Pendulum]] = mapped_column( 

79 "end", 

80 PendulumDateTimeAsIsoTextColType, 

81 comment="Date/time that contact ended", 

82 ) 

83 patient_contact: Mapped[Optional[int]] = mapped_camcops_column( 

84 permitted_value_checker=BIT_CHECKER, 

85 comment="Patient contact involved (0 no, 1 yes)?", 

86 ) 

87 staff_liaison: Mapped[Optional[int]] = mapped_camcops_column( 

88 permitted_value_checker=BIT_CHECKER, 

89 comment="Liaison with staff involved (0 no, 1 yes)?", 

90 ) 

91 other_liaison: Mapped[Optional[int]] = mapped_camcops_column( 

92 permitted_value_checker=BIT_CHECKER, 

93 comment="Liaison with others (e.g. family) involved (0 no, 1 yes)?", 

94 ) 

95 comment: Mapped[Optional[str]] = mapped_column( 

96 "comment", UnicodeText, comment="Comment" 

97 ) 

98 

99 @staticmethod 

100 def longname(req: "CamcopsRequest") -> str: 

101 _ = req.gettext 

102 return _("Clinical contact log") 

103 

104 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]: 

105 if not self.is_complete(): 

106 return CTV_INCOMPLETE 

107 contact_type = "Patient" if self.patient_contact else "Non-patient" 

108 return [ 

109 CtvInfo( 

110 content=( 

111 f"{contact_type} contact. Duration (hours:minutes) " 

112 f"{get_duration_h_m(self.start, self.end)}." 

113 ) 

114 ) 

115 ] 

116 

117 def is_complete(self) -> bool: 

118 return ( 

119 self.start is not None 

120 and self.end is not None 

121 and self.field_contents_valid() 

122 ) 

123 

124 def get_task_html(self, req: CamcopsRequest) -> str: 

125 return f""" 

126 <table class="{CssClass.TASKDETAIL}"> 

127 <tr> 

128 <td width="33%">Location:</td> 

129 <td width="67%"><b>{ws.webify(self.location)}</b></td> 

130 </tr> 

131 {tr_qa("Start:", format_datetime(self.start, 

132 DateFormat.SHORT_DATETIME, 

133 None))} 

134 {tr_qa("End:", format_datetime(self.end, 

135 DateFormat.SHORT_DATETIME, 

136 None))} 

137 {tr(italic("Calculated duration (hours:minutes)"), 

138 italic(get_duration_h_m(self.start, self.end)))} 

139 {tr_qa("Patient contact?", 

140 get_yes_no_none(req, self.patient_contact))} 

141 {tr_qa("Staff liaison?", 

142 get_yes_no_none(req, self.staff_liaison))} 

143 {tr_qa("Other liaison?", 

144 get_yes_no_none(req, self.other_liaison))} 

145 {tr_qa("Comment:", self.comment)} 

146 </table> 

147 """