Patient.getAgeSplitted()   C
last analyzed

Complexity

Conditions 10

Size

Total Lines 48
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 48
rs 5.9999
c 0
b 0
f 0
cc 10
nop 1

How to fix   Complexity   

Complexity

Complex classes like bika.health.content.patient.Patient.getAgeSplitted() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.HEALTH.
4
#
5
# SENAITE.HEALTH is free software: you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by the Free
7
# Software Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2019 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
from datetime import datetime
22
23
from Products.ATContentTypes.utils import DT2dt
24
from Products.ATExtensions.ateapi import RecordsField
25
from Products.Archetypes import atapi
26
from Products.Archetypes.public import *
27
from Products.CMFCore.utils import getToolByName
28
from Products.CMFPlone.utils import safe_unicode
29
from zope.interface import implements
30
31
from bika.health import bikaMessageFactory as _
32
from bika.health import logger
33
from bika.health.config import *
34
from bika.health.interfaces import IPatient
35
from bika.health.utils import translate_i18n as t
36
from bika.health.widgets import SplittedDateWidget
37
from bika.health.widgets.patientmenstrualstatuswidget import \
38
    PatientMenstrualStatusWidget
39
from bika.lims import api
40
from bika.lims import idserver
41
from bika.lims.browser.fields import AddressField
42
from bika.lims.browser.fields import DateTimeField as DateTimeField_bl
43
from bika.lims.browser.fields.remarksfield import RemarksField
44
from bika.lims.browser.widgets import AddressWidget
45
from bika.lims.browser.widgets import DateTimeWidget as DateTimeWidget_bl
46
from bika.lims.browser.widgets import RecordsWidget
47
from bika.lims.browser.widgets import ReferenceWidget
48
from bika.lims.browser.widgets.remarkswidget import RemarksWidget
49
from bika.lims.catalog.analysisrequest_catalog import \
50
    CATALOG_ANALYSIS_REQUEST_LISTING
51
from bika.lims.catalog.bika_catalog import BIKA_CATALOG
52
from bika.lims.content.person import Person
53
from bika.lims.interfaces import IClient
54
55
schema = Person.schema.copy() + Schema((
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable Schema does not seem to be defined.
Loading history...
56
    StringField(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable StringField does not seem to be defined.
Loading history...
57
        'PatientID',
58
        searchable=1,
59
        required=0,
60
        widget=StringWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable StringWidget does not seem to be defined.
Loading history...
61
            visible={'view': 'visible', 'edit': 'hidden'},
62
            label=_('Patient ID'),
63
            css='readonly-emphasize',
64
        ),
65
    ),
66
    ReferenceField(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReferenceField does not seem to be defined.
Loading history...
67
        'PrimaryReferrer',
68
        allowed_types=('Client',),
69
        relationship='PatientClient',
70
        widget=ReferenceWidget(
71
            label=_("Client"),
72
            size=30,
73
            catalog_name="portal_catalog",
74
            base_query={"is_active": True,
75
                        "sort_limit": 30,
76
                        "sort_on": "sortable_title",
77
                        "sort_order": "ascending"},
78
            colModel=[
79
                {"columnName": "Title", "label": _("Title"),
80
                 "width": "30", "align": "left"},
81
                {"columnName": "getProvince", "label": _("Province"),
82
                 "width": "30", "align": "left"},
83
                {"columnName": "getDistrict", "label": _("District"),
84
                 "width": "30", "align": "left"}],
85
            showOn=True,
86
        ),
87
    ),
88
    ComputedField(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ComputedField does not seem to be defined.
Loading history...
89
        'PrimaryReferrerID',
90
        expression="context.getClientID()",
91
        widget=ComputedWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ComputedWidget does not seem to be defined.
Loading history...
92
        ),
93
    ),
94
    ComputedField(
95
        'PrimaryReferrerTitle',
96
        expression="context.getClientTitle()",
97
        widget=ComputedWidget(
98
        ),
99
    ),
100
    ComputedField(
101
        'PrimaryReferrerUID',
102
        expression="context.getClientUID()",
103
        widget=ComputedWidget(
104
        ),
105
    ),
106
    ComputedField(
107
        'PrimaryReferrerURL',
108
        expression="context.getClientURL()",
109
        widget=ComputedWidget(
110
            visible=False
111
        ),
112
    ),
113
    StringField(
114
        'Gender',
115
        vocabulary=GENDERS,
116
        index='FieldIndex',
117
        default='dk',
118
        widget=SelectionWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable SelectionWidget does not seem to be defined.
Loading history...
119
            format='select',
120
            label=_('Gender'),
121
        ),
122
    ),
123
    StringField(
124
        'Age',
125
        widget=StringWidget(
126
            label=_('Age'),
127
            visible=0,
128
            width=3,
129
        ),
130
    ),
131
    DateTimeField_bl(
132
        'BirthDate',
133
        required=1,
134
        validators=('isDateFormat',),
135
        widget=DateTimeWidget_bl(
136
            label=_('Birth date'),
137
            datepicker_nofuture=1,
138
        ),
139
    ),
140
    BooleanField(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable BooleanField does not seem to be defined.
Loading history...
141
        'BirthDateEstimated',
142
        default=False,
143
        widget=BooleanWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable BooleanWidget does not seem to be defined.
Loading history...
144
            label=_('Birth date is estimated'),
145
        ),
146
    ),
147
    RecordsField(
148
        'AgeSplitted',
149
        required=1,
150
        widget=SplittedDateWidget(
151
            label=_('Age'),
152
        ),
153
    ),
154
    ComputedField(
155
        'AgeSplittedStr',
156
        expression="context.getAgeSplittedStr()",
157
        widget=ComputedWidget(
158
            visible=False
159
        ),
160
    ),
161
    AddressField(
162
        'CountryState',
163
        widget=AddressWidget(
164
            searchable=True,
165
            label=_("Country and state"),
166
            showLegend=True,
167
            showDistrict=True,
168
            showCopyFrom=False,
169
            showCity=False,
170
            showPostalCode=False,
171
            showAddress=False,
172
        ),
173
    ),
174
    RecordsField(
175
        'PatientIdentifiers',
176
        type='patientidentifiers',
177
        subfields=(
178
            'IdentifierType',
179
            'Identifier'
180
        ),
181
        subfield_labels={
182
            'IdentifierType': _('Identifier Type'),
183
            'Identifier': _('Identifier')
184
        },
185
        subfield_sizes={
186
            'Identifier': 15,
187
            'Identifier Type': 25
188
        },
189
        widget=RecordsWidget(
190
            label=_('Additional identifiers'),
191
            description=_('Patient additional identifiers'),
192
            combogrid_options={
193
                'IdentifierType': {
194
                    'colModel': [
195
                        {
196
                            'columnName': 'IdentifierType',
197
                            'width': '30',
198
                            'label': _('Title')
199
                        },
200
                        {
201
                            'columnName': 'Description',
202
                            'width': '70',
203
                            'label': _('Description')
204
                        }
205
                    ],
206
                    'url': 'getidentifiertypes',
207
                    'showOn': True,
208
                    'width': '550px'
209
                },
210
            },
211
        ),
212
    ),
213
    ComputedField(
214
        'PatientIdentifiersStr',
215
        expression="context.getPatientIdentifiersStr()",
216
        widget=ComputedWidget(
217
            visible=False
218
        ),
219
    ),
220
    RemarksField(
221
        'Remarks',
222
        searchable=True,
223
        widget=RemarksWidget(
224
            label=_('Remarks'),
225
        ),
226
    ),
227
    RecordsField(
228
        'TreatmentHistory',
229
        type='treatmenthistory',
230
        subfields=(
231
            'Treatment',
232
            'Drug',
233
            'Start',
234
            'End'
235
        ),
236
        required_subfields=(
237
            'Drug',
238
            'Start',
239
            'End'
240
        ),
241
        subfield_labels={
242
            'Drug': _('Drug'),
243
            'Start': _('Start'),
244
            'End': _('End')
245
        },
246
        subfield_sizes={
247
            'Drug': 40,
248
            'Start': 10,
249
            'End': 10
250
        },
251
        subfield_types={
252
            'Start': 'datepicker_nofuture',
253
            'End': 'datepicker'
254
        },
255
        widget=RecordsWidget(
256
            label='Drug History',
257
            description=_("A list of patient treatments and drugs administered."),
258
            combogrid_options={
259
                'Treatment': {
260
                    'colModel': [
261
                        {
262
                            'columnName': 'Treatment',
263
                            'width': '30',
264
                            'label': _('Title')
265
                        },
266
                        {
267
                            'columnName': 'Description',
268
                            'width': '70',
269
                            'label': _('Description')
270
                        }
271
                    ],
272
                    'url': 'gettreatments',
273
                    'showOn': True,
274
                    'width': '550px'
275
                },
276
                'Drug': {
277
                    'colModel': [
278
                        {
279
                            'columnName': 'Drug',
280
                            'width': '30',
281
                            'label': _('Title')
282
                        },
283
                        {
284
                            'columnName': 'Description',
285
                            'width': '70',
286
                            'label': _('Description')
287
                        }
288
                    ],
289
                    'url': 'getdrugs',
290
                    'showOn': True,
291
                    'width': '550px'
292
                },
293
            },
294
        ),
295
    ),
296
    RecordsField(
297
        'Allergies',
298
        type='allergies',
299
        subfields=(
300
            'DrugProhibition',
301
            'Drug',
302
            'Remarks'
303
        ),
304
        required_subfields=(
305
            'DrugProhibition',
306
            'Drug'
307
        ),
308
        subfield_labels={
309
            'DrugProhibition': _('Drug Prohibition Explanation'),
310
            'Drug': _('Drug'),
311
            'Remarks': _('Remarks')
312
        },
313
        subfield_sizes={
314
            'DrugProhibition': 30,
315
            'Drug': 30,
316
            'Remarks': 30
317
        },
318
        widget=RecordsWidget(
319
            label='Allergies',
320
            description=_("Known Patient allergies to keep information that can aid drug reaction interpretation"),
321
            combogrid_options={
322
                'Drug': {
323
                    'colModel': [
324
                        {
325
                            'columnName': 'Title',
326
                            'width': '30',
327
                            'label': _('Title')
328
                        },
329
                        {
330
                            'columnName': 'Description',
331
                            'width': '70',
332
                            'label': _('Description')
333
                        }
334
                    ],
335
                    'url': 'getdrugs',
336
                    'showOn': True,
337
                    'width': '550px'
338
                },
339
                'DrugProhibition': {
340
                    'colModel': [
341
                        {
342
                            'columnName': 'DrugProhibition',
343
                            'width': '30',
344
                            'label': _('Title')
345
                        },
346
                        {
347
                            'columnName': 'Description',
348
                            'width': '70',
349
                            'label': _('Description')
350
                        }
351
                    ],
352
                    'url': 'getdrugprohibitions',
353
                    'showOn': True,
354
                    'width': '550px'
355
                },
356
            },
357
        ),
358
    ),
359
    RecordsField(
360
        'ImmunizationHistory',
361
        type='immunizationhistory',
362
        subfields=(
363
            'EPINumber',
364
            'Immunization',
365
            'VaccinationCenter',
366
            'Date',
367
            'Remarks'
368
        ),
369
        required_subfields=(
370
            'EPINumber',
371
            'Immunization',
372
            'Date'
373
        ),
374
        subfield_labels={
375
            'EPINumber': _('EPI Number'),
376
            'Immunization': _('Immunization'),
377
            'VaccinationCenter': _('Vaccination Center'),
378
            'Date': _('Date'),
379
            'Remarks': _('Remarks')
380
        },
381
        subfield_sizes={
382
            'EPINumber': 12,
383
            'Immunization': 20,
384
            'VaccinationCenter': 10,
385
            'Date': 10,
386
            'Remarks': 25
387
        },
388
        subfield_types={
389
            'Date': 'datepicker_nofuture'
390
        },
391
        widget=RecordsWidget(
392
            label='Immunization History',
393
            description=_("A list of immunizations administered to the patient."),
394
            combogrid_options={
395
                'Immunization': {
396
                    'colModel': [
397
                        {
398
                            'columnName': 'Immunization',
399
                            'width': '30',
400
                            'label': _('Title')
401
                        },
402
                        {
403
                            'columnName': 'Description',
404
                            'width': '70',
405
                            'label': _('Description')
406
                        }
407
                    ],
408
                    'url': 'getimmunizations',
409
                    'showOn': True,
410
                    'width': '550px'
411
                },
412
                'VaccinationCenter': {
413
                    'colModel': [
414
                        {
415
                            'columnName': 'VaccinationCenter',
416
                            'width': '100',
417
                            'label': _('Title')
418
                        }
419
                    ],
420
                    'url': 'getvaccinationcenters',
421
                    'showOn': True,
422
                    'width': '550px'
423
                },
424
            },
425
        ),
426
    ),
427
    RecordsField(
428
        'TravelHistory',
429
        type='travelhistory',
430
        subfields=(
431
            'TripStartDate',
432
            'TripEndDate',
433
            'Country',
434
            'Location',
435
            'Remarks'
436
        ),
437
        required_subfields='Country',
438
        subfield_labels={
439
            'TripStartDate': _('Trip Start Date', 'Start date'),
440
            'TripEndDate': _('Trip End Date', 'End date'),
441
            'Country': _('Country'),
442
            'Location': _('Location'),
443
            'Remarks': _('Remarks')},
444
        subfield_sizes={
445
            'TripStartDate': 10,
446
            'TripEndDate': 10,
447
            'Country': 20,
448
            'Location': 20,
449
            'Remarks': 25},
450
        subfield_types={
451
            'TripStartDate': 'datepicker_nofuture',
452
            'TripEndDate': 'datepicker'
453
        },
454
        widget=RecordsWidget(
455
            label='Travel History',
456
            description=_("A list of places visited by the patient."),
457
            combogrid_options={
458
                'Country': {
459
                    'colModel': [
460
                        {
461
                            'columnName': 'Code',
462
                            'width': '15',
463
                            'label': _('Code')
464
                        },
465
                        {
466
                            'columnName': 'Country',
467
                            'width': '85',
468
                            'label': _('Country')
469
                        }
470
                    ],
471
                    'url': 'getCountries',
472
                    'showOn': True,
473
                    'width': "450px",
474
                },
475
            },
476
        ),
477
    ),
478
    RecordsField(
479
        'ChronicConditions',
480
        type='chronicconditions',
481
        subfields=(
482
            'Code',
483
            'Title',
484
            'Description',
485
            'Onset',
486
            'End'
487
        ),
488
        required_subfields=(
489
            'Title',
490
            'Onset'
491
        ),
492
        subfield_sizes={
493
            'Code': 7,
494
            'Title': 20,
495
            'Description': 35,
496
            'Onset': 10,
497
            'End': 10
498
        },
499
        subfield_types={
500
            'Onset': 'datepicker_nofuture',
501
            'End': 'datepicker'
502
        },
503
        widget=RecordsWidget(
504
            label='Past Medical History',
505
            description=_("Patient's past medical history."),
506
            combogrid_options={
507
                'Title': {
508
                    'colModel': [
509
                        {
510
                            'columnName': 'Code',
511
                            'width': '10',
512
                            'label': _('Code')
513
                        },
514
                        {
515
                            'columnName': 'Title',
516
                            'width': '30',
517
                            'label': _('Title')
518
                        },
519
                        {
520
                            'columnName': 'Description',
521
                            'width': '60',
522
                            'label': _('Description')
523
                        }
524
                    ],
525
                    'url': 'getdiseases',
526
                    'showOn': True,
527
                    'width': "650px",
528
                },
529
            },
530
        ),
531
    ),
532
    StringField(
533
        'BirthPlace',
534
        schemata='Personal',
535
        widget=StringWidget(
536
            label=_('Birth place'),
537
        ),
538
    ),
539
    # TODO This field will be removed on release 319. We maintain this field on release 318
540
    # because of the transference between string field and content type data.
541
    StringField(
542
        'Ethnicity',
543
        schemata='Personal',
544
        index='FieldIndex',
545
        vocabulary=ETHNICITIES,
546
        widget=ReferenceWidget(
547
            label=_('Ethnicity'),
548
            description=_("Ethnicity eg. Asian, African, etc."),
549
            visible=False,
550
        ),
551
    ),
552
    # TODO This field will change its name on v319 and it'll be called Ethnicity
553
    ReferenceField(
554
        'Ethnicity_Obj',
555
        schemata='Personal',
556
        vocabulary='getEthnicitiesVocabulary',
557
        allowed_types=('Ethnicity',),
558
        relationship='PatientEthnicity',
559
        widget=SelectionWidget(
560
            format='select',
561
            label=_('Ethnicity'),
562
            description=_("Ethnicity eg. Asian, African, etc."),
563
        ),
564
    ),
565
    StringField(
566
        'Citizenship',
567
        schemata='Personal',
568
        widget=StringWidget(
569
            label=_('Citizenship'),
570
        ),
571
    ),
572
    StringField(
573
        'MothersName',
574
        schemata='Personal',
575
        widget=StringWidget(
576
            label=_('Mothers name'),
577
        ),
578
    ),
579
    StringField(
580
        'FathersName',
581
        schemata='Personal',
582
        widget=StringWidget(
583
            label=_('Fathers name'),
584
        ),
585
    ),
586
    StringField(
587
        'CivilStatus',
588
        schemata='Personal',
589
        widget=StringWidget(
590
            label=_('Civil status'),
591
        ),
592
    ),
593
    ImageField(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ImageField does not seem to be defined.
Loading history...
594
        'Photo',
595
        schemata='Identification',
596
        widget=ImageWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ImageWidget does not seem to be defined.
Loading history...
597
            label=_('Photo'),
598
        ),
599
    ),
600
    ImageField(
601
        'Feature',
602
        schemata='Identification',
603
        multiValue=1,
604
        widget=ImageWidget(
605
            label=_('Feature'),
606
        ),
607
    ),
608
    RecordsField(
609
        'MenstrualStatus',
610
        type='menstrualstatus',
611
        widget=PatientMenstrualStatusWidget(
612
            label='Menstrual status',
613
        ),
614
    ),
615
    StringField(
616
        'ClientPatientID',
617
        searchable=1,
618
        validators=('unique_client_patient_ID_validator',),
619
        required=1,
620
        widget=StringWidget(
621
            label=_('Client Patient ID'),
622
        ),
623
    ),
624
    BooleanField(
625
        'Anonymous',
626
        default=False,
627
        widget=BooleanWidget(
628
            label=_("Anonymous")
629
        ),
630
    ),
631
    BooleanField(
632
        'DefaultResultsDistribution',
633
        schemata="Publication preference",
634
        default=True,
635
        widget=BooleanWidget(
636
            label=_("Inherit default settings"),
637
            description=_("If checked, settings will be inherited from "
638
                          "the Client, so further changes in Client for this "
639
                          "setting will be populated too."))
640
    ),
641
    BooleanField(
642
        'AllowResultsDistribution',
643
        schemata="Publication preference",
644
        default=False,
645
        widget=BooleanWidget(
646
            label=_("Allow results distribution to this patient"),
647
            description=_("If checked, results reports will also be sent "
648
                          "to the Patient automatically."))
649
    ),
650
    BooleanField(
651
        'PublicationAttachmentsPermitted',
652
        default=False,
653
        schemata='Publication preference',
654
        widget=BooleanWidget(
655
            label=_("Results attachments permitted"),
656
            description=_("File attachments to results, e.g. microscope "
657
                          "photos, will be included in emails to patient "
658
                          "if this option is enabled"))
659
    ),
660
    ReferenceField(
661
        'InsuranceCompany',
662
        vocabulary='get_insurancecompanies',
663
        allowed_types=('InsuranceCompany',),
664
        relationship='InsuranceCompany',
665
        required=False,
666
        widget=SelectionWidget(
667
            format='select',
668
            label=_('Insurance Company'),
669
            ),
670
        ),
671
    StringField(
672
        'InsuranceNumber',
673
        searchable=1,
674
        required=0,
675
        widget=StringWidget(
676
            label=_('Insurance Number'),
677
        ),
678
    ),
679
    BooleanField(
680
        'InvoiceToInsuranceCompany',
681
        default=False,
682
        widget=BooleanWidget(
683
            label=_("Send invoices to the insurance company."),
684
            description=_("If it is checked the invoices will be send to the insurance company."
685
                          " In this case the insurance number will be mandatory."))
686
    ),
687
    BooleanField(
688
        'PatientAsGuarantor',
689
        schemata='Insurance',
690
        default=True,
691
        widget=BooleanWidget(
692
            label=_("The patient is the guarantor."),
693
            description=_("The patient and the guarantor are the same."))
694
    ),
695
    StringField(
696
        'GuarantorID',
697
        searchable=1,
698
        schemata='Insurance',
699
        required=0,
700
        widget=StringWidget(
701
            label=_('Guarantor ID'),
702
            description=_("The ID number (Insurance Number) from the person whose contract cover the current patient.")
703
        ),
704
    ),
705
    StringField(
706
        'GuarantorSurname',
707
        searchable=1,
708
        schemata='Insurance',
709
        required=0,
710
        widget=StringWidget(
711
            label=_("Guarantor's Surname"),
712
        ),
713
    ),
714
    StringField(
715
        'GuarantorFirstname',
716
        searchable=1,
717
        schemata='Insurance',
718
        required=0,
719
        widget=StringWidget(
720
            label=_("Guarantor's First Name"),
721
        ),
722
    ),
723
    AddressField(
724
        'GuarantorPostalAddress',
725
        searchable=1,
726
        schemata='Insurance',
727
        required=0,
728
        widget=AddressWidget(
729
            label=_("Guarantor's postal address"),
730
        ),
731
    ),
732
    StringField(
733
        'GuarantorBusinessPhone',
734
        schemata='Insurance',
735
        widget=StringWidget(
736
            label=_("Guarantor's Phone (business)"),
737
        ),
738
    ),
739
    StringField(
740
        'GuarantorHomePhone',
741
        schemata='Insurance',
742
        widget=StringWidget(
743
            label=_("Guarantor's Phone (home)"),
744
        ),
745
    ),
746
    StringField(
747
        'GuarantorMobilePhone',
748
        schemata='Insurance',
749
        widget=StringWidget(
750
            label=_("Guarantor's Phone (mobile)"),
751
        ),
752
    ),
753
    BooleanField(
754
        'ConsentSMS',
755
        default=False,
756
        widget=BooleanWidget(
757
            label=_('Consent to SMS'),
758
        ),
759
    ),
760
    ComputedField(
761
        'NumberOfSamples',
762
        expression="len(context.getSamples())",
763
        widget=ComputedWidget(
764
            visible=False
765
        ),
766
    ),
767
    ComputedField(
768
        'NumberOfSamplesCancelled',
769
        expression="len(context.getSamplesCancelled())",
770
        widget=ComputedWidget(
771
            visible=False
772
        ),
773
    ),
774
    ComputedField(
775
        'NumberOfSamplesOngoing',
776
        expression="len(context.getSamplesOngoing())",
777
        widget=ComputedWidget(
778
            visible=False
779
        ),
780
    ),
781
    ComputedField(
782
        'NumberOfSamplesPublished',
783
        expression="len(context.getSamplesPublished())",
784
        widget=ComputedWidget(
785
            visible=False
786
        ),
787
    ),
788
    ComputedField(
789
        'RatioOfSamplesOngoing',
790
        expression="context.getNumberOfSamplesOngoingRatio()",
791
        widget=ComputedWidget(
792
            visible=False
793
        ),
794
    ),
795
))
796
797
schema['JobTitle'].widget.visible = False
798
schema['Department'].widget.visible = False
799
schema['BusinessPhone'].widget.visible = False
800
schema['BusinessFax'].widget.visible = False
801
# Don't make title required - it will be computed from the Person's Fullname
802
schema['title'].required = 0
803
schema['title'].widget.visible = False
804
schema['EmailAddress'].schemata = 'Personal'
805
schema['HomePhone'].schemata = 'Personal'
806
schema['MobilePhone'].schemata = 'Personal'
807
schema['InsuranceCompany'].schemata = 'Insurance'
808
schema['InsuranceNumber'].schemata = 'Insurance'
809
schema['InvoiceToInsuranceCompany'].schemata = 'Insurance'
810
schema.moveField('PrimaryReferrer', after='Surname')
811
schema.moveField('PatientID', before='title')
812
schema.moveField('ClientPatientID', after='PatientID')
813
schema.moveField('Anonymous', before='ClientPatientID')
814
schema.moveField('InsuranceCompany', after='PrimaryReferrer')
815
schema.moveField('InsuranceNumber', after='InsuranceCompany')
816
schema.moveField('PatientIdentifiers', after='InsuranceNumber')
817
schema.moveField('Gender', after='PatientIdentifiers')
818
schema.moveField('Age', after='Gender')
819
schema.moveField('BirthDate', after='Age')
820
schema.moveField('BirthDateEstimated', after='BirthDate')
821
schema.moveField('AgeSplitted', after='BirthDateEstimated')
822
schema.moveField('CountryState', after='AgeSplitted')
823
schema.moveField('MenstrualStatus', after='AgeSplitted')
824
schema.moveField('ConsentSMS', after='PrimaryReferrer')
825
schema.moveField('PrimaryReferrer', before='ClientPatientID')
826
827
828
class Patient(Person):
829
    implements(IPatient)
830
    _at_rename_after_creation = True
831
    displayContentsTab = False
832
    schema = schema
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable schema does not seem to be defined.
Loading history...
833
834
    def _renameAfterCreation(self, check_auto_id=False):
835
        """Autogenerate the ID of the object based on core's ID formatting
836
        settings for this type
837
        """
838
        idserver.renameAfterCreation(self)
839
840
    def Title(self):
841
        """Return the Fullname of the patient
842
        """
843
        return safe_unicode(self.getFullname()).encode('utf-8')
844
845
    def getPatientID(self):
846
        return self.getId()
847
848
    def getSamples(self, **kwargs):
849
        """Return samples taken from this Patient
850
        """
851
        catalog = api.get_tool(CATALOG_ANALYSIS_REQUEST_LISTING, context=self)
852
        query = dict([(k, v) for k, v in kwargs.items()
853
                      if k in catalog.indexes()])
854
        query["getPatientUID"] = api.get_uid(self)
855
        brains = api.search(query, CATALOG_ANALYSIS_REQUEST_LISTING)
856
        if not kwargs.get("full_objects", False):
857
            return brains
858
        return map(api.get_object, brains)
859
860
    def getSamplesCancelled(self, full_objects=False):
861
        """Return samples taken from this Patient that are in cancelled state
862
        """
863
        return self.getSamples(review_state="cancelled",
864
                               full_objects=full_objects)
865
866
    def getSamplesPublished(self, full_objects=False):
867
        """Return samples taken from this Patient that are in published state
868
        """
869
        return self.getSamples(review_state="published",
870
                               full_objects=full_objects)
871
872
    def getSamplesOngoing(self, full_objects=False):
873
        """Return samples taken from this Patient that are ongoing
874
        """
875
        ongoing_statuses = [
876
            "to_be_sampled",
877
            "scheduled_sampling",
878
            "to_be_sampled",
879
            "sample_due",
880
            "sample_received",
881
            "attachment_due",
882
            "to_be_verified",
883
            "verified",
884
            "to_be_preserved"]
885
        return self.getSamples(review_state=ongoing_statuses, is_active=True,
886
                               full_objects=full_objects)
887
888
    def getNumberOfSamplesOngoingRatio(self):
889
        """
890
        Returns the ratio between NumberOfSamplesOngoing/NumberOfSamples
891
        """
892
        samples = self.getSamples()
893
        if len(samples) > 0:
894
            return len(self.getSamplesOngoing())/len(samples)
895
        return 0
896
897
    def get_insurancecompanies(self):
898
        """
899
        Return all the registered insurance companies.
900
        """
901
        bsc = getToolByName(self, 'bika_setup_catalog')
902
        # Void selection
903
        ret = [('', '')]
904
        # Other selections
905
        for ic in bsc(portal_type='InsuranceCompany',
906
                      is_active=True,
907
                      sort_on='sortable_title'):
908
            ret.append((ic.UID, ic.Title))
909
        return DisplayList(ret)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable DisplayList does not seem to be defined.
Loading history...
910
911
    def getPatientIdentifiersList(self):
912
        """Returns a list with the additional identifiers for this patient
913
        """
914
        ids = self.getPatientIdentifiers()
915
        ids = map(lambda patient_id: patient_id.get("Identifier"), ids)
916
        return filter(None, ids)
917
918
    def getPatientIdentifiersStr(self):
919
        """Returns a string representation of the additional identifiers for
920
        this patient
921
        """
922
        ids = self.getPatientIdentifiersList()
923
        return " ".join(ids)
924
925
    def getAgeSplitted(self):
926
927
        if self.getBirthDate():
928
            dob = DT2dt(self.getBirthDate()).replace(tzinfo=None)
929
            now = datetime.today()
930
931
            currentday = now.day
932
            currentmonth = now.month
933
            currentyear = now.year
934
            birthday = dob.day
935
            birthmonth = dob.month
936
            birthyear = dob.year
937
            ageday = currentday - birthday
938
            agemonth = 0
939
            ageyear = 0
940
            months31days = [1, 3, 5, 7, 8, 10, 12]
941
942
            if ageday < 0:
943
                currentmonth -= 1
944
                if currentmonth < 1:
945
                    currentyear -= 1
946
                    currentmonth = currentmonth + 12
947
948
                dayspermonth = 30
949
                if currentmonth in months31days:
950
                    dayspermonth = 31
951
                elif currentmonth == 2:
952
                    dayspermonth = 28
953
                    if(currentyear % 4 == 0
954
                       and (currentyear % 100 > 0 or currentyear % 400 == 0)):
955
                        dayspermonth += 1
956
957
                ageday = ageday + dayspermonth
958
959
            agemonth = currentmonth - birthmonth
960
            if agemonth < 0:
961
                currentyear -= 1
962
                agemonth = agemonth + 12
963
964
            ageyear = currentyear - birthyear
965
966
            return [{'year': ageyear,
967
                     'month': agemonth,
968
                     'day': ageday}]
969
        else:
970
            return [{'year': '',
971
                     'month': '',
972
                     'day': ''}]
973
974
    def getAge(self):
975
        return self.getAgeSplitted()[0]['year']
976
977
    def getAgeSplittedStr(self):
978
        splitted = self.getAgeSplitted()[0]
979
        arr = []
980
        arr.append(splitted['year'] and str(splitted['year']) + 'y' or '')
981
        arr.append(splitted['month'] and str(splitted['month']) + 'm' or '')
982
        arr.append(splitted['day'] and str(splitted['day']) + 'd' or '')
983
        return ' '.join(arr)
984
985
    def getCountryState(self):
986
        return self.getField('CountryState').get(self) \
987
            if self.getField('CountryState').get(self) \
988
            else self.getPhysicalAddress()
989
990
    def getGuarantorID(self):
991
        """
992
        If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as
993
        the current patient fields.
994
        :return: The guarantor ID (insurance number) from
995
        """
996
        return self.getInsuranceNumber() if self.getPatientAsGuarantor() else self.getField('GuarantorID').get(self)
997
998
    def getGuarantorSurname(self):
999
        """
1000
        If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as
1001
        the current patient fields.
1002
        """
1003
        return self.getSurname() if self.getPatientAsGuarantor() else self.getField('GuarantorSurname').get(self)
1004
1005
    def getGuarantorFirstname(self):
1006
        """
1007
        If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as
1008
        the current patient fields.
1009
        """
1010
        return self.getFirstname() if self.getPatientAsGuarantor() else self.getField('GuarantorFirstname').get(self)
1011
1012
    def getGuarantorPostalAddress(self):
1013
        """
1014
        If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as
1015
        the current patient fields.
1016
        """
1017
        return self.getPostalAddress() \
1018
            if self.getPatientAsGuarantor() \
1019
            else self.getField('GuarantorPostalAddress').get(self)
1020
1021
    def getGuarantorBusinessPhone(self):
1022
        """
1023
        If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as
1024
        the current patient fields.
1025
        """
1026
        return self.getBusinessPhone() \
1027
            if self.getPatientAsGuarantor() \
1028
            else self.getField('GuarantorBusinessPhone').get(self)
1029
1030
    def getGuarantorHomePhone(self):
1031
        """
1032
        If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as
1033
        the current patient fields.
1034
        """
1035
        return self.getHomePhone() if self.getPatientAsGuarantor() else self.getField('GuarantorHomePhone').get(self)
1036
1037
    def getGuarantorMobilePhone(self):
1038
        """
1039
        If the patient is the guarantor, all the fields related with the guarantor are going to have the same value as
1040
        the current patient fields.
1041
        """
1042
        return self.getMobilePhone() \
1043
            if self.getPatientAsGuarantor() \
1044
            else self.getField('GuarantorMobilePhone').get(self)
1045
1046
    def getEthnicitiesVocabulary(self, instance=None):
1047
        """
1048
        Obtain all the ethnicities registered in the system and returns them as a list
1049
        """
1050
        bsc = getToolByName(self, 'bika_setup_catalog')
1051
        items = [(c.UID, c.Title)
1052
                 for c in bsc(portal_type='Ethnicity',
1053
                              is_active=True)]
1054
        items.sort(lambda x, y: cmp(x[1], y[1]))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable cmp does not seem to be defined.
Loading history...
1055
        items.insert(0, ('', t(_(''))))
1056
        return DisplayList(items)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable DisplayList does not seem to be defined.
Loading history...
1057
1058
    # TODO This function will will be removed on v319
1059
    def getEthnicity(self):
1060
        """
1061
        This function exists because we are changing the construction of ethnicities. Until now, ethnicities options were
1062
        hand-coded but now they are a new content type. So we need to pass all patient's ethnicity values, but to do
1063
        such thing, we need to create new ethnicity types on upgrade step and edit patient ethnicity field to relate them
1064
        with its corresponding ethnicity content type.
1065
        :return:
1066
        """
1067
        return self.getEthnicity_Obj()
1068
1069
    # TODO This function will be removed on v319
1070
    def setEthnicity(self, value):
1071
        self.setEthnicity_Obj(value)
1072
1073
    def getDocuments(self):
1074
        """
1075
        Return all the multifile objects related with the patient
1076
        """
1077
        return self.objectValues('Multifile')
1078
1079
    def getPrimaryReferrer(self):
1080
        """Returns the client the current Patient is assigned to. Delegates the
1081
        action to function getClient.
1082
        NOTE: This is kept for backwards compatibility
1083
        """
1084
        logger.warn("Patient.getPrimaryReferrer: better use 'getClient'")
1085
        return self.getClient()
1086
1087
    def getClient(self):
1088
        """Returns the client the current Patient is assigned to, if any
1089
        """
1090
        # The schema's field PrimaryReferrer is only used to allow the user to
1091
        # assign the patient to a client in edit form. The entered value is used
1092
        # in ObjectModifiedEventHandler to move the patient to the Client's
1093
        # folder, so the value stored in the Schema's is not used anymore
1094
        # See https://github.com/senaite/senaite.core/pull/152
1095
        client = self.aq_parent
1096
        if IClient.providedBy(client):
1097
            return client
1098
        return None
1099
1100
    def setClient(self, value):
1101
        """Sets the client the current Patient has to be assigned to
1102
        """
1103
        self.setPrimaryReferrer(value)
1104
1105
    def getClientID(self):
1106
        """Returns the ID of the client this Patient belongs to or None
1107
        """
1108
        client = self.getClient()
1109
        return client and api.get_id(client) or None
1110
1111
    def getClientUID(self):
1112
        """Returns the UID of the client this Patient belongs to or None
1113
        """
1114
        client = self.getClient()
1115
        return client and api.get_uid(client) or None
1116
1117
    def getClientURL(self):
1118
        """Returns the URL of the client this Patient belongs to or None
1119
        """
1120
        client = self.getClient()
1121
        return client and api.get_url(client) or None
1122
1123
    def getClientTitle(self):
1124
        """Returns the title of the client this Patient belongs to or None
1125
        """
1126
        client = self.getClient()
1127
        return client and api.get_title(client) or None
1128
1129
    def getBatches(self, full_objects=False):
1130
        """Returns the Batches (Clinic Cases) this Patient is assigned to
1131
        """
1132
        query = dict(portal_type="Batch", getPatientUID=api.get_uid(self))
1133
        batches = api.search(query, BIKA_CATALOG)
1134
        if full_objects:
1135
            return map(api.get_object, batches)
1136
        return batches
1137
1138
    def SearchableText(self):
1139
        """
1140
        Override searchable text logic based on the requirements.
1141
1142
        This method constructs a text blob which contains all full-text
1143
        searchable text for this content item.
1144
        https://docs.plone.org/develop/plone/searching_and_indexing/indexing.html#full-text-searching
1145
        """
1146
1147
        # Speed up string concatenation ops by using a buffer
1148
        entries = []
1149
1150
        # plain text fields we index from ourself,
1151
        # a list of accessor methods of the class
1152
        plain_text_fields = ("Title", "getFullname", "getId",
1153
                             "getPrimaryReferrerID", "getPrimaryReferrerTitle", "getClientPatientID")
1154
1155
        def read(accessor):
1156
            """
1157
            Call a class accessor method to give a value for certain Archetypes
1158
            field.
1159
            """
1160
            try:
1161
                value = accessor()
1162
            except:
1163
                value = ""
1164
1165
            if value is None:
1166
                value = ""
1167
1168
            return value
1169
1170
        # Concatenate plain text fields as they are
1171
        for f in plain_text_fields:
1172
            accessor = getattr(self, f)
1173
            value = read(accessor)
1174
            entries.append(value)
1175
1176
        # Adding HTML Fields to SearchableText can be uncommented if necessary
1177
        # transforms = getToolByName(self, 'portal_transforms')
1178
        #
1179
        # # Run HTML valued fields through text/plain conversion
1180
        # for f in html_fields:
1181
        #     accessor = getattr(self, f)
1182
        #     value = read(accessor)
1183
        #
1184
        #     if value != "":
1185
        #         stream = transforms.convertTo('text/plain', value, mimetype='text/html')
1186
        #         value = stream.getData()
1187
        #
1188
        #     entries.append(value)
1189
1190
        # Plone accessor methods assume utf-8
1191
        def convertToUTF8(text):
1192
            if type(text) == unicode:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable unicode does not seem to be defined.
Loading history...
1193
                return text.encode("utf-8")
1194
            return text
1195
1196
        entries = [convertToUTF8(entry) for entry in entries]
1197
1198
        # Concatenate all strings to one text blob
1199
        return " ".join(entries)
1200
1201
1202
# schemata.finalizeATCTSchema(schema, folderish=True, moveDiscussion=False)
1203
atapi.registerType(Patient, PROJECTNAME)
1204