Coverage for tasks/icd10specpd.py : 44%

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/tasks/icd10specpd.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"""
29from typing import Any, Dict, List, Optional, Tuple, Type
31from cardinal_pythonlib.datetimefunc import format_datetime
32import cardinal_pythonlib.rnc_web as ws
33from cardinal_pythonlib.stringfunc import strseq
34from cardinal_pythonlib.typetests import is_false
35from sqlalchemy.ext.declarative import DeclarativeMeta
36from sqlalchemy.sql.schema import Column
37from sqlalchemy.sql.sqltypes import Boolean, Date, UnicodeText
39from camcops_server.cc_modules.cc_constants import (
40 CssClass,
41 DateFormat,
42 ICD10_COPYRIGHT_DIV,
43 PV,
44)
45from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo
46from camcops_server.cc_modules.cc_db import add_multiple_columns
47from camcops_server.cc_modules.cc_html import (
48 answer,
49 get_yes_no_none,
50 get_yes_no_unknown,
51 subheading_spanning_two_columns,
52 tr_qa,
53)
54from camcops_server.cc_modules.cc_request import CamcopsRequest
55from camcops_server.cc_modules.cc_sqla_coltypes import (
56 BIT_CHECKER,
57 CamcopsColumn,
58)
59from camcops_server.cc_modules.cc_string import AS
60from camcops_server.cc_modules.cc_summaryelement import SummaryElement
61from camcops_server.cc_modules.cc_task import (
62 Task,
63 TaskHasClinicianMixin,
64 TaskHasPatientMixin,
65)
68# =============================================================================
69# Icd10SpecPD
70# =============================================================================
72def ctv_info_pd(req: CamcopsRequest,
73 condition: str, has_it: Optional[bool]) -> CtvInfo:
74 return CtvInfo(content=condition + ": " + get_yes_no_unknown(req, has_it))
77class Icd10SpecPDMetaclass(DeclarativeMeta):
78 # noinspection PyInitNewSignature
79 def __init__(cls: Type['Icd10SpecPD'],
80 name: str,
81 bases: Tuple[Type, ...],
82 classdict: Dict[str, Any]) -> None:
83 add_multiple_columns(
84 cls, "g", 1, cls.N_GENERAL, Boolean,
85 pv=PV.BIT,
86 comment_fmt="G{n}: {s}",
87 comment_strings=["pathological 1", "pervasive",
88 "pathological 2", "persistent",
89 "primary 1", "primary 2"]
90 )
91 add_multiple_columns(
92 cls, "g1_", 1, cls.N_GENERAL_1, Boolean,
93 pv=PV.BIT,
94 comment_fmt="G1{n}: {s}",
95 comment_strings=["cognition", "affectivity",
96 "impulse control", "interpersonal"]
97 )
98 add_multiple_columns(
99 cls, "paranoid", 1, cls.N_PARANOID, Boolean,
100 pv=PV.BIT,
101 comment_fmt="Paranoid ({n}): {s}",
102 comment_strings=["sensitive", "grudges", "suspicious",
103 "personal rights", "sexual jealousy",
104 "self-referential", "conspiratorial"]
105 )
106 add_multiple_columns(
107 cls, "schizoid", 1, cls.N_SCHIZOID,
108 Boolean,
109 pv=PV.BIT,
110 comment_fmt="Schizoid ({n}): {s}",
111 comment_strings=["little pleasure",
112 "cold/detached",
113 "limited capacity for warmth",
114 "indifferent to praise/criticism",
115 "little interest in sex",
116 "solitary",
117 "fantasy/introspection",
118 "0/1 close friends/confidants",
119 "insensitive to social norms"]
120 )
121 add_multiple_columns(
122 cls, "dissocial", 1, cls.N_DISSOCIAL, Boolean,
123 pv=PV.BIT,
124 comment_fmt="Dissocial ({n}): {s}",
125 comment_strings=["unconcern", "irresponsibility",
126 "incapacity to maintain relationships",
127 "low tolerance to frustration",
128 "incapacity for guilt",
129 "prone to blame others"]
130 )
131 add_multiple_columns(
132 cls, "eu", 1, cls.N_EU, Boolean,
133 pv=PV.BIT,
134 comment_fmt="Emotionally unstable ({n}): {s}",
135 comment_strings=["act without considering consequences",
136 "quarrelsome", "outbursts of anger",
137 "can't maintain actions with immediate reward",
138 "unstable/capricious mood",
139 "uncertain self-image",
140 "intense/unstable relationships",
141 "avoids abandonment",
142 "threats/acts of self-harm",
143 "feelings of emptiness"]
144 )
145 add_multiple_columns(
146 cls, "histrionic", 1, cls.N_HISTRIONIC, Boolean,
147 pv=PV.BIT,
148 comment_fmt="Histrionic ({n}): {s}",
149 comment_strings=["theatricality",
150 "suggestibility",
151 "shallow/labile affect",
152 "centre of attention",
153 "inappropriately seductive",
154 "concerned with attractiveness"]
155 )
156 add_multiple_columns(
157 cls, "anankastic", 1, cls.N_ANANKASTIC, Boolean,
158 pv=PV.BIT,
159 comment_fmt="Anankastic ({n}): {s}",
160 comment_strings=["doubt/caution",
161 "preoccupation with details",
162 "perfectionism",
163 "excessively conscientious",
164 "preoccupied with productivity",
165 "excessive pedantry",
166 "rigid/stubborn",
167 "require others do things specific way"]
168 )
169 add_multiple_columns(
170 cls, "anxious", 1, cls.N_ANXIOUS, Boolean,
171 pv=PV.BIT,
172 comment_fmt="Anxious ({n}), {s}",
173 comment_strings=["tension/apprehension",
174 "preoccupied with criticism/rejection",
175 "won't get involved unless certain liked",
176 "need for security restricts lifestyle",
177 "avoidance of interpersonal contact"]
178 )
179 add_multiple_columns(
180 cls, "dependent", 1, cls.N_DEPENDENT, Boolean,
181 pv=PV.BIT,
182 comment_fmt="Dependent ({n}): {s}",
183 comment_strings=["others decide",
184 "subordinate needs to those of others",
185 "unwilling to make reasonable demands",
186 "uncomfortable/helpless when alone",
187 "fears of being left to oneself",
188 "everyday decisions require advice/reassurance"]
189 )
190 super().__init__(name, bases, classdict)
193class Icd10SpecPD(TaskHasClinicianMixin, TaskHasPatientMixin, Task,
194 metaclass=Icd10SpecPDMetaclass):
195 """
196 Server implementation of the ICD10-PD task.
197 """
198 __tablename__ = "icd10specpd"
199 shortname = "ICD10-PD"
201 date_pertains_to = Column(
202 "date_pertains_to", Date,
203 comment="Date the assessment pertains to"
204 )
205 comments = Column(
206 "comments", UnicodeText,
207 comment="Clinician's comments"
208 )
209 skip_paranoid = CamcopsColumn(
210 "skip_paranoid", Boolean,
211 permitted_value_checker=BIT_CHECKER,
212 comment="Skip questions for paranoid PD?"
213 )
214 skip_schizoid = CamcopsColumn(
215 "skip_schizoid", Boolean,
216 permitted_value_checker=BIT_CHECKER,
217 comment="Skip questions for schizoid PD?"
218 )
219 skip_dissocial = CamcopsColumn(
220 "skip_dissocial", Boolean,
221 permitted_value_checker=BIT_CHECKER,
222 comment="Skip questions for dissocial PD?"
223 )
224 skip_eu = CamcopsColumn(
225 "skip_eu", Boolean,
226 permitted_value_checker=BIT_CHECKER,
227 comment="Skip questions for emotionally unstable PD?"
228 )
229 skip_histrionic = CamcopsColumn(
230 "skip_histrionic", Boolean,
231 permitted_value_checker=BIT_CHECKER,
232 comment="Skip questions for histrionic PD?"
233 )
234 skip_anankastic = CamcopsColumn(
235 "skip_anankastic", Boolean,
236 permitted_value_checker=BIT_CHECKER,
237 comment="Skip questions for anankastic PD?"
238 )
239 skip_anxious = CamcopsColumn(
240 "skip_anxious", Boolean,
241 permitted_value_checker=BIT_CHECKER,
242 comment="Skip questions for anxious PD?"
243 )
244 skip_dependent = CamcopsColumn(
245 "skip_dependent", Boolean,
246 permitted_value_checker=BIT_CHECKER,
247 comment="Skip questions for dependent PD?"
248 )
249 other_pd_present = CamcopsColumn(
250 "other_pd_present", Boolean,
251 permitted_value_checker=BIT_CHECKER,
252 comment="Is another personality disorder present?"
253 )
254 vignette = Column(
255 "vignette", UnicodeText,
256 comment="Vignette"
257 )
259 N_GENERAL = 6
260 N_GENERAL_1 = 4
261 N_PARANOID = 7
262 N_SCHIZOID = 9
263 N_DISSOCIAL = 6
264 N_EU = 10
265 N_EUPD_I = 5
266 N_HISTRIONIC = 6
267 N_ANANKASTIC = 8
268 N_ANXIOUS = 5
269 N_DEPENDENT = 6
271 GENERAL_FIELDS = strseq("g", 1, N_GENERAL)
272 GENERAL_1_FIELDS = strseq("g1_", 1, N_GENERAL_1)
273 PARANOID_FIELDS = strseq("paranoid", 1, N_PARANOID)
274 SCHIZOID_FIELDS = strseq("schizoid", 1, N_SCHIZOID)
275 DISSOCIAL_FIELDS = strseq("dissocial", 1, N_DISSOCIAL)
276 EU_FIELDS = strseq("eu", 1, N_EU)
277 EUPD_I_FIELDS = strseq("eu", 1, N_EUPD_I) # impulsive
278 EUPD_B_FIELDS = strseq("eu", N_EUPD_I + 1, N_EU) # borderline
279 HISTRIONIC_FIELDS = strseq("histrionic", 1, N_HISTRIONIC)
280 ANANKASTIC_FIELDS = strseq("anankastic", 1, N_ANANKASTIC)
281 ANXIOUS_FIELDS = strseq("anxious", 1, N_ANXIOUS)
282 DEPENDENT_FIELDS = strseq("dependent", 1, N_DEPENDENT)
284 @staticmethod
285 def longname(req: "CamcopsRequest") -> str:
286 _ = req.gettext
287 return _("ICD-10 criteria for specific personality disorders (F60)")
289 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
290 if not self.is_complete():
291 return CTV_INCOMPLETE
292 infolist = [ctv_info_pd(req,
293 self.wxstring(req, "meets_general_criteria"),
294 self.has_pd()),
295 ctv_info_pd(req,
296 self.wxstring(req, "paranoid_pd_title"),
297 self.has_paranoid_pd()),
298 ctv_info_pd(req,
299 self.wxstring(req, "schizoid_pd_title"),
300 self.has_schizoid_pd()),
301 ctv_info_pd(req,
302 self.wxstring(req, "dissocial_pd_title"),
303 self.has_dissocial_pd()),
304 ctv_info_pd(req,
305 self.wxstring(req, "eu_pd_i_title"),
306 self.has_eupd_i()),
307 ctv_info_pd(req,
308 self.wxstring(req, "eu_pd_b_title"),
309 self.has_eupd_b()),
310 ctv_info_pd(req,
311 self.wxstring(req, "histrionic_pd_title"),
312 self.has_histrionic_pd()),
313 ctv_info_pd(req,
314 self.wxstring(req, "anankastic_pd_title"),
315 self.has_anankastic_pd()),
316 ctv_info_pd(req,
317 self.wxstring(req, "anxious_pd_title"),
318 self.has_anxious_pd()),
319 ctv_info_pd(req,
320 self.wxstring(req, "dependent_pd_title"),
321 self.has_dependent_pd())]
322 return infolist
324 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
325 return self.standard_task_summary_fields() + [
326 SummaryElement(
327 name="meets_general_criteria", coltype=Boolean(),
328 value=self.has_pd(),
329 comment="Meets general criteria for personality disorder?"),
330 SummaryElement(
331 name="paranoid_pd", coltype=Boolean(),
332 value=self.has_paranoid_pd(),
333 comment="Meets criteria for paranoid PD?"),
334 SummaryElement(
335 name="schizoid_pd", coltype=Boolean(),
336 value=self.has_schizoid_pd(),
337 comment="Meets criteria for schizoid PD?"),
338 SummaryElement(
339 name="dissocial_pd", coltype=Boolean(),
340 value=self.has_dissocial_pd(),
341 comment="Meets criteria for dissocial PD?"),
342 SummaryElement(
343 name="eupd_i", coltype=Boolean(),
344 value=self.has_eupd_i(),
345 comment="Meets criteria for EUPD (impulsive type)?"),
346 SummaryElement(
347 name="eupd_b", coltype=Boolean(),
348 value=self.has_eupd_b(),
349 comment="Meets criteria for EUPD (borderline type)?"),
350 SummaryElement(
351 name="histrionic_pd", coltype=Boolean(),
352 value=self.has_histrionic_pd(),
353 comment="Meets criteria for histrionic PD?"),
354 SummaryElement(
355 name="anankastic_pd", coltype=Boolean(),
356 value=self.has_anankastic_pd(),
357 comment="Meets criteria for anankastic PD?"),
358 SummaryElement(
359 name="anxious_pd", coltype=Boolean(),
360 value=self.has_anxious_pd(),
361 comment="Meets criteria for anxious PD?"),
362 SummaryElement(
363 name="dependent_pd", coltype=Boolean(),
364 value=self.has_dependent_pd(),
365 comment="Meets criteria for dependent PD?"),
366 ]
368 # noinspection PyUnresolvedReferences
369 def is_pd_excluded(self) -> bool:
370 return (
371 is_false(self.g1) or
372 is_false(self.g2) or
373 is_false(self.g3) or
374 is_false(self.g4) or
375 is_false(self.g5) or
376 is_false(self.g6) or
377 (
378 self.all_fields_not_none(self.GENERAL_1_FIELDS) and
379 self.count_booleans(self.GENERAL_1_FIELDS) <= 1
380 )
381 )
383 def is_complete_general(self) -> bool:
384 return (
385 self.all_fields_not_none(self.GENERAL_FIELDS) and
386 self.all_fields_not_none(self.GENERAL_1_FIELDS)
387 )
389 def is_complete_paranoid(self) -> bool:
390 return self.all_fields_not_none(self.PARANOID_FIELDS)
392 def is_complete_schizoid(self) -> bool:
393 return self.all_fields_not_none(self.SCHIZOID_FIELDS)
395 def is_complete_dissocial(self) -> bool:
396 return self.all_fields_not_none(self.DISSOCIAL_FIELDS)
398 def is_complete_eu(self) -> bool:
399 return self.all_fields_not_none(self.EU_FIELDS)
401 def is_complete_histrionic(self) -> bool:
402 return self.all_fields_not_none(self.HISTRIONIC_FIELDS)
404 def is_complete_anankastic(self) -> bool:
405 return self.all_fields_not_none(self.ANANKASTIC_FIELDS)
407 def is_complete_anxious(self) -> bool:
408 return self.all_fields_not_none(self.ANXIOUS_FIELDS)
410 def is_complete_dependent(self) -> bool:
411 return self.all_fields_not_none(self.DEPENDENT_FIELDS)
413 # Meets criteria? These also return null for unknown.
414 def has_pd(self) -> Optional[bool]:
415 if self.is_pd_excluded():
416 return False
417 if not self.is_complete_general():
418 return None
419 return (
420 self.all_truthy(self.GENERAL_FIELDS) and
421 self.count_booleans(self.GENERAL_1_FIELDS) > 1
422 )
424 def has_paranoid_pd(self) -> Optional[bool]:
425 hpd = self.has_pd()
426 if not hpd:
427 return hpd
428 if not self.is_complete_paranoid():
429 return None
430 return self.count_booleans(self.PARANOID_FIELDS) >= 4
432 def has_schizoid_pd(self) -> Optional[bool]:
433 hpd = self.has_pd()
434 if not hpd:
435 return hpd
436 if not self.is_complete_schizoid():
437 return None
438 return self.count_booleans(self.SCHIZOID_FIELDS) >= 4
440 def has_dissocial_pd(self) -> Optional[bool]:
441 hpd = self.has_pd()
442 if not hpd:
443 return hpd
444 if not self.is_complete_dissocial():
445 return None
446 return self.count_booleans(self.DISSOCIAL_FIELDS) >= 3
448 # noinspection PyUnresolvedReferences
449 def has_eupd_i(self) -> Optional[bool]:
450 hpd = self.has_pd()
451 if not hpd:
452 return hpd
453 if not self.is_complete_eu():
454 return None
455 return (
456 self.count_booleans(self.EUPD_I_FIELDS) >= 3 and
457 self.eu2
458 )
460 def has_eupd_b(self) -> Optional[bool]:
461 hpd = self.has_pd()
462 if not hpd:
463 return hpd
464 if not self.is_complete_eu():
465 return None
466 return (
467 self.count_booleans(self.EUPD_I_FIELDS) >= 3 and
468 self.count_booleans(self.EUPD_B_FIELDS) >= 2
469 )
471 def has_histrionic_pd(self) -> Optional[bool]:
472 hpd = self.has_pd()
473 if not hpd:
474 return hpd
475 if not self.is_complete_histrionic():
476 return None
477 return self.count_booleans(self.HISTRIONIC_FIELDS) >= 4
479 def has_anankastic_pd(self) -> Optional[bool]:
480 hpd = self.has_pd()
481 if not hpd:
482 return hpd
483 if not self.is_complete_anankastic():
484 return None
485 return self.count_booleans(self.ANANKASTIC_FIELDS) >= 4
487 def has_anxious_pd(self) -> Optional[bool]:
488 hpd = self.has_pd()
489 if not hpd:
490 return hpd
491 if not self.is_complete_anxious():
492 return None
493 return self.count_booleans(self.ANXIOUS_FIELDS) >= 4
495 def has_dependent_pd(self) -> Optional[bool]:
496 hpd = self.has_pd()
497 if not hpd:
498 return hpd
499 if not self.is_complete_dependent():
500 return None
501 return self.count_booleans(self.DEPENDENT_FIELDS) >= 4
503 def is_complete(self) -> bool:
504 return (
505 self.date_pertains_to is not None and (
506 self.is_pd_excluded() or (
507 self.is_complete_general() and
508 (self.skip_paranoid or self.is_complete_paranoid()) and
509 (self.skip_schizoid or self.is_complete_schizoid()) and
510 (self.skip_dissocial or self.is_complete_dissocial()) and
511 (self.skip_eu or self.is_complete_eu()) and
512 (self.skip_histrionic or self.is_complete_histrionic()) and
513 (self.skip_anankastic or self.is_complete_anankastic()) and
514 (self.skip_anxious or self.is_complete_anxious()) and
515 (self.skip_dependent or self.is_complete_dependent())
516 )
517 ) and
518 self.field_contents_valid()
519 )
521 def pd_heading(self, req: CamcopsRequest, wstringname: str) -> str:
522 return f"""
523 <tr class="{CssClass.HEADING}">
524 <td colspan="2">{self.wxstring(req, wstringname)}</td>
525 </tr>
526 """
528 def pd_skiprow(self, req: CamcopsRequest, stem: str) -> str:
529 return self.get_twocol_bool_row(
530 req, "skip_" + stem, label=self.wxstring(req, "skip_this_pd"))
532 def pd_subheading(self, req: CamcopsRequest, wstringname: str) -> str:
533 return f"""
534 <tr class="{CssClass.SUBHEADING}">
535 <td colspan="2">{self.wxstring(req, wstringname)}</td>
536 </tr>
537 """
539 def pd_general_criteria_bits(self, req: CamcopsRequest) -> str:
540 return f"""
541 <tr>
542 <td>{self.wxstring(req, "general_criteria_must_be_met")}</td>
543 <td><i><b>{get_yes_no_unknown(req, self.has_pd())}</b></i></td>
544 </tr>
545 """
547 def pd_b_text(self, req: CamcopsRequest, wstringname: str) -> str:
548 return f"""
549 <tr>
550 <td>{self.wxstring(req, wstringname)}</td>
551 <td class="{CssClass.SUBHEADING}"></td>
552 </tr>
553 """
555 def pd_basic_row(self, req: CamcopsRequest, stem: str, i: int) -> str:
556 return self.get_twocol_bool_row_true_false(
557 req, stem + str(i), self.wxstring(req, stem + str(i)))
559 def standard_pd_html(self, req: CamcopsRequest, stem: str, n: int) -> str:
560 html = self.pd_heading(req, stem + "_pd_title")
561 html += self.pd_skiprow(req, stem)
562 html += self.pd_general_criteria_bits(req)
563 html += self.pd_b_text(req, stem + "_pd_B")
564 for i in range(1, n + 1):
565 html += self.pd_basic_row(req, stem, i)
566 return html
568 def get_task_html(self, req: CamcopsRequest) -> str:
569 h = self.get_standard_clinician_comments_block(req, self.comments)
570 h += f"""
571 <div class="{CssClass.SUMMARY}">
572 <table class="{CssClass.SUMMARY}">
573 """
574 h += self.get_is_complete_tr(req)
575 h += tr_qa(req.wappstring(AS.DATE_PERTAINS_TO),
576 format_datetime(self.date_pertains_to,
577 DateFormat.LONG_DATE, default=None))
578 h += tr_qa(self.wxstring(req, "meets_general_criteria"),
579 get_yes_no_none(req, self.has_pd()))
580 h += tr_qa(self.wxstring(req, "paranoid_pd_title"),
581 get_yes_no_none(req, self.has_paranoid_pd()))
582 h += tr_qa(self.wxstring(req, "schizoid_pd_title"),
583 get_yes_no_none(req, self.has_schizoid_pd()))
584 h += tr_qa(self.wxstring(req, "dissocial_pd_title"),
585 get_yes_no_none(req, self.has_dissocial_pd()))
586 h += tr_qa(self.wxstring(req, "eu_pd_i_title"),
587 get_yes_no_none(req, self.has_eupd_i()))
588 h += tr_qa(self.wxstring(req, "eu_pd_b_title"),
589 get_yes_no_none(req, self.has_eupd_b()))
590 h += tr_qa(self.wxstring(req, "histrionic_pd_title"),
591 get_yes_no_none(req, self.has_histrionic_pd()))
592 h += tr_qa(self.wxstring(req, "anankastic_pd_title"),
593 get_yes_no_none(req, self.has_anankastic_pd()))
594 h += tr_qa(self.wxstring(req, "anxious_pd_title"),
595 get_yes_no_none(req, self.has_anxious_pd()))
596 h += tr_qa(self.wxstring(req, "dependent_pd_title"),
597 get_yes_no_none(req, self.has_dependent_pd()))
599 h += f"""
600 </table>
601 </div>
602 <div>
603 <p><i>Vignette:</i></p>
604 <p>{answer(ws.webify(self.vignette),
605 default_for_blank_strings=True)}</p>
606 </div>
607 <table class="{CssClass.TASKDETAIL}">
608 <tr>
609 <th width="80%">Question</th>
610 <th width="20%">Answer</th>
611 </tr>
612 """
614 # General
615 h += subheading_spanning_two_columns(self.wxstring(req, "general"))
616 h += self.get_twocol_bool_row_true_false(
617 req, "g1", self.wxstring(req, "G1"))
618 h += self.pd_b_text(req, "G1b")
619 for i in range(1, Icd10SpecPD.N_GENERAL_1 + 1):
620 h += self.get_twocol_bool_row_true_false(
621 req, "g1_" + str(i), self.wxstring(req, "G1_" + str(i)))
622 for i in range(2, Icd10SpecPD.N_GENERAL + 1):
623 h += self.get_twocol_bool_row_true_false(
624 req, "g" + str(i), self.wxstring(req, "G" + str(i)))
626 # Paranoid, etc.
627 h += self.standard_pd_html(req, "paranoid", Icd10SpecPD.N_PARANOID)
628 h += self.standard_pd_html(req, "schizoid", Icd10SpecPD.N_SCHIZOID)
629 h += self.standard_pd_html(req, "dissocial", Icd10SpecPD.N_DISSOCIAL)
631 # EUPD is special
632 h += self.pd_heading(req, "eu_pd_title")
633 h += self.pd_skiprow(req, "eu")
634 h += self.pd_general_criteria_bits(req)
635 h += self.pd_subheading(req, "eu_pd_i_title")
636 h += self.pd_b_text(req, "eu_pd_i_B")
637 for i in range(1, Icd10SpecPD.N_EUPD_I + 1):
638 h += self.pd_basic_row(req, "eu", i)
639 h += self.pd_subheading(req, "eu_pd_b_title")
640 h += self.pd_b_text(req, "eu_pd_b_B")
641 for i in range(Icd10SpecPD.N_EUPD_I + 1, Icd10SpecPD.N_EU + 1):
642 h += self.pd_basic_row(req, "eu", i)
644 # Back to plain ones
645 h += self.standard_pd_html(req, "histrionic", Icd10SpecPD.N_HISTRIONIC)
646 h += self.standard_pd_html(req, "anankastic", Icd10SpecPD.N_ANANKASTIC)
647 h += self.standard_pd_html(req, "anxious", Icd10SpecPD.N_ANXIOUS)
648 h += self.standard_pd_html(req, "dependent", Icd10SpecPD.N_DEPENDENT)
650 # Done
651 h += """
652 </table>
653 """ + ICD10_COPYRIGHT_DIV
654 return h