Completed
Push — master ( b6fd08...0b342d )
by Ramon
21s queued 12s
created

Patient.getPatientIdentifiersStrHtml()   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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