Mercurial > hgweb > health
view tryton/health/health.py @ 1082:40f7d8e45ec1
all : update version to 2.2.2 and Changelog
| author | Luis Falcon <falcon@gnu.org> |
|---|---|
| date | Wed, 13 Nov 2013 19:48:27 -0300 |
| parents | 92896dc88f2c |
| children | 92408378aa66 |
line wrap: on
line source
# -*- coding: utf-8 -*- ############################################################################## # # GNU Health: The Free Health and Hospital Information System # Copyright (C) 2008-2013 Luis Falcon <falcon@gnu.org> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## from dateutil.relativedelta import relativedelta from datetime import datetime from trytond.model import ModelView, ModelSingleton, ModelSQL, fields from trytond.wizard import Wizard, StateAction, StateView, Button from trytond.transaction import Transaction from trytond.backend import TableHandler from trytond.pyson import Eval, Not, Bool, PYSONEncoder, Equal from trytond.pool import Pool __all__ = [ 'DrugDoseUnits', 'MedicationFrequency', 'DrugForm', 'DrugRoute', 'Occupation', 'Ethnicity', 'MedicalSpecialty', 'HealthProfessional', 'HealthProfessionalSpecialties', 'PhysicianSP', 'OperationalArea', 'OperationalSector', 'Family', 'FamilyMember', 'DomiciliaryUnit', 'MedicamentCategory', 'Medicament', 'PathologyCategory', 'PathologyGroup', 'Pathology', 'DiseaseMembers', 'ProcedureCode', 'InsurancePlan', 'Insurance', 'AlternativePersonID', 'PartyPatient', 'PartyAddress', 'ProductCategory', 'ProductTemplate', 'Product', 'GnuHealthSequences', 'PatientData', 'PatientDiseaseInfo', 'Appointment', 'AppointmentReport', 'OpenAppointmentReportStart', 'OpenAppointmentReport', 'PatientMedication', 'PatientVaccination', 'PatientPrescriptionOrder', 'PrescriptionLine', 'PatientEvaluation', 'Directions', 'SecondaryCondition', 'DiagnosticHypothesis', 'SignsAndSymptoms', 'HospitalBuilding', 'HospitalUnit', 'HospitalOR', 'HospitalWard', 'HospitalBed'] class DrugDoseUnits(ModelSQL, ModelView): 'Drug Dose Unit' __name__ = 'gnuhealth.dose.unit' name = fields.Char('Unit', required=True, select=True, translate=True) desc = fields.Char('Description', translate=True) @classmethod def __setup__(cls): super(DrugDoseUnits, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Unit must be unique !'), ] class MedicationFrequency(ModelSQL, ModelView): 'Medication Common Frequencies' __name__ = 'gnuhealth.medication.dosage' name = fields.Char( 'Frequency', required=True, select=True, translate=True, help='Common frequency name') code = fields.Char( 'Code', help='Dosage Code,for example: SNOMED 229798009 = 3 times per day') abbreviation = fields.Char( 'Abbreviation', help='Dosage abbreviation, such as tid in the US or tds in the UK') @classmethod def __setup__(cls): super(MedicationFrequency, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Unit must be unique !'), ] class DrugForm(ModelSQL, ModelView): 'Drug Form' __name__ = 'gnuhealth.drug.form' name = fields.Char('Form', required=True, select=True, translate=True) code = fields.Char('Code') @classmethod def __setup__(cls): super(DrugForm, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Unit must be unique !'), ] class DrugRoute(ModelSQL, ModelView): 'Drug Administration Route' __name__ = 'gnuhealth.drug.route' name = fields.Char('Unit', required=True, select=True, translate=True) code = fields.Char('Code') @classmethod def __setup__(cls): super(DrugRoute, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Name must be unique !'), ] class Occupation(ModelSQL, ModelView): 'Occupation' __name__ = 'gnuhealth.occupation' name = fields.Char('Name', required=True, translate=True) code = fields.Char('Code') @classmethod def __setup__(cls): super(Occupation, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Name must be unique !'), ] class Ethnicity(ModelSQL, ModelView): 'Ethnicity' __name__ = 'gnuhealth.ethnicity' name = fields.Char('Name', required=True, translate=True) code = fields.Char('Code') notes = fields.Char('Notes') @classmethod def __setup__(cls): super(Ethnicity, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Name must be unique !'), ] class MedicalSpecialty(ModelSQL, ModelView): 'Medical Specialty' __name__ = 'gnuhealth.specialty' name = fields.Char( 'Specialty', required=True, translate=True, help='ie, Addiction Psychiatry') code = fields.Char('Code', help='ie, ADP') @classmethod def __setup__(cls): super(MedicalSpecialty, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Specialty must be unique !'), ] class HealthProfessional(ModelSQL, ModelView): 'Health Professional' __name__ = 'gnuhealth.physician' @classmethod def get_health_professional(cls): # Get the professional associated to the internal user id # that logs into GNU Health cursor = Transaction().cursor User = Pool().get('res.user') user = User(Transaction().user) login_user_id = int(user.id) cursor.execute('SELECT id FROM party_party WHERE is_doctor=True AND \ internal_user = %s LIMIT 1', (login_user_id,)) partner_id = cursor.fetchone() if partner_id: cursor = Transaction().cursor cursor.execute('SELECT id FROM gnuhealth_physician WHERE \ name = %s LIMIT 1', (partner_id[0],)) doctor_id = cursor.fetchone() if (doctor_id): return int(doctor_id[0]) name = fields.Many2One( 'party.party', 'Health Professional', required=True, domain=[ ('is_doctor', '=', True), ('is_person', '=', True), ], help='Health Professional\'s Name, from the partner list') institution = fields.Many2One( 'party.party', 'Institution', domain=[('is_institution', '=', True)], help='Instituion where she/he works') code = fields.Char('ID', help='License ID') specialties = fields.One2Many( 'gnuhealth.hp_specialty', 'name', 'Specialties') info = fields.Text('Extra info') def get_rec_name(self, name): if self.name: res = self.name.name if self.name.lastname: res = self.name.lastname + ', ' + self.name.name return res @classmethod def __setup__(cls): super(HealthProfessional, cls).__setup__() cls._sql_constraints = [ ('hp_uniq', 'UNIQUE(name)', 'The health professional must be unique'), ] class HealthProfessionalSpecialties(ModelSQL, ModelView): 'Health Professional Specialties' __name__ = 'gnuhealth.hp_specialty' name = fields.Many2One('gnuhealth.physician', 'Health Professional') specialty = fields.Many2One( 'gnuhealth.specialty', 'Specialty', help='Specialty Code') def get_rec_name(self, name): return self.specialty.name class PhysicianSP(ModelSQL, ModelView): # Add Main Specialty field after from the Health Professional Speciality 'Health Professional' __name__ = 'gnuhealth.physician' main_specialty = fields.Many2One( 'gnuhealth.hp_specialty', 'Main Specialty', domain=[('name', '=', Eval('active_id'))], help='Specialty Code') @classmethod # Update to version 2.2 def __register__(cls, module_name): super(PhysicianSP, cls).__register__(module_name) cursor = Transaction().cursor table = TableHandler(cursor, cls, module_name) # Insert the current "specialty" associated to the HP in the # table that keeps the specialties associated to different health # professionals, gnuhealth.hp_specialty if table.column_exist('specialty'): # Update the list of specialties of that health professional # with the current specialty cursor.execute( "INSERT INTO gnuhealth_hp_specialty (name, specialty) \ SELECT id, specialty from gnuhealth_physician;") # Drop old specialty column, replaced by main_specialty table.drop_column('specialty') class OperationalArea(ModelSQL, ModelView): 'Operational Area' __name__ = 'gnuhealth.operational_area' name = fields.Char( 'Name', required=True, help='Operational Area of the city or region') operational_sector = fields.One2Many( 'gnuhealth.operational_sector', 'operational_area', 'Operational Sector', readonly=True) info = fields.Text('Extra Information') @classmethod def __setup__(cls): super(OperationalArea, cls).__setup__() cls._sql_constraints += [ ('name_uniq', 'UNIQUE(name)', 'The operational area must be unique !'), ] class OperationalSector(ModelSQL, ModelView): 'Operational Sector' __name__ = 'gnuhealth.operational_sector' name = fields.Char( 'Op. Sector', required=True, help='Region included in an operational area') operational_area = fields.Many2One( 'gnuhealth.operational_area', 'Operational Area') info = fields.Text('Extra Information') @classmethod def __setup__(cls): super(OperationalSector, cls).__setup__() cls._sql_constraints += [ ('name_uniq', 'UNIQUE(name, operational_area)', 'The operational sector must be unique in each' ' operational area!'), ] class Family(ModelSQL, ModelView): 'Family' __name__ = 'gnuhealth.family' name = fields.Char('Family', required=True, help='Family code') members = fields.One2Many( 'gnuhealth.family_member', 'name', 'Family Members') info = fields.Text('Extra Information') @classmethod def __setup__(cls): super(Family, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Family Code must be unique !'), ] @classmethod # Update to version 2.0 def __register__(cls, module_name): super(Family, cls).__register__(module_name) cursor = Transaction().cursor table = TableHandler(cursor, cls, module_name) # Remove Operational Sector from the family model # The operational Sector is linked to the Domiciliary Unit # Since GHealth 2.0 , the family model will contain their # members and their role. if table.column_exist('operational_sector'): table.drop_column('operational_sector') class FamilyMember(ModelSQL, ModelView): 'Family Member' __name__ = 'gnuhealth.family_member' name = fields.Many2One( 'gnuhealth.family', 'Family', required=True, readonly=True, help='Family code') party = fields.Many2One( 'party.party', 'Party', required=True, domain=[('is_person', '=', True)], help='Family Member') role = fields.Char('Role', help='Father, Mother, sibbling...') class DomiciliaryUnit(ModelSQL, ModelView): 'Domiciliary Unit' __name__ = 'gnuhealth.du' name = fields.Char('Code', required=True) desc = fields.Char('Desc') address_street = fields.Char('Street') address_street_number = fields.Integer('Number') address_street_bis = fields.Char('Apartment') address_district = fields.Char( 'District', help="Neighborhood, Village, Barrio....") address_municipality = fields.Char( 'Municipality', help="Municipality, Township, county ..") address_city = fields.Char('City') address_zip = fields.Char('Zip Code') address_country = fields.Many2One( 'country.country', 'Country', help='Country') address_subdivision = fields.Many2One( 'country.subdivision', 'Province', domain=[('country', '=', Eval('address_country'))], depends=['address_country']) operational_sector = fields.Many2One( 'gnuhealth.operational_sector', 'Operational Sector') picture = fields.Binary('Picture') latitude = fields.Numeric('Latidude', digits=(3, 14)) longitude = fields.Numeric('Longitude', digits=(4, 14)) urladdr = fields.Char( 'OSM Map', on_change_with=[ 'latitude', 'longitude', 'address_street', 'address_street_number', 'address_district', 'address_municipality', 'address_city', 'address_zip', 'address_subdivision', 'address_country'], help="Locates the DU on the Open Street Map by default") # Infrastructure dwelling = fields.Selection([ (None, ''), ('single_house', 'Single / Detached House'), ('apartment', 'Apartment'), ('townhouse', 'Townhouse'), ('factory', 'Factory'), ('building', 'Building'), ('mobilehome', 'Mobile House'), ], 'Type', sort=False) materials = fields.Selection([ (None, ''), ('concrete', 'Concrete'), ('adobe', 'Adobe'), ('wood', 'Wood'), ('mud', 'Mud / Straw'), ('stone', 'Stone'), ], 'Material', sort=False) roof_type = fields.Selection([ (None, ''), ('concrete', 'Concrete'), ('adobe', 'Adobe'), ('wood', 'Wood'), ('mud', 'Mud'), ('thatch', 'Thatched'), ('stone', 'Stone'), ], 'Roof', sort=False) total_surface = fields.Integer('Surface', help="Surface in sq. meters") bedrooms = fields.Integer('Bedrooms') bathrooms = fields.Integer('Bathrooms') housing = fields.Selection([ (None, ''), ('0', 'Shanty, deficient sanitary conditions'), ('1', 'Small, crowded but with good sanitary conditions'), ('2', 'Comfortable and good sanitary conditions'), ('3', 'Roomy and excellent sanitary conditions'), ('4', 'Luxury and excellent sanitary conditions'), ], 'Conditions', help="Housing and sanitary living conditions", sort=False) sewers = fields.Boolean('Sanitary Sewers') water = fields.Boolean('Running Water') trash = fields.Boolean('Trash recollection') electricity = fields.Boolean('Electrical supply') gas = fields.Boolean('Gas supply') telephone = fields.Boolean('Telephone') television = fields.Boolean('Television') internet = fields.Boolean('Internet') members = fields.One2Many('party.party', 'du', 'Members', readonly=True) def on_change_with_urladdr(self): # Generates the URL to be used in OpenStreetMap # The address will be mapped to the URL in the following way # If the latitud and longitude of the DU are given, then those # parameters will be used. # Otherwise, it will try to find the address by the # Street, municipality, city, postalcode, state and country. if (self.latitude and self.longitude): ret_url = 'http://openstreetmap.org/?mlat=' + \ str(self.latitude) + '&mlon=' + str(self.longitude) else: state = '' country = '' street_number = str(self.address_street_number).encode('utf-8') \ or '' street = (self.address_street).encode('utf-8') or '' municipality = (self.address_municipality).encode('utf-8') or '' city = (self.address_city).encode('utf-8') or '' if (self.address_subdivision): state = (self.address_subdivision.name).encode('utf-8') or '' postalcode = (self.address_zip).encode('utf-8') or '' if (self.address_country): country = (self.address_country.code).encode('utf-8') or '' ret_url = 'http://nominatim.openstreetmap.org/search?' + \ 'street=' + street_number + ' ' + \ street + '&county=' + municipality \ + '&city=' + city + '&state=' + state \ + '&postalcode=' + postalcode + '&country=' + country return ret_url @classmethod def __setup__(cls): super(DomiciliaryUnit, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Domiciliary Unit must be unique !'), ] # Use the template as in Product category. class MedicamentCategory(ModelSQL, ModelView): 'Medicament Category' __name__ = 'gnuhealth.medicament.category' name = fields.Char('Name', required=True, translate=True) parent = fields.Many2One( 'gnuhealth.medicament.category', 'Parent', select=True) childs = fields.One2Many( 'gnuhealth.medicament.category', 'parent', string='Children') @classmethod def __register__(cls, module_name): cursor = Transaction().cursor super(MedicamentCategory, cls).__register__(module_name) # Upgrade from GNU Health 1.8.1: moved who essential medicines cursor.execute( "UPDATE ir_model_data " "SET module = REPLACE(module, %s, %s) " "WHERE (fs_id like 'em%%' OR fs_id = 'medicament') " " AND module = %s", ('health', 'health_who_essential_medicines', module_name,)) @classmethod def __setup__(cls): super(MedicamentCategory, cls).__setup__() cls._order.insert(0, ('name', 'ASC')) @classmethod def validate(cls, categories): super(MedicamentCategory, cls).validate(categories) cls.check_recursion(categories, rec_name='name') def get_rec_name(self, name): if self.parent: return self.parent.get_rec_name(name) + ' / ' + self.name else: return self.name class Medicament(ModelSQL, ModelView): 'Medicament' __name__ = 'gnuhealth.medicament' _rec_name = 'active_component' name = fields.Many2One( 'product.product', 'Product', required=True, domain=[('is_medicament', '=', True)], help='Product Name') active_component = fields.Char( 'Active component', translate=True, help='Active Component') category = fields.Many2One( 'gnuhealth.medicament.category', 'Category', select=True) therapeutic_action = fields.Char( 'Therapeutic effect', help='Therapeutic action') composition = fields.Text('Composition', help='Components') indications = fields.Text('Indication', help='Indications') dosage = fields.Text('Dosage Instructions', help='Dosage / Indications') overdosage = fields.Text('Overdosage', help='Overdosage') pregnancy_warning = fields.Boolean( 'Pregnancy Warning', help='The drug represents risk to pregnancy or lactancy') pregnancy = fields.Text( 'Pregnancy and Lactancy', help='Warnings for Pregnant Women') pregnancy_category = fields.Selection([ (None, ''), ('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D'), ('X', 'X'), ('N', 'N'), ], 'Pregnancy Category', help='** FDA Pregancy Categories ***\n' 'CATEGORY A :Adequate and well-controlled human studies have failed' ' to demonstrate a risk to the fetus in the first trimester of' ' pregnancy (and there is no evidence of risk in later' ' trimesters).\n\n' 'CATEGORY B : Animal reproduction studies have failed todemonstrate a' ' risk to the fetus and there are no adequate and well-controlled' ' studies in pregnant women OR Animal studies have shown an adverse' ' effect, but adequate and well-controlled studies in pregnant women' ' have failed to demonstrate a risk to the fetus in any' ' trimester.\n\n' 'CATEGORY C : Animal reproduction studies have shown an adverse' ' effect on the fetus and there are no adequate and well-controlled' ' studies in humans, but potential benefits may warrant use of the' ' drug in pregnant women despite potential risks. \n\n ' 'CATEGORY D : There is positive evidence of human fetal risk based' ' on adverse reaction data from investigational or marketing' ' experience or studies in humans, but potential benefits may warrant' ' use of the drug in pregnant women despite potential risks.\n\n' 'CATEGORY X : Studies in animals or humans have demonstrated fetal' ' abnormalities and/or there is positive evidence of human fetal risk' ' based on adverse reaction data from investigational or marketing' ' experience, and the risks involved in use of the drug in pregnant' ' women clearly outweigh potential benefits.\n\n' 'CATEGORY N : Not yet classified') presentation = fields.Text('Presentation', help='Packaging') adverse_reaction = fields.Text('Adverse Reactions') storage = fields.Text('Storage Conditions') notes = fields.Text('Extra Info') @classmethod def __register__(cls, module_name): cursor = Transaction().cursor super(Medicament, cls).__register__(module_name) # Upgrade from GNU Health 1.8.1: moved who essential medicines cursor.execute( "UPDATE ir_model_data " "SET module = REPLACE(module, %s, %s) " "WHERE fs_id like 'meds_em%%' AND module = %s", ('health', 'health_who_essential_medicines', module_name,)) def get_rec_name(self, name): return self.name.name @classmethod def check_xml_record(cls, records, values): return True class PathologyCategory(ModelSQL, ModelView): 'Disease Categories' __name__ = 'gnuhealth.pathology.category' name = fields.Char('Category Name', required=True, translate=True) parent = fields.Many2One( 'gnuhealth.pathology.category', 'Parent Category', select=True) childs = fields.One2Many( 'gnuhealth.pathology.category', 'parent', 'Children Category') @classmethod def __setup__(cls): super(PathologyCategory, cls).__setup__() cls._order.insert(0, ('name', 'ASC')) @classmethod def validate(cls, categories): super(PathologyCategory, cls).validate(categories) cls.check_recursion(categories, rec_name='name') def get_rec_name(self, name): if self.parent: return self.parent.get_rec_name(name) + ' / ' + self.name else: return self.name class PathologyGroup(ModelSQL, ModelView): 'Pathology Groups' __name__ = 'gnuhealth.pathology.group' name = fields.Char( 'Name', required=True, translate=True, help='Group name') code = fields.Char( 'Code', required=True, help='for example MDG6 code will contain the Millennium Development' ' Goals # 6 diseases : Tuberculosis, Malaria and HIV/AIDS') desc = fields.Char('Short Description', required=True) info = fields.Text('Detailed information') @classmethod def __register__(cls, module_name): # Upgrade from GNU Health 1.4.5 super(PathologyGroup, cls).__register__(module_name) cursor = Transaction().cursor table = TableHandler(cursor, cls, module_name) # Drop old foreign key and change to char name table.drop_fk('name') table.alter_type('name', 'varchar') # Drop group column. No longer required table.drop_column('group') # Migration from 2.4: drop required on sequence table.not_null_action('sequence', action='remove') class Pathology(ModelSQL, ModelView): 'Diseases' __name__ = 'gnuhealth.pathology' name = fields.Char( 'Name', required=True, translate=True, help='Disease name') code = fields.Char( 'Code', required=True, help='Specific Code for the Disease (eg, ICD-10)') category = fields.Many2One( 'gnuhealth.pathology.category', 'Main Category', help='Select the main category for this disease This is usually' ' associated to the standard. For instance, the chapter on the ICD-10' ' will be the main category for de disease') groups = fields.One2Many( 'gnuhealth.disease_group.members', 'name', 'Groups', help='Specify the groups this pathology belongs. Some' ' automated processes act upon the code of the group') chromosome = fields.Char('Affected Chromosome', help='chromosome number') protein = fields.Char( 'Protein involved', help='Name of the protein(s) affected') gene = fields.Char('Gene', help='Name of the gene(s) affected') info = fields.Text('Extra Info') @classmethod def __setup__(cls): super(Pathology, cls).__setup__() cls._sql_constraints += [ ('code_uniq', 'UNIQUE(code)', 'The disease code must be unique'), ] # DISEASE GROUP MEMBERS class DiseaseMembers(ModelSQL, ModelView): 'Disease group members' __name__ = 'gnuhealth.disease_group.members' name = fields.Many2One('gnuhealth.pathology', 'Disease', readonly=True) disease_group = fields.Many2One( 'gnuhealth.pathology.group', 'Group', required=True) class ProcedureCode(ModelSQL, ModelView): 'Medical Procedures' __name__ = 'gnuhealth.procedure' name = fields.Char('Code', required=True) description = fields.Char('Long Text', translate=True) # Search by the Procedure code or the description @classmethod def search_rec_name(cls, name, clause): field = None for field in ('name', 'description'): procedures = cls.search([(field,) + clause[1:]], limit=1) if procedures: break if procedures: return [(field,) + clause[1:]] return [(cls._rec_name,) + clause[1:]] # Include code + description in result def get_rec_name(self, name): return (self.name + ' : ' + self.description) class InsurancePlan(ModelSQL, ModelView): 'Insurance Plan' __name__ = 'gnuhealth.insurance.plan' _rec_name = 'company' name = fields.Many2One( 'product.product', 'Plan', required=True, domain=[('is_insurance_plan', '=', True)], help='Insurance company plan') company = fields.Many2One( 'party.party', 'Insurance Company', required=True, domain=[('is_insurance_company', '=', True)]) is_default = fields.Boolean( 'Default plan', help='Check if this is the default plan when assigning this insurance' ' company to a patient') notes = fields.Text('Extra info') def get_rec_name(self, name): return self.name.name class Insurance(ModelSQL, ModelView): 'Insurance' __name__ = 'gnuhealth.insurance' _rec_name = 'number' # Insurance associated to an individual name = fields.Many2One('party.party', 'Owner') number = fields.Char('Number', required=True) company = fields.Many2One( 'party.party', 'Insurance Company', required=True, select=True, domain=[('is_insurance_company', '=', True)]) member_since = fields.Date('Member since') member_exp = fields.Date('Expiration date') category = fields.Char( 'Category', help='Insurance company plan / category') insurance_type = fields.Selection([ (None, ''), ('state', 'State'), ('labour_union', 'Labour Union / Syndical'), ('private', 'Private'), ], 'Insurance Type', select=True) plan_id = fields.Many2One( 'gnuhealth.insurance.plan', 'Plan', help='Insurance company plan', domain=[('company', '=', Eval('company'))]) notes = fields.Text('Extra Info') def get_rec_name(self, name): return (self.company.name + ' : ' + self.number) class AlternativePersonID (ModelSQL, ModelView): 'Alternative person ID' __name__ = 'gnuhealth.person_alternative_identification' name = fields.Many2One('party.party', 'Party', readonly=True) code = fields.Char('Code', required=True) alternative_id_type = fields.Selection( [ ('country_id', 'Country of origin SSN'), ('passport', 'Passport'), ('other', 'Other'), ], 'ID type', required=True, sort=False,) comments = fields.Char('Comments') class PartyPatient (ModelSQL, ModelView): 'Party' __name__ = 'party.party' activation_date = fields.Date( 'Activation date', help='Date of activation of the party') alias = fields.Char('Alias', help='Common name that the Party is reffered') ref = fields.Char( 'SSN', help='Patient Social Security Number or equivalent', states={'invisible': Not(Bool(Eval('is_person')))}) unidentified = fields.Boolean( 'Unidentified', help='Patient is currently unidentified', states={'invisible': Not(Bool(Eval('is_person')))}) is_person = fields.Boolean( 'Person', on_change_with=['is_person', 'is_patient', 'is_doctor'], help='Check if the party is a person.') is_patient = fields.Boolean( 'Patient', states={'invisible': Not(Bool(Eval('is_person')))}, help='Check if the party is a patient') is_doctor = fields.Boolean( 'Health Prof', states={'invisible': Not(Bool(Eval('is_person')))}, help='Check if the party is a health professional') is_institution = fields.Boolean( 'Institution', help='Check if the party is a Medical Center') is_insurance_company = fields.Boolean( 'Insurance Company', help='Check if the party is an Insurance Company') is_pharmacy = fields.Boolean( 'Pharmacy', help='Check if the party is a Pharmacy') lastname = fields.Char('Last Name', help='Last Name') dob = fields.Date('DoB', help='Date of Birth') sex = fields.Selection([ (None, ''), ('m', 'Male'), ('f', 'Female'), ], 'Sex', states={'required': Bool(Eval('is_person'))}) photo = fields.Binary('Picture') ethnic_group = fields.Many2One('gnuhealth.ethnicity', 'Ethnic group') marital_status = fields.Selection([ (None, ''), ('s', 'Single'), ('m', 'Married'), ('c', 'Concubinage'), ('w', 'Widowed'), ('d', 'Divorced'), ('x', 'Separated'), ], 'Marital Status', sort=False) citizenship = fields.Many2One( 'country.country', 'Citizenship', help='Country of Citizenship') residence = fields.Many2One( 'country.country', 'Country of Residence', help='Country of Residence') alternative_identification = fields.Boolean( 'Alternative ID', help='Other type of ' 'identification, not the official SSN from this country health' ' center. Examples : Passport, foreign ID,..') alternative_ids = fields.One2Many( 'gnuhealth.person_alternative_identification', 'name', 'Alternative IDs', states={'invisible': Not(Bool(Eval('alternative_identification')))}) insurance = fields.One2Many('gnuhealth.insurance', 'name', 'Insurance') internal_user = fields.Many2One( 'res.user', 'Internal User', help='In GNU Health is the user (doctor, nurse) that logins.When the' ' party is a doctor or a health professional, it will be the user' ' that maps the doctor\'s party name. It must be present.', states={ 'invisible': Not(Bool(Eval('is_doctor'))), 'required': Bool(Eval('is_doctor')), }) insurance_company_type = fields.Selection([ (None, ''), ('state', 'State'), ('labour_union', 'Labour Union / Syndical'), ('private', 'Private'), ], 'Insurance Type', select=True) insurance_plan_ids = fields.One2Many( 'gnuhealth.insurance.plan', 'company', 'Insurance Plans') du = fields.Many2One('gnuhealth.du', 'Domiciliary Unit') @classmethod def write(cls, parties, vals): # We use this method overwrite to make the fields that have a unique # constraint get the NULL value at PostgreSQL level, and not the value # '' coming from the client if vals.get('ref') == '': vals['ref'] = None return super(PartyPatient, cls).write(parties, vals) @classmethod def create(cls, vlist): # We use this method overwrite to make the fields that have a unique # constraint get the NULL value at PostgreSQL level, and not the value # '' coming from the client vlist = [x.copy() for x in vlist] for values in vlist: if 'ref' in values and not values['ref']: values['ref'] = None return super(PartyPatient, cls).create(vlist) @classmethod def __setup__(cls): super(PartyPatient, cls).__setup__() cls._sql_constraints += [ ('ref_uniq', 'UNIQUE(ref)', 'The SSN must be unique'), ('internal_user_uniq', 'UNIQUE(internal_user)', 'This health professional is already assigned to a party')] def get_rec_name(self, name): if self.lastname: return self.lastname + ', ' + self.name else: return self.name @classmethod def search_rec_name(cls, name, clause): field = None for field in ('name', 'lastname'): parties = cls.search([(field,) + clause[1:]], limit=1) if parties: break if parties: return [(field,) + clause[1:]] return [(cls._rec_name,) + clause[1:]] def on_change_with_is_person(self): # Set is_person if the party is a health professional or a patient if (self.is_doctor or self.is_patient or self.is_person): return True @classmethod def validate(cls, parties): super(PartyPatient, cls).validate(parties) for party in parties: party.check_person() def check_person(self): # Verify that health professional and patient # are unchecked when is_person is False if not self.is_person and (self.is_patient or self.is_doctor): self.raise_user_error( "The Person field must be set if the party is a health" " professional or a patient") class PartyAddress(ModelSQL, ModelView): 'Party Address' __name__ = 'party.address' relationship = fields.Char( 'Relationship', help='Include the relationship with the person - friend, co-worker,' ' brother,...') relative_id = fields.Many2One( 'party.party', 'Contact', domain=[('is_person', '=', True)], help='Include link to the relative') is_school = fields.Boolean( "School", help="Check this box to mark the school address") is_work = fields.Boolean( "Work", help="Check this box to mark the work address") class ProductCategory(ModelSQL, ModelView): 'Product Category' __name__ = 'product.category' @classmethod def __register__(cls, module_name): cursor = Transaction().cursor super(ProductCategory, cls).__register__(module_name) # Upgrade from GNU Health 1.8.1: moved who essential medicines cursor.execute( "UPDATE ir_model_data " "SET module = REPLACE(module, %s, %s) " "WHERE fs_id like 'prod_medicament%%' AND module = %s", ('health', 'health_who_essential_medicines', module_name,)) class ProductTemplate(ModelSQL, ModelView): 'Product Template' __name__ = 'product.template' @classmethod def __register__(cls, module_name): cursor = Transaction().cursor super(ProductTemplate, cls).__register__(module_name) # Upgrade from GNU Health 1.8.1: moved who essential medicines cursor.execute( "UPDATE ir_model_data " "SET module = REPLACE(module, %s, %s), " " fs_id = REPLACE(fs_id, 'prod_em', 'templ_em') " "WHERE fs_id like 'prod_em%%' AND module = %s " " AND model = %s", ('health', 'health_who_essential_medicines', module_name, 'product.template',)) class Product(ModelSQL, ModelView): 'Product' __name__ = 'product.product' is_medicament = fields.Boolean( 'Medicament', help='Check if the product is a medicament') is_medical_supply = fields.Boolean( 'Medical Supply', help='Check if the product is a medical supply') is_vaccine = fields.Boolean( 'Vaccine', help='Check if the product is a vaccine') is_bed = fields.Boolean( 'Bed', help='Check if the product is a bed on the gnuhealth.center') is_insurance_plan = fields.Boolean( 'Insurance Plan', help='Check if the product is an insurance plan') @classmethod def __register__(cls, module_name): cursor = Transaction().cursor super(Product, cls).__register__(module_name) # Upgrade from GNU Health 1.8.1: moved who essential medicines cursor.execute( "UPDATE ir_model_data " "SET module = REPLACE(module, %s, %s) " "WHERE fs_id like 'prod_em%%' AND module = %s " " AND model = %s", ('health', 'health_who_essential_medicines', module_name, 'product.product',)) @classmethod def check_xml_record(cls, records, values): return True # GNU HEALTH SEQUENCES class GnuHealthSequences(ModelSingleton, ModelSQL, ModelView): 'Standard Sequences for GNU Health' __name__ = 'gnuhealth.sequences' patient_sequence = fields.Property(fields.Many2One( 'ir.sequence', 'Patient Sequence', required=True, domain=[('code', '=', 'gnuhealth.patient')])) appointment_sequence = fields.Property(fields.Many2One( 'ir.sequence', 'Appointment Sequence', required=True, domain=[('code', '=', 'gnuhealth.appointment')])) prescription_sequence = fields.Property(fields.Many2One( 'ir.sequence', 'Prescription Sequence', required=True, domain=[('code', '=', 'gnuhealth.prescription.order')])) # PATIENT GENERAL INFORMATION class PatientData(ModelSQL, ModelView): 'Patient related information' __name__ = 'gnuhealth.patient' # Get the patient age in the following format : 'YEARS MONTHS DAYS' # It will calculate the age of the patient while the patient is alive. # When the patient dies, it will show the age at time of death. def patient_age(self, name): def compute_age_from_dates(patient_dob, patient_deceased, patient_dod, patient_sex): now = datetime.now() if (patient_dob): dob = datetime.strptime(str(patient_dob), '%Y-%m-%d') if patient_deceased: dod = datetime.strptime( str(patient_dod), '%Y-%m-%d %H:%M:%S') delta = relativedelta(dod, dob) deceased = ' (deceased)' else: delta = relativedelta(now, dob) deceased = '' years_months_days = str(delta.years) + 'y ' \ + str(delta.months) + 'm ' \ + str(delta.days) + 'd' + deceased else: years_months_days = 'No DoB !' # Return the age in format y m d when the caller is the field name if name == 'age': return years_months_days # Return if the patient is in the period of childbearing age >10 is # the caller is childbearing_potential if (name == 'childbearing_age' and patient_dob): if (delta.years >= 11 and delta.years <= 55 and patient_sex == 'f'): return True else: return False return compute_age_from_dates(self.dob, self.deceased, self.dod, self.sex) name = fields.Many2One( 'party.party', 'Patient', required=True, domain=[ ('is_patient', '=', True), ('is_person', '=', True), ], states={'readonly': Bool(Eval('name'))}, help="Person associated to this patient") lastname = fields.Function( fields.Char('Lastname'), 'get_patient_lastname', searcher='search_patient_lastname') ssn = fields.Function( fields.Char('SSN'), 'get_patient_ssn', searcher='search_patient_ssn') identification_code = fields.Char( 'ID', readonly=True, help='Patient Identifier provided by the Health Center.Is not the' ' Social Security Number') family = fields.Many2One( 'gnuhealth.family', 'Family', help='Family Code') current_insurance = fields.Many2One( 'gnuhealth.insurance', 'Insurance', domain=[('name', '=', Eval('name'))], depends=['name'], help='Insurance information. You may choose from the different' ' insurances belonging to the patient') current_address = fields.Many2One( 'party.address', 'Temp. Addr', domain=[('party', '=', Eval('name'))], depends=['name'], help="Use this address for temporary contact information. For example \ if the patient is on vacation, you can put the hotel address. \ In the case of a Domiciliary Unit, just link it to the name of the \ contact in the address form.") primary_care_doctor = fields.Many2One( 'gnuhealth.physician', 'GP', help='Current General Practitioner / Family Doctor') # Removed in 2.0 . PHOTO It's now a functional field # Retrieves the information from the party. photo = fields.Function(fields.Binary('Picture'), 'get_patient_photo') # Removed in 2.0 . DOB It's now a functional field # Retrieves the information from the party. # dob = fields.Date('DoB', help='Date of Birth') dob = fields.Function(fields.Date('DoB'), 'get_patient_dob') age = fields.Function(fields.Char('Age'), 'patient_age') # Removed in 2.0 . SEX It's now a functional field # Retrieves the information from the party. # sex = fields.Selection([ # ('m', 'Male'), # ('f', 'Female'), # ], 'Sex', required=True) sex = fields.Function(fields.Selection([ ('m', 'Male'), ('f', 'Female'), ], 'Sex'), 'get_patient_sex') # Removed in 2.0 . MARITAL STATUS It's now a functional field # Retrieves the information from the party. marital_status = fields.Function( fields.Selection([ (None, ''), ('s', 'Single'), ('m', 'Married'), ('c', 'Concubinage'), ('w', 'Widowed'), ('d', 'Divorced'), ('x', 'Separated'), ], 'Marital Status', sort=False), 'get_patient_marital_status') blood_type = fields.Selection([ (None, ''), ('A', 'A'), ('B', 'B'), ('AB', 'AB'), ('O', 'O'), ], 'Blood Type', sort=False) rh = fields.Selection([ (None, ''), ('+', '+'), ('-', '-'), ], 'Rh') # Removed in 2.0 . ETHNIC GROUP is on the party model now # ethnic_group = fields.Many2One('gnuhealth.ethnicity', 'Ethnic group') vaccinations = fields.One2Many( 'gnuhealth.vaccination', 'name', 'Vaccinations') medications = fields.One2Many( 'gnuhealth.patient.medication', 'name', 'Medications') # Removed in 1.6 # prescriptions = fields.One2Many('gnuhealth.prescription.order', 'name', # 'Prescriptions') diseases = fields.One2Many('gnuhealth.patient.disease', 'name', 'Diseases') critical_info = fields.Text( 'Important disease, allergy or procedures' ' information', help='Write any important information on the patient\'s disease,' ' surgeries, allergies, ...') # Removed it in 1.6 # Not used anymore . Now we relate with a shortcut. Clearer # evaluation_ids = fields.One2Many('gnuhealth.patient.evaluation', # 'patient', 'Evaluation') # admissions_ids = fields.One2Many('gnuhealth.patient.admission', 'name', # 'Admission / Discharge') general_info = fields.Text( 'General Information', help='General information about the patient') deceased = fields.Boolean('Deceased', help='Mark if the patient has died') dod = fields.DateTime( 'Date of Death', states={ 'invisible': Not(Bool(Eval('deceased'))), 'required': Bool(Eval('deceased')), }, depends=['deceased']) cod = fields.Many2One( 'gnuhealth.pathology', 'Cause of Death', states={ 'invisible': Not(Bool(Eval('deceased'))), 'required': Bool(Eval('deceased')), }, depends=['deceased']) childbearing_age = fields.Function( fields.Boolean('Potential for Childbearing'), 'patient_age') appointments = fields.One2Many( 'gnuhealth.appointment', 'patient', 'Appointments') @classmethod def __setup__(cls): super(PatientData, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name)', 'The Patient already exists !'), ] def get_patient_dob(self, name): return self.name.dob def get_patient_sex(self, name): return self.name.sex def get_patient_photo(self, name): return self.name.photo def get_patient_ssn(self, name): return self.name.ref def get_patient_marital_status(self, name): return self.name.marital_status @classmethod def search_patient_ssn(cls, name, clause): res = [] value = clause[2] res.append(('name.ref', clause[1], value)) return res def get_patient_lastname(self, name): return self.name.lastname @classmethod def search_patient_lastname(cls, name, clause): res = [] value = clause[2] res.append(('name.lastname', clause[1], value)) return res # Search by the patient name, lastname or SSN @classmethod def search_rec_name(cls, name, clause): field = None for field in ('name', 'lastname', 'ssn'): patients = cls.search([(field,) + clause[1:]], limit=1) if patients: break if patients: return [(field,) + clause[1:]] return [(cls._rec_name,) + clause[1:]] @classmethod def create(cls, vlist): Sequence = Pool().get('ir.sequence') Config = Pool().get('gnuhealth.sequences') vlist = [x.copy() for x in vlist] for values in vlist: if not values.get('identification_code'): config = Config(1) values['identification_code'] = Sequence.get_id( config.patient_sequence.id) return super(PatientData, cls).create(vlist) def get_rec_name(self, name): if self.name.lastname: return self.name.lastname + ', ' + self.name.name else: return self.name.name @classmethod # Update to version 2.0 def __register__(cls, module_name): super(PatientData, cls).__register__(module_name) cursor = Transaction().cursor table = TableHandler(cursor, cls, module_name) # Move Date of Birth from patient to party if table.column_exist('dob'): cursor.execute( 'UPDATE PARTY_PARTY ' 'SET DOB = GNUHEALTH_PATIENT.DOB ' 'FROM GNUHEALTH_PATIENT ' 'WHERE GNUHEALTH_PATIENT.NAME = PARTY_PARTY.ID') table.drop_column('dob') # Move Patient Gender from patient to party if table.column_exist('sex'): cursor.execute( 'UPDATE PARTY_PARTY ' 'SET SEX = GNUHEALTH_PATIENT.SEX ' 'FROM GNUHEALTH_PATIENT ' 'WHERE GNUHEALTH_PATIENT.NAME = PARTY_PARTY.ID') table.drop_column('sex') # Move Patient Photo from patient to party if table.column_exist('photo'): cursor.execute( 'UPDATE PARTY_PARTY ' 'SET PHOTO = GNUHEALTH_PATIENT.PHOTO ' 'FROM GNUHEALTH_PATIENT ' 'WHERE GNUHEALTH_PATIENT.NAME = PARTY_PARTY.ID') table.drop_column('photo') # Move Patient Ethnic Group from patient to party if table.column_exist('ethnic_group'): cursor.execute( 'UPDATE PARTY_PARTY ' 'SET ETHNIC_GROUP = GNUHEALTH_PATIENT.ETHNIC_GROUP ' 'FROM GNUHEALTH_PATIENT ' 'WHERE GNUHEALTH_PATIENT.NAME = PARTY_PARTY.ID') table.drop_column('ethnic_group') # Move Patient Marital Status from patient to party if table.column_exist('marital_status'): cursor.execute( 'UPDATE PARTY_PARTY ' 'SET MARITAL_STATUS = GNUHEALTH_PATIENT.MARITAL_STATUS ' 'FROM GNUHEALTH_PATIENT ' 'WHERE GNUHEALTH_PATIENT.NAME = PARTY_PARTY.ID') table.drop_column('marital_status') # PATIENT DISESASES INFORMATION class PatientDiseaseInfo(ModelSQL, ModelView): 'Patient Disease History' __name__ = 'gnuhealth.patient.disease' name = fields.Many2One('gnuhealth.patient', 'Patient') pathology = fields.Many2One( 'gnuhealth.pathology', 'Disease', required=True, help='Disease') disease_severity = fields.Selection([ (None, ''), ('1_mi', 'Mild'), ('2_mo', 'Moderate'), ('3_sv', 'Severe'), ], 'Severity', select=True, sort=False) is_on_treatment = fields.Boolean('Currently on Treatment') is_infectious = fields.Boolean( 'Infectious Disease', help='Check if the patient has an infectious / transmissible disease') short_comment = fields.Char( 'Remarks', help='Brief, one-line remark of the disease. Longer description will' ' go on the Extra info field') doctor = fields.Many2One( 'gnuhealth.physician', 'Physician', help='Physician who treated or diagnosed the patient') diagnosed_date = fields.Date('Date of Diagnosis') healed_date = fields.Date('Healed') is_active = fields.Boolean('Active disease') age = fields.Integer( 'Age when diagnosed', help='Patient age at the moment of the diagnosis. Can be estimative') pregnancy_warning = fields.Boolean('Pregnancy warning') weeks_of_pregnancy = fields.Integer('Contracted in pregnancy week #') is_allergy = fields.Boolean('Allergic Disease') allergy_type = fields.Selection([ (None, ''), ('da', 'Drug Allergy'), ('fa', 'Food Allergy'), ('ma', 'Misc Allergy'), ('mc', 'Misc Contraindication'), ], 'Allergy type', select=True, sort=False) pcs_code = fields.Many2One( 'gnuhealth.procedure', 'Code', help='Procedure code, for example, ICD-10-PCS Code 7-character string') treatment_description = fields.Char('Treatment Description') date_start_treatment = fields.Date('Start', help='Start of treatment date') date_stop_treatment = fields.Date('End', help='End of treatment date') status = fields.Selection([ (None, ''), ('a', 'acute'), ('c', 'chronic'), ('u', 'unchanged'), ('h', 'healed'), ('i', 'improving'), ('w', 'worsening'), ], 'Status of the disease', select=True, sort=False) extra_info = fields.Text('Extra Info') @classmethod def __setup__(cls): super(PatientDiseaseInfo, cls).__setup__() cls._order.insert(0, ('is_active', 'DESC')) cls._order.insert(1, ('disease_severity', 'DESC')) cls._order.insert(2, ('is_infectious', 'DESC')) cls._order.insert(3, ('diagnosed_date', 'DESC')) cls._constraints += [ ('validate_disease_period', 'end_date_before_start'), ('validate_treatment_dates', 'end_treatment_date_before_start')] cls._error_messages.update({ 'end_date_before_start': 'The HEALED date is BEFORE DIAGNOSED' ' DATE !', 'end_treatment_date_before_start': 'The Treatment END DATE is' ' BEFORE the start date!', }) @staticmethod def default_is_active(): return True def validate_disease_period(self): res = True if (self.healed_date and self.diagnosed_date): if (self.healed_date < self.diagnosed_date): res = False return res def validate_treatment_dates(self): if (self.date_stop_treatment and self.date_start_treatment): if (self.date_stop_treatment < self.date_start_treatment): return False else: return True else: return True # PATIENT APPOINTMENT class Appointment(ModelSQL, ModelView): 'Patient Appointments' __name__ = 'gnuhealth.appointment' name = fields.Char('Appointment ID', readonly=True) doctor = fields.Many2One( 'gnuhealth.physician', 'Physician', select=True, help='Physician\'s Name') patient = fields.Many2One( 'gnuhealth.patient', 'Patient', required=True, select=True, help='Patient Name') appointment_date = fields.DateTime('Date and Time') institution = fields.Many2One( 'party.party', 'Health Center', domain=[('is_institution', '=', True)], help='Medical Center') speciality = fields.Many2One( 'gnuhealth.specialty', 'Specialty', on_change_with=['doctor'], help='Medical Specialty / Sector') state = fields.Selection([ (None, ''), ('confirmed', 'Confirmed'), ('done', 'Done'), ('user_cancelled', 'Cancelled by patient'), ('center_cancelled', 'Cancelled by Health Center'), ('no_show', 'No show') ], 'State', sort=False) urgency = fields.Selection([ (None, ''), ('a', 'Normal'), ('b', 'Urgent'), ('c', 'Medical Emergency'), ], 'Urgency', sort=False) comments = fields.Text('Comments') appointment_type = fields.Selection([ (None, ''), ('ambulatory', 'Ambulatory'), ('outpatient', 'Outpatient'), ('inpatient', 'Inpatient'), ], 'Type', sort=False) visit_type = fields.Selection([ (None, ''), ('new', 'New health condition'), ('followup', 'Followup'), ('chronic', 'Chronic condition checkup'), ('well_child', 'Well Child visit'), ('well_woman', 'Well Woman visit'), ('well_man', 'Well Man visit'), ], 'Visit', sort=False) consultations = fields.Many2One( 'product.product', 'Consultation Services', domain=[('type', '=', 'service')], help='Consultation Services') @classmethod def __setup__(cls): super(Appointment, cls).__setup__() cls._order.insert(0, ('name', 'DESC')) @classmethod def create(cls, vlist): Sequence = Pool().get('ir.sequence') Config = Pool().get('gnuhealth.sequences') vlist = [x.copy() for x in vlist] for values in vlist: if not values.get('name'): config = Config(1) values['name'] = Sequence.get_id( config.appointment_sequence.id) return super(Appointment, cls).create(vlist) @staticmethod def default_doctor(): return HealthProfessional().get_health_professional() @staticmethod def default_urgency(): return 'a' @staticmethod def default_appointment_date(): return datetime.now() @staticmethod def default_appointment_type(): return 'ambulatory' @staticmethod def default_state(): return 'confirmed' @staticmethod def default_institution(): return Transaction().context.get('company') def on_change_with_speciality(self): # Return the Current / Main speciality of the Health Professional # if this speciality has been specified in the HP record. if (self.doctor and self.doctor.main_specialty): specialty = self.doctor.main_specialty.specialty.id return specialty @staticmethod def default_speciality(): # This method will assign the Main specialty to the appointment # if there is a health professional associated to the login user # as a default value. # It will be overwritten if the health professional is modified in # this view, the on_change_with will take effect. # Get Party ID associated to the Health Professional hp_party_id = HealthProfessional().get_health_professional() if hp_party_id: # Retrieve the health professional Main specialty, if assigned health_professional_obj = Pool().get('gnuhealth.physician') health_professional = health_professional_obj.search( [('id', '=', hp_party_id)], limit=1)[0] hp_main_specialty = health_professional.main_specialty if hp_main_specialty: return hp_main_specialty.specialty.id def get_rec_name(self, name): return self.name class AppointmentReport(ModelSQL, ModelView): 'Appointment Report' __name__ = 'gnuhealth.appointment.report' identification_code = fields.Char('Identification Code') ref = fields.Char('SSN') patient = fields.Many2One('gnuhealth.patient', 'Patient') doctor = fields.Many2One('gnuhealth.physician', 'Doctor') age = fields.Function(fields.Char('Age'), 'get_patient_age') sex = fields.Selection([('m', 'Male'), ('f', 'Female')], 'Sex') address = fields.Function(fields.Char('Address'), 'get_address') insurance = fields.Function(fields.Char('Insurance'), 'get_insurance') appointment_date = fields.Date('Date') appointment_date_time = fields.DateTime('Date and Time') diagnosis = fields.Function( fields.Many2One( 'gnuhealth.pathology', 'Presumptive Diagnosis'), 'get_diagnosis') @classmethod def __setup__(cls): super(AppointmentReport, cls).__setup__() cls._order.insert(0, ('appointment_date_time', 'ASC')) @classmethod def table_query(cls): where_clause = ' ' args = [] if Transaction().context.get('date'): where_clause += "AND a.appointment_date >= %s AND " \ "a.appointment_date < %s + integer '1' " args.append(Transaction().context['date']) args.append(Transaction().context['date']) if Transaction().context.get('doctor'): where_clause += 'AND a.doctor = %s ' args.append(Transaction().context['doctor']) return ('SELECT id, create_uid, create_date, write_uid, write_date, ' 'identification_code, ref, patient, sex, ' 'appointment_date, appointment_date_time, doctor ' 'FROM (' 'SELECT a.id, a.create_uid, a.create_date, ' 'a.write_uid, a.write_date, p.identification_code, ' 'r.ref, p.id as patient, r.sex, a.appointment_date, ' 'a.appointment_date as appointment_date_time, ' 'a.doctor ' 'FROM gnuhealth_appointment a, ' 'gnuhealth_patient p, party_party r ' 'WHERE a.patient = p.id ' + where_clause + 'AND p.name = r.id) AS ' + cls._table, args) def get_address(self, name): res = '' if self.patient.name.addresses: res = self.patient.name.addresses[0].full_address return res def get_insurance(self, name): res = '' if self.patient.current_insurance: res = self.patient.current_insurance.company.name return res def get_diagnosis(self, name): Evaluation = Pool().get('gnuhealth.patient.evaluation') res = None evaluations = Evaluation.search([ ('evaluation_date', '=', self.id) ]) if evaluations: evaluation = evaluations[0] if evaluation.diagnosis: res = evaluation.diagnosis.id return res def get_patient_age(self, name): return self.patient.age class OpenAppointmentReportStart(ModelView): 'Open Appointment Report' __name__ = 'gnuhealth.appointment.report.open.start' date = fields.Date('Date', required=True) doctor = fields.Many2One('gnuhealth.physician', 'Doctor', required=True) @staticmethod def default_date(): return datetime.now() @staticmethod def default_doctor(): return HealthProfessional().get_health_professional() class OpenAppointmentReport(Wizard): 'Open Appointment Report' __name__ = 'gnuhealth.appointment.report.open' start = StateView( 'gnuhealth.appointment.report.open.start', 'health.appointments_report_open_start_view_form', [ Button('Cancel', 'end', 'tryton-cancel'), Button('Open', 'open_', 'tryton-ok', default=True), ]) open_ = StateAction('health.act_appointments_report_view_tree') def do_open_(self, action): action['pyson_context'] = PYSONEncoder().encode({ 'date': self.start.date, 'doctor': self.start.doctor.id, }) action['name'] += ' - %s, %s' % (self.start.doctor.name.lastname, self.start.doctor.name.name) return action, {} def transition_open_(self): return 'end' # PATIENT MEDICATION TREATMENT class PatientMedication(ModelSQL, ModelView): 'Patient Medication' __name__ = 'gnuhealth.patient.medication' # Remove inherits to be compatible with Tryton 2.8 # _inherits = {'gnuhealth.medication.template': 'template'} # template = fields.Many2One('gnuhealth.medication.template', # 'Medication') medicament = fields.Many2One( 'gnuhealth.medicament', 'Medicament', required=True, help='Prescribed Medicament') indication = fields.Many2One( 'gnuhealth.pathology', 'Indication', help='Choose a disease for this medicament from the disease list. It' ' can be an existing disease of the patient or a prophylactic.') name = fields.Many2One( 'gnuhealth.patient', 'Patient', readonly=True) doctor = fields.Many2One( 'gnuhealth.physician', 'Physician', help='Physician who prescribed the medicament') is_active = fields.Boolean( 'Active', on_change_with=['discontinued', 'course_completed'], help='Check if the patient is currently taking the medication') discontinued = fields.Boolean( 'Discontinued', on_change_with=['is_active', 'course_completed']) course_completed = fields.Boolean( 'Course Completed', on_change_with=['is_active', 'discontinued']) discontinued_reason = fields.Char( 'Reason for discontinuation', states={ 'invisible': Not(Bool(Eval('discontinued'))), 'required': Bool(Eval('discontinued')), }, depends=['discontinued'], help='Short description for discontinuing the treatment',) adverse_reaction = fields.Text( 'Adverse Reactions', help='Side effects or adverse reactions that the patient experienced') notes = fields.Text('Extra Info') start_treatment = fields.DateTime( 'Start', help='Date of start of Treatment') end_treatment = fields.DateTime( 'End', help='Date of start of Treatment') dose = fields.Float( 'Dose', help='Amount of medication (eg, 250 mg) per dose') dose_unit = fields.Many2One( 'gnuhealth.dose.unit', 'dose unit', help='Unit of measure for the medication to be taken') route = fields.Many2One( 'gnuhealth.drug.route', 'Administration Route', help='Drug administration route code.') form = fields.Many2One( 'gnuhealth.drug.form', 'Form', help='Drug form, such as tablet or gel') qty = fields.Integer( 'x', help='Quantity of units (eg, 2 capsules) of the medicament') duration = fields.Integer( 'Treatment duration', help='Period that the patient must take the medication. in minutes,' ' hours, days, months, years or indefinately') duration_period = fields.Selection([ (None, ''), ('minutes', 'minutes'), ('hours', 'hours'), ('days', 'days'), ('months', 'months'), ('years', 'years'), ('indefinite', 'indefinite'), ], 'Treatment period', sort=False, help='Period that the patient must take the medication in minutes,' ' hours, days, months, years or indefinately') common_dosage = fields.Many2One( 'gnuhealth.medication.dosage', 'Frequency', help='Common / standard dosage frequency for this medicament') admin_times = fields.Char( 'Admin hours', help='Suggested administration hours. For example, at 08:00, 13:00' ' and 18:00 can be encoded like 08 13 18') frequency = fields.Integer( 'Frequency', help='Time in between doses the patient must wait (ie, for 1 pill' ' each 8 hours, put here 8 and select \"hours\" in the unit field') frequency_unit = fields.Selection([ (None, ''), ('seconds', 'seconds'), ('minutes', 'minutes'), ('hours', 'hours'), ('days', 'days'), ('weeks', 'weeks'), ('wr', 'when required'), ], 'unit', select=True, sort=False) frequency_prn = fields.Boolean( 'PRN', help='Use it as needed, pro re nata') @classmethod def __setup__(cls): super(PatientMedication, cls).__setup__() cls._constraints += [ ('validate_medication_dates', 'end_date_before_start'), ] cls._error_messages.update({ 'end_date_before_start': 'The Medication END DATE is BEFORE the' ' start date!', }) @classmethod def __register__(cls, module_name): super(PatientMedication, cls).__register__(module_name) cursor = Transaction().cursor table = TableHandler(cursor, cls, module_name) # Update to version 2.0 # Move data from template to patient medication if table.column_exist('template'): cursor.execute( 'UPDATE gnuhealth_patient_medication ' 'SET medicament = gmt.medicament, ' 'indication = gmt.indication, ' 'dose = gmt.dose, ' 'dose_unit = gmt.dose_unit, ' 'route = gmt.route, ' 'form = gmt.form, ' 'qty = gmt.qty, ' 'common_dosage = gmt.common_dosage, ' 'frequency = gmt.frequency, ' 'frequency_unit = gmt.frequency_unit, ' 'frequency_prn = gmt.frequency_prn, ' 'admin_times = gmt.admin_times, ' 'duration = gmt.duration, ' 'duration_period = gmt.duration_period, ' 'start_treatment = gmt.start_treatment, ' 'end_treatment = gmt.end_treatment ' 'FROM gnuhealth_medication_template gmt ' 'WHERE gnuhealth_patient_medication.template = gmt.id') table.drop_column('template') def on_change_with_is_active(self): return not (self.discontinued or self.course_completed) def on_change_with_discontinued(self): return not (self.is_active or self.course_completed) def on_change_with_course_completed(self): return not (self.is_active or self.discontinued) @staticmethod def default_is_active(): return True @staticmethod def default_frequency_unit(): return 'hours' @staticmethod def default_duration_period(): return 'days' @staticmethod def default_qty(): return 1 def validate_medication_dates(self): res = True if self.end_treatment: if (self.end_treatment < self.start_treatment): res = False return res # PATIENT VACCINATION INFORMATION class PatientVaccination(ModelSQL, ModelView): 'Patient Vaccination information' __name__ = 'gnuhealth.vaccination' def check_vaccine_expiration_date(self): if self.vaccine_expiration_date: if self.vaccine_expiration_date < datetime.date(self.date): return False return True name = fields.Many2One('gnuhealth.patient', 'Patient', readonly=True) vaccine = fields.Many2One( 'product.product', 'Name', required=True, domain=[('is_vaccine', '=', True)], help='Vaccine Name. Make sure that the vaccine (product) has all the' ' proper information at product level. Information such as provider,' ' supplier code, tracking number, etc.. This information must always' ' be present. If available, please copy / scan the vaccine leaflet' ' and attach it to this record') admin_route = fields.Selection([ (None, ''), ('im', 'Intramuscular'), ('sc', 'Subcutaneous'), ('id', 'Intradermal'), ('nas', 'Intranasal'), ('po', 'Oral'), ], 'Route', sort=False) vaccine_expiration_date = fields.Date('Expiration date') vaccine_lot = fields.Char( 'Lot Number', help='Please check on the vaccine (product) production lot numberand' ' tracking number when available !') institution = fields.Many2One( 'party.party', 'Institution', domain=[('is_institution', '=', True)], help='Medical Center where the patient is being or was vaccinated') date = fields.DateTime('Date') dose = fields.Integer('Dose #') next_dose_date = fields.DateTime('Next Dose') observations = fields.Char('Observations') @classmethod def __setup__(cls): super(PatientVaccination, cls).__setup__() cls._sql_constraints = [ ('dose_uniq', 'UNIQUE(name, vaccine, dose)', 'This vaccine dose has been given already to the patient'), ] cls._constraints += [ ('check_vaccine_expiration_date', 'expired_vaccine'), ('validate_next_dose_date', 'next_dose_before_first'), ] cls._error_messages.update({ 'expired_vaccine': 'EXPIRED VACCINE. PLEASE INFORM THE LOCAL ' 'HEALTH AUTHORITIES AND DO NOT USE IT !!!', 'next_dose_before_first': 'The Vaccine next dose is BEFORE the ' 'first one !' }) @staticmethod def default_date(): return datetime.now() @staticmethod def default_dose(): return 1 def validate_next_dose_date(self): if (self.next_dose_date): if (self.next_dose_date < self.date): return False else: return True # If the next dose is not available, then keep going. else: return True class PatientPrescriptionOrder(ModelSQL, ModelView): 'Prescription Order' __name__ = 'gnuhealth.prescription.order' _rec_name = 'prescription_id' patient = fields.Many2One( 'gnuhealth.patient', 'Patient', required=True, on_change=['patient']) prescription_id = fields.Char( 'Prescription ID', readonly=True, help='Type in the ID of this prescription') prescription_date = fields.DateTime('Prescription Date') # In 1.8 we associate the prescribing doctor to the physician name # instead to the old user_id (res.user) user_id = fields.Many2One('res.user', 'Prescribing Doctor', readonly=True) pharmacy = fields.Many2One( 'party.party', 'Pharmacy', domain=[('is_pharmacy', '=', True)]) prescription_line = fields.One2Many( 'gnuhealth.prescription.line', 'name', 'Prescription line') notes = fields.Text('Prescription Notes') pregnancy_warning = fields.Boolean('Pregancy Warning', readonly=True) prescription_warning_ack = fields.Boolean('Prescription verified') doctor = fields.Many2One( 'gnuhealth.physician', 'Prescribing Doctor', readonly=True) @classmethod def __setup__(cls): super(PatientPrescriptionOrder, cls).__setup__() cls._constraints += [ ('check_prescription_warning', 'drug_pregnancy_warning'), ('check_health_professional', 'health_professional_warning'), ] cls._error_messages.update({ 'drug_pregnancy_warning': '== DRUG AND PREGNANCY VERIFICATION ==\n\n' '- IS THE PATIENT PREGNANT ? \n' '- IS PLANNING to BECOME PREGNANT ?\n' '- HOW MANY WEEKS OF PREGNANCY \n\n' '- IS THE PATIENT BREASTFEEDING \n\n' 'Verify and check for safety the prescribed drugs\n', 'health_professional_warning': 'No health professional associated to this user', }) def check_health_professional(self): return self.doctor @staticmethod def default_doctor(): return HealthProfessional().get_health_professional() def check_prescription_warning(self): return self.prescription_warning_ack # Method that makes the doctor to acknowledge if there is any # warning in the prescription def on_change_patient(self): preg_warning = False presc_warning_ack = True if self.patient: # Trigger the warning if the patient is at a childbearing age if (self.patient.childbearing_age): preg_warning = True presc_warning_ack = False return { 'prescription_warning_ack': presc_warning_ack, 'pregnancy_warning': preg_warning, } @staticmethod def default_prescription_date(): return datetime.now() @staticmethod def default_user_id(): User = Pool().get('res.user') user = User(Transaction().user) return int(user.id) @classmethod def create(cls, vlist): Sequence = Pool().get('ir.sequence') Config = Pool().get('gnuhealth.sequences') vlist = [x.copy() for x in vlist] for values in vlist: if not values.get('prescription_id'): config = Config(1) values['prescription_id'] = Sequence.get_id( config.prescription_sequence.id) return super(PatientPrescriptionOrder, cls).create(vlist) @classmethod def copy(cls, prescriptions, default=None): if default is None: default = {} default = default.copy() default['prescription_id'] = None default['prescription_date'] = cls.default_prescription_date() return super(PatientPrescriptionOrder, cls).copy( prescriptions, default=default) # PRESCRIPTION LINE class PrescriptionLine(ModelSQL, ModelView): 'Prescription Line' __name__ = 'gnuhealth.prescription.line' # Remove inherits to be compatible with Tryton 2.8 # _inherits = {'gnuhealth.medication.template': 'template'} # template = fields.Many2One('gnuhealth.medication.template', # 'Medication Template') name = fields.Many2One('gnuhealth.prescription.order', 'Prescription ID') review = fields.DateTime('Review') quantity = fields.Integer( 'Units', help="Number of units of the medicament." " Example : 30 capsules of amoxicillin") refills = fields.Integer('Refills #') allow_substitution = fields.Boolean('Allow substitution') short_comment = fields.Char( 'Comment', help='Short comment on the specific drug') prnt = fields.Boolean( 'Print', help='Check this box to print this line of the prescription.') medicament = fields.Many2One( 'gnuhealth.medicament', 'Medicament', required=True, help='Prescribed Medicament') indication = fields.Many2One( 'gnuhealth.pathology', 'Indication', help='Choose a disease for this medicament from the disease list. It' ' can be an existing disease of the patient or a prophylactic.') start_treatment = fields.DateTime( 'Start', help='Date of start of Treatment') end_treatment = fields.DateTime( 'End', help='Date of start of Treatment') dose = fields.Float( 'Dose', help='Amount of medication (eg, 250 mg) per dose') dose_unit = fields.Many2One( 'gnuhealth.dose.unit', 'dose unit', help='Unit of measure for the medication to be taken') route = fields.Many2One( 'gnuhealth.drug.route', 'Administration Route', help='Drug administration route code.') form = fields.Many2One( 'gnuhealth.drug.form', 'Form', help='Drug form, such as tablet or gel') qty = fields.Integer( 'x', help='Quantity of units (eg, 2 capsules) of the medicament') common_dosage = fields.Many2One( 'gnuhealth.medication.dosage', 'Frequency', help='Common / standard dosage frequency for this medicament') admin_times = fields.Char( 'Admin hours', help='Suggested administration hours. For example, at 08:00, 13:00' ' and 18:00 can be encoded like 08 13 18') frequency = fields.Integer( 'Frequency', help='Time in between doses the patient must wait (ie, for 1 pill' ' each 8 hours, put here 8 and select \"hours\" in the unit field') frequency_unit = fields.Selection([ (None, ''), ('seconds', 'seconds'), ('minutes', 'minutes'), ('hours', 'hours'), ('days', 'days'), ('weeks', 'weeks'), ('wr', 'when required'), ], 'unit', select=True, sort=False) frequency_prn = fields.Boolean('PRN', help='Use it as needed, pro re nata') duration = fields.Integer( 'Treatment duration', help='Period that the patient must take the medication. in minutes,' ' hours, days, months, years or indefinately') duration_period = fields.Selection([ (None, ''), ('minutes', 'minutes'), ('hours', 'hours'), ('days', 'days'), ('months', 'months'), ('years', 'years'), ('indefinite', 'indefinite'), ], 'Treatment period', sort=False, help='Period that the patient must take the medication in minutes,' ' hours, days, months, years or indefinately') @classmethod def __register__(cls, module_name): super(PrescriptionLine, cls).__register__(module_name) cursor = Transaction().cursor table = TableHandler(cursor, cls, module_name) # Update to version 2.0 # Move data from template to prescription line if table.column_exist('template'): cursor.execute( 'UPDATE gnuhealth_prescription_line ' 'SET medicament = gmt.medicament, ' 'indication = gmt.indication, ' 'dose = gmt.dose, ' 'dose_unit = gmt.dose_unit, ' 'route = gmt.route, ' 'form = gmt.form, ' 'qty = gmt.qty, ' 'common_dosage = gmt.common_dosage, ' 'frequency = gmt.frequency, ' 'frequency_unit = gmt.frequency_unit, ' 'frequency_prn = gmt.frequency_prn, ' 'admin_times = gmt.admin_times, ' 'duration = gmt.duration, ' 'duration_period = gmt.duration_period, ' 'start_treatment = gmt.start_treatment, ' 'end_treatment = gmt.end_treatment ' 'FROM gnuhealth_medication_template gmt ' 'WHERE gnuhealth_prescription_line.template = gmt.id') table.drop_column('template') @staticmethod def default_qty(): return 1 @staticmethod def default_duration_period(): return 'days' @staticmethod def default_frequency_unit(): return 'hours' @staticmethod def default_quantity(): return 1 @staticmethod def default_prnt(): return True class PatientEvaluation(ModelSQL, ModelView): 'Patient Evaluation' __name__ = 'gnuhealth.patient.evaluation' def evaluation_duration(self, name): duration = '' if (self.evaluation_endtime and self.evaluation_start): delta = relativedelta( self.evaluation_endtime, self.evaluation_start) duration = str( delta.days*24 + delta.hours*60 + delta.minutes) return duration patient = fields.Many2One('gnuhealth.patient', 'Patient') evaluation_date = fields.Many2One( 'gnuhealth.appointment', 'Appointment', domain=[('patient', '=', Eval('patient'))], depends=['patient'], help='Enter or select the date / ID of the appointment related to' ' this evaluation') evaluation_start = fields.DateTime('Start', required=True) evaluation_endtime = fields.DateTime('End', required=True) evaluation_length = fields.Function( fields.Char( 'Length', help="Duration of the evaluation, in minutes"), 'evaluation_duration') state = fields.Selection([ ('in_progress', 'In progress'), ('done', 'Done'), ], 'State', readonly=True, sort=False) next_evaluation = fields.Many2One( 'gnuhealth.appointment', 'Next Appointment', domain=[('patient', '=', Eval('patient'))], depends=['patient']) user_id = fields.Many2One('res.user', 'Last Changed by', readonly=True) doctor = fields.Many2One( 'gnuhealth.physician', 'Health Prof', help="Health professional that initiates the evaluation." "This health professional might or might not be the same that" " signs and finishes the evaluation." "The evaluation remains in progress state until it is signed" ", when it becomes read-only", readonly=True) signed_by = fields.Many2One( 'gnuhealth.physician', 'Signed by', readonly=True, states={'invisible': Equal(Eval('state'), 'in_progress')}, help="Health Professional that signed the patient evaluation document") specialty = fields.Many2One('gnuhealth.specialty', 'Specialty') visit_type = fields.Selection([ (None, ''), ('new', 'New health condition'), ('followup', 'Followup'), ('chronic', 'Chronic condition checkup'), ('well_child', 'Well Child visit'), ('well_woman', 'Well Woman visit'), ('well_man', 'Well Man visit'), ], 'Visit', sort=False) urgency = fields.Selection([ (None, ''), ('a', 'Normal'), ('b', 'Urgent'), ('c', 'Medical Emergency'), ], 'Urgency', sort=False) information_source = fields.Char( 'Source', help="Source of" "Information, eg : Self, relative, friend ...") reliable_info = fields.Boolean( 'Reliable', help="Uncheck this option" "if the information provided by the source seems not reliable") derived_from = fields.Many2One( 'gnuhealth.physician', 'Derived from', help='Physician who derived the case') derived_to = fields.Many2One( 'gnuhealth.physician', 'Derived to', help='Physician to whom escalate / derive the case') evaluation_type = fields.Selection([ (None, ''), ('ambulatory', 'Ambulatory'), ('outpatient', 'Outpatient'), ('inpatient', 'Inpatient'), ], 'Type', sort=False) chief_complaint = fields.Char('Chief Complaint', help='Chief Complaint') notes_complaint = fields.Text('Complaint details') present_illness = fields.Text('Present Illness') evaluation_summary = fields.Text('Evaluation Summary') glycemia = fields.Float( 'Glycemia', help='Last blood glucose level. Can be approximative.') hba1c = fields.Float( 'Glycated Hemoglobin', help='Last Glycated Hb level. Can be approximative.') cholesterol_total = fields.Integer( 'Last Cholesterol', help='Last cholesterol reading. Can be approximative') hdl = fields.Integer( 'Last HDL', help='Last HDL Cholesterol reading. Can be approximative') ldl = fields.Integer( 'Last LDL', help='Last LDL Cholesterol reading. Can be approximative') tag = fields.Integer( 'Last TAGs', help='Triacylglycerol(triglicerides) level. Can be approximative') systolic = fields.Integer('Systolic Pressure') diastolic = fields.Integer('Diastolic Pressure') bpm = fields.Integer( 'Heart Rate', help='Heart rate expressed in beats per minute') respiratory_rate = fields.Integer( 'Respiratory Rate', help='Respiratory rate expressed in breaths per minute') osat = fields.Integer( 'Oxygen Saturation', help='Oxygen Saturation(arterial).') malnutrition = fields.Boolean( 'Malnutrition', help='Check this box if the patient show signs of malnutrition. If' ' associated to a disease, please encode the correspondent disease' ' on the patient disease history. For example, Moderate' ' protein-energy malnutrition, E44.0 in ICD-10 encoding') dehydration = fields.Boolean( 'Dehydration', help='Check this box if the patient show signs of dehydration. If' ' associated to a disease, please encode the correspondent disease' ' on the patient disease history. For example, Volume Depletion, E86' ' in ICD-10 encoding') temperature = fields.Float( 'Temperature', help='Temperature in celcius') weight = fields.Float('Weight', help='Weight in Kilos') height = fields.Float('Height', help='Height in centimeters, eg 175') bmi = fields.Float( 'Body Mass Index', on_change_with=['weight', 'height', 'bmi']) head_circumference = fields.Float( 'Head Circumference', help='Head circumference') abdominal_circ = fields.Float('Waist') hip = fields.Float('Hip', help='Hip circumference in centimeters, eg 100') whr = fields.Float( 'WHR', help='Waist to hip ratio', on_change_with=['abdominal_circ', 'hip', 'whr']) # DEPRECATION NOTE : SIGNS AND SYMPTOMS FIELDS TO BE REMOVED IN 1.6 . # NOW WE USE A O2M OBJECT TO MAKE IT MORE SCALABLE, CLEARER AND FUNCTIONAL # TO WORK WITH THE CLINICAL FINDINGS OF THE PATIENT loc = fields.Integer( 'Glasgow', on_change_with=['loc_verbal', 'loc_motor', 'loc_eyes'], help='Level of Consciousness - on Glasgow Coma Scale : < 9 severe -' ' 9-12 Moderate, > 13 minor') loc_eyes = fields.Selection([ ('1', 'Does not Open Eyes'), ('2', 'Opens eyes in response to painful stimuli'), ('3', 'Opens eyes in response to voice'), ('4', 'Opens eyes spontaneously'), ], 'Glasgow - Eyes', sort=False) loc_verbal = fields.Selection([ ('1', 'Makes no sounds'), ('2', 'Incomprehensible sounds'), ('3', 'Utters inappropriate words'), ('4', 'Confused, disoriented'), ('5', 'Oriented, converses normally'), ], 'Glasgow - Verbal', sort=False) loc_motor = fields.Selection([ ('1', 'Makes no movement'), ('2', 'Extension to painful stimuli - decerebrate response -'), ('3', 'Abnormal flexion to painful stimuli (decorticate response)'), ('4', 'Flexion / Withdrawal to painful stimuli'), ('5', 'Localizes painful stimuli'), ('6', 'Obeys commands'), ], 'Glasgow - Motor', sort=False) tremor = fields.Boolean( 'Tremor', help='If associated to a disease, please encode it on the patient' ' disease history') violent = fields.Boolean( 'Violent Behaviour', help='Check this box if the patient is agressive or violent at the' ' moment') mood = fields.Selection([ (None, ''), ('n', 'Normal'), ('s', 'Sad'), ('f', 'Fear'), ('r', 'Rage'), ('h', 'Happy'), ('d', 'Disgust'), ('e', 'Euphoria'), ('fl', 'Flat'), ], 'Mood', sort=False) orientation = fields.Boolean( 'Orientation', help='Check this box if the patient is disoriented in time and/or' ' space') memory = fields.Boolean( 'Memory', help='Check this box if the patient has problems in short or long' ' term memory') knowledge_current_events = fields.Boolean( 'Knowledge of Current Events', help='Check this box if the patient can not respond to public' ' notorious events') judgment = fields.Boolean( 'Jugdment', help='Check this box if the patient can not interpret basic scenario' ' solutions') abstraction = fields.Boolean( 'Abstraction', help='Check this box if the patient presents abnormalities in' ' abstract reasoning') vocabulary = fields.Boolean( 'Vocabulary', help='Check this box if the patient lacks basic intelectual capacity,' ' when she/he can not describe elementary objects') calculation_ability = fields.Boolean( 'Calculation Ability', help='Check this box if the patient can not do simple arithmetic' ' problems') object_recognition = fields.Boolean( 'Object Recognition', help='Check this box if the patient suffers from any sort of gnosia' ' disorders, such as agnosia, prosopagnosia ...') praxis = fields.Boolean( 'Praxis', help='Check this box if the patient is unable to make voluntary' 'movements') diagnosis = fields.Many2One( 'gnuhealth.pathology', 'Presumptive Diagnosis', help='Presumptive Diagnosis. If no diagnosis can be made' ', encode the main sign or symptom.') secondary_conditions = fields.One2Many( 'gnuhealth.secondary_condition', 'evaluation', 'Secondary Conditions', help='Other, Secondary' ' conditions found on the patient') diagnostic_hypothesis = fields.One2Many( 'gnuhealth.diagnostic_hypothesis', 'evaluation', 'Hypotheses / DDx', help='Other Diagnostic Hypotheses /' ' Differential Diagnosis (DDx)') signs_and_symptoms = fields.One2Many( 'gnuhealth.signs_and_symptoms', 'evaluation', 'Signs and Symptoms', help='Enter the Signs and Symptoms' ' for the patient in this evaluation.') info_diagnosis = fields.Text('Presumptive Diagnosis: Extra Info') directions = fields.Text('Plan') actions = fields.One2Many( 'gnuhealth.directions', 'name', 'Procedures', help='Procedures / Actions to take') notes = fields.Text('Notes') @classmethod def __setup__(cls): super(PatientEvaluation, cls).__setup__() cls._constraints += [ ('check_health_professional', 'health_professional_warning'), ('validate_evaluation_period', 'end_date_before_start'), ] cls._error_messages.update({ 'health_professional_warning': 'No health professional associated to this user', 'end_date_before_start': 'End time BEFORE evaluation start' }) cls._buttons.update({ 'discharge': { 'invisible': Equal(Eval('state'), 'done'), }, }) def validate_evaluation_period(self): res = True if (self.evaluation_endtime and self.evaluation_start): if (self.evaluation_endtime < self.evaluation_start): res = False return res @classmethod def write(cls, evaluations, vals): # Don't allow to write the record if the evaluation has been done if evaluations[0].state == 'done': cls.raise_user_error( "This evaluation is at state Done\n" "You can no longer modify it.") return super(PatientEvaluation, cls).write(evaluations, vals) # End the evaluation and discharge the patient @classmethod @ModelView.button def discharge(cls, evaluations): evaluation_id = evaluations[0] # Change the state of the evaluation to "Done" # and write the name of the signing doctor signing_hp = HealthProfessional().get_health_professional() if not signing_hp: cls.raise_user_error( "No health professional associated to this user !") cls.write(evaluations, { 'state': 'done', 'signed_by': signing_hp}) def check_health_professional(self): return self.doctor @staticmethod def default_doctor(): return HealthProfessional().get_health_professional() @staticmethod def default_loc_eyes(): return '4' @staticmethod def default_loc_verbal(): return '5' @staticmethod def default_loc_motor(): return '6' @staticmethod def default_loc(): return 15 @staticmethod def default_evaluation_type(): return 'ambulatory' @staticmethod def default_state(): return 'in_progress' def on_change_with_bmi(self): if self.height and self.weight: if (self.height > 0): return self.weight / ((self.height / 100) ** 2) return 0 def on_change_with_loc(self): return int(self.loc_motor) + int(self.loc_eyes) + int(self.loc_verbal) @staticmethod def default_information_source(): return 'Self' @staticmethod def default_reliable_info(): return True @staticmethod def default_evaluation_start(): return datetime.now() # Calculate the WH ratio def on_change_with_whr(self): waist = self.abdominal_circ hip = self.hip if (hip > 0): whr = waist / hip else: whr = 0 return whr def get_rec_name(self, name): return str(self.evaluation_start) # PATIENT EVALUATION DIRECTIONS class Directions(ModelSQL, ModelView): 'Patient Directions' __name__ = 'gnuhealth.directions' name = fields.Many2One( 'gnuhealth.patient.evaluation', 'Evaluation', readonly=True) procedure = fields.Many2One( 'gnuhealth.procedure', 'Procedure', required=True) comments = fields.Char('Comments') # SECONDARY CONDITIONS ASSOCIATED TO THE PATIENT IN THE EVALUATION class SecondaryCondition(ModelSQL, ModelView): 'Secondary Conditions' __name__ = 'gnuhealth.secondary_condition' evaluation = fields.Many2One( 'gnuhealth.patient.evaluation', 'Evaluation', readonly=True) pathology = fields.Many2One( 'gnuhealth.pathology', 'Pathology', required=True) comments = fields.Char('Comments') # PATIENT EVALUATION OTHER DIAGNOSTIC HYPOTHESES class DiagnosticHypothesis(ModelSQL, ModelView): 'Other Diagnostic Hypothesis' __name__ = 'gnuhealth.diagnostic_hypothesis' evaluation = fields.Many2One( 'gnuhealth.patient.evaluation', 'Evaluation', readonly=True) pathology = fields.Many2One( 'gnuhealth.pathology', 'Pathology', required=True) comments = fields.Char('Comments') # PATIENT EVALUATION CLINICAL FINDINGS (SIGNS AND SYMPTOMS) class SignsAndSymptoms(ModelSQL, ModelView): 'Evaluation Signs and Symptoms' __name__ = 'gnuhealth.signs_and_symptoms' evaluation = fields.Many2One( 'gnuhealth.patient.evaluation', 'Evaluation', readonly=True) sign_or_symptom = fields.Selection([ (None, ''), ('sign', 'Sign'), ('symptom', 'Symptom')], 'Subjective / Objective', required=True) clinical = fields.Many2One( 'gnuhealth.pathology', 'Sign or Symptom', domain=[('code', 'like', 'R%')], required=True) comments = fields.Char('Comments') # HEALTH CENTER / HOSPITAL INFRASTRUCTURE class HospitalBuilding(ModelSQL, ModelView): 'Hospital Building' __name__ = 'gnuhealth.hospital.building' name = fields.Char( 'Name', required=True, help='Name of the building within the institution') institution = fields.Many2One( 'party.party', 'Institution', domain=[('is_institution', '=', True)], help='Medical Center') code = fields.Char('Code') extra_info = fields.Text('Extra Info') class HospitalUnit(ModelSQL, ModelView): 'Hospital Unit' __name__ = 'gnuhealth.hospital.unit' name = fields.Char( 'Name', required=True, help='Name of the unit, eg Neonatal, Intensive Care, ...') institution = fields.Many2One( 'party.party', 'Institution', domain=[('is_institution', '=', True)], help='Medical Center') code = fields.Char('Code') extra_info = fields.Text('Extra Info') class HospitalOR(ModelSQL, ModelView): 'Operating Room' __name__ = 'gnuhealth.hospital.or' name = fields.Char( 'Name', required=True, help='Name of the Operating Room') institution = fields.Many2One( 'party.party', 'Institution', domain=[('is_institution', '=', True)], help='Medical Center') building = fields.Many2One( 'gnuhealth.hospital.building', 'Building', select=True) unit = fields.Many2One('gnuhealth.hospital.unit', 'Unit') extra_info = fields.Text('Extra Info') @classmethod def __setup__(cls): super(HospitalOR, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'UNIQUE(name, institution)', 'The Operating Room code must be unique per Health' ' Center'), ] class HospitalWard(ModelSQL, ModelView): 'Hospital Ward' __name__ = 'gnuhealth.hospital.ward' name = fields.Char('Name', required=True, help='Ward / Room code') institution = fields.Many2One( 'party.party', 'Institution', domain=[('is_institution', '=', True)], help='Medical Center') building = fields.Many2One('gnuhealth.hospital.building', 'Building') floor = fields.Integer('Floor Number') unit = fields.Many2One('gnuhealth.hospital.unit', 'Unit') private = fields.Boolean( 'Private', help='Check this option for private room') bio_hazard = fields.Boolean( 'Bio Hazard', help='Check this option if there is biological hazard') number_of_beds = fields.Integer( 'Number of beds', help='Number of patients per ward') telephone = fields.Boolean('Telephone access') ac = fields.Boolean('Air Conditioning') private_bathroom = fields.Boolean('Private Bathroom') guest_sofa = fields.Boolean('Guest sofa-bed') tv = fields.Boolean('Television') internet = fields.Boolean('Internet Access') refrigerator = fields.Boolean('Refrigerator') microwave = fields.Boolean('Microwave') gender = fields.Selection(( ('men', 'Men Ward'), ('women', 'Women Ward'), ('unisex', 'Unisex'), ), 'Gender', required=True, sort=False) state = fields.Selection(( (None, ''), ('beds_available', 'Beds available'), ('full', 'Full'), ('na', 'Not available'), ), 'Status', sort=False) extra_info = fields.Text('Extra Info') @staticmethod def default_gender(): return 'unisex' @staticmethod def default_number_of_beds(): return 1 class HospitalBed(ModelSQL, ModelView): 'Hospital Bed' __name__ = 'gnuhealth.hospital.bed' _rec_name = 'telephone_number' name = fields.Many2One( 'product.product', 'Bed', required=True, domain=[('is_bed', '=', True)], help='Bed Number') ward = fields.Many2One( 'gnuhealth.hospital.ward', 'Ward', help='Ward or room') bed_type = fields.Selection(( ('gatch', 'Gatch Bed'), ('electric', 'Electric'), ('stretcher', 'Stretcher'), ('low', 'Low Bed'), ('low_air_loss', 'Low Air Loss'), ('circo_electric', 'Circo Electric'), ('clinitron', 'Clinitron'), ), 'Bed Type', required=True, sort=False) telephone_number = fields.Char( 'Telephone Number', help='Telephone number / Extension') extra_info = fields.Text('Extra Info') state = fields.Selection(( ('free', 'Free'), ('reserved', 'Reserved'), ('occupied', 'Occupied'), ('na', 'Not available'), ), 'Status', readonly=True, sort=False) @staticmethod def default_bed_type(): return 'gatch' @staticmethod def default_state(): return 'free' def get_rec_name(self, name): if self.name: return self.name.name @classmethod def search_rec_name(cls, name, clause): return [('name',) + clause[1:]]
