class PatientRounding(ModelSQL, ModelView): 'Patient Rounding' __name__ = 'gnuhealth.patient.rounding' name = fields.Many2One('gnuhealth.inpatient.registration', 'Registration Code', required=True) health_professional = fields.Many2One('gnuhealth.healthprofessional', 'Health Professional', readonly=True) evaluation_start = fields.DateTime('Start', required=True) evaluation_end = fields.DateTime('End', required=True) environmental_assessment = fields.Char('Environment', help="Environment" " assessment . State any disorder in the room.") # The 6 P's of rounding pain = fields.Boolean('Pain', help="Check if the patient is in pain") pain_level = fields.Integer('Pain', help="Enter the pain level, from 1 to " "10") potty = fields.Boolean('Potty', help="Check if the patient needs to " "urinate / defecate") position = fields.Boolean('Position', help="Check if the patient needs to " "be repositioned or is unconfortable") proximity = fields.Boolean('Proximity', help="Check if personal items, " "water, alarm, ... are not in easy reach") pump = fields.Boolean('Pumps', help="Check if there is any issues with " "the pumps - IVs ... ") personal_needs = fields.Boolean('Personal needs', help="Check if the " "patient requests anything") # Vital Signs 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).') temperature = fields.Float('Temperature', help='Temperature in celsius') # Diuresis diuresis = fields.Integer('Diuresis',help="volume in ml") urinary_catheter = fields.Boolean('Urinary Catheter') #Glycemia glycemia = fields.Integer('Glycemia', help='Blood Glucose level') depression = fields.Boolean('Depression signs', help="Check this if the " "patient shows signs of depression") evolution = fields.Selection([ ('n', 'Status Quo'), ('i', 'Improving'), ('w', 'Worsening'), ], 'Evolution', required=True, help="Check your judgement of current " "patient condition", sort=False) round_summary = fields.Text('Round Summary') warning = fields.Boolean('Warning', help="Check this box to alert the " "supervisor about this patient rounding. It will be shown in red in " "the rounding list") procedures = fields.One2Many('gnuhealth.rounding_procedure', 'name', 'Procedures', help="List of the procedures in this rounding. Please " "enter the first one as the main procedure") @classmethod def __setup__(cls): super(PatientRounding, cls).__setup__() cls._error_messages.update({ 'health_professional_warning': 'No health professional associated to this user', }) @classmethod def validate(cls, roundings): super(PatientRounding, cls).validate(roundings) for rounding in roundings: rounding.check_health_professional() def check_health_professional(self): if not self.health_professional: self.raise_user_error('health_professional_warning') @staticmethod def default_health_professional(): 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_healthprof=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_healthprofessional WHERE \ name = %s LIMIT 1', (partner_id[0],)) doctor_id = cursor.fetchone() return int(doctor_id[0]) @staticmethod def default_evaluation_start(): return datetime.now()
class BlogPost(Workflow, ModelSQL, ModelView): 'Blog Post' __name__ = 'blog.post' _rec_name = 'title' title = fields.Char('Title', required=True, select=True, states=STATES) uri = fields.Char( 'URI', required=True, select=True, on_change_with=['title', 'uri'], states=STATES, ) nereid_user = fields.Many2One('nereid.user', 'Nereid User', required=True, select=True, states=STATES) post_date = fields.DateTime('Post Date', states=STATES) content = fields.Text('Content', states=STATES) allow_guest_comments = fields.Boolean('Allow Guest Comments ?', select=True) comments = fields.One2Many('blog.post.comment', 'post', 'Comments') published_comments = fields.Function( fields.One2Many('blog.post.comment', None, 'Published Comments'), 'get_published_comments') state = fields.Selection([ ('Draft', 'Draft'), ('Published', 'Published'), ('Archived', 'Archived'), ], 'State', readonly=True) @staticmethod def default_state(): return 'Draft' def get_published_comments(self, name): "Returns the published comments, i.e., comments not marked as spam" Comment = Pool().get('blog.post.comment') return map( int, Comment.search([('post', '=', self.id), ('is_spam', '=', False)])) @classmethod def __setup__(cls): super(BlogPost, cls).__setup__() cls._sql_constraints += [ ('nereid_user_uri_uniq', 'UNIQUE(nereid_user, uri)', 'URI must be unique for a nereid user'), ] cls._transitions |= set(( ('Draft', 'Published'), ('Published', 'Draft'), ('Draft', 'Archived'), ('Published', 'Archived'), ('Archived', 'Draft'), )) cls._buttons.update({ 'publish': { 'invisible': Eval('state') != 'Draft', }, 'draft': { 'invisible': Eval('state') == 'Draft', }, 'archive': { 'invisible': Eval('state') != 'Published', } }) cls.per_page = 10 @classmethod @ModelView.button @Workflow.transition('Draft') def draft(cls, posts): pass @classmethod @ModelView.button @Workflow.transition('Published') def publish(cls, posts): cls.write(posts, {'post_date': datetime.utcnow()}) @classmethod @ModelView.button @Workflow.transition('Archived') def archive(cls, posts): pass def on_change_with_uri(self): if self.title and not self.uri: return slugify(self.title) return self.uri def serialize(self, purpose=None): ''' Return serializable dict for `self` ''' res = { 'id': self.id, 'title': self.title, 'uri': self.uri, 'post_date': self.post_date.isoformat() if self.post_date else None, 'allow_guest_comments': self.allow_guest_comments, 'state': self.state, 'nereid_user': self.nereid_user.id, 'displayName': self.rec_name, } if purpose == 'activity_stream': res['objectType'] = self.__name__ res['content'] = self.content[0:50] else: res['content'] = self.content return res @classmethod @route('/post/-new', methods=['GET', 'POST']) @login_required def new_post(cls): """Create a new post """ post_form = BlogPostForm(request.form) if request.method == 'POST' and post_form.validate(): post, = cls.create([{ 'title': post_form.title.data, 'uri': post_form.uri.data, 'content': post_form.content.data, 'nereid_user': request.nereid_user.id, 'allow_guest_comments': post_form.allow_guest_comments.data, }]) if post_form.publish.data: cls.publish([post]) flash('Your post has been published.') else: flash('Your post has been saved.') if request.is_xhr: return jsonify(success=True, item=post.serialize()) return redirect( url_for('blog.post.render', user_id=post.nereid_user.id, uri=post.uri)) if request.is_xhr: return jsonify( success=request.method != 'POST', # False for POST, else True errors=post_form.errors or None, ) return render_template('blog_post_form.jinja', form=post_form) @classmethod def get_post_for_uri(cls, uri): """ Return post for current user and uri """ posts = cls.search([ ('uri', '=', uri), ('nereid_user', '=', request.nereid_user.id), ]) if not posts: abort(404) return posts[0] @classmethod @route('/post/<uri>/-edit', methods=['GET', 'POST']) @login_required def edit_post_for_uri(cls, uri): """ Edit an existing post from uri """ return cls.get_post_for_uri(uri).edit_post() @route('/post/<int:active_id>/-edit', methods=['GET', 'POST']) @login_required def edit_post(self): """ Edit an existing post """ if self.nereid_user != request.nereid_user: abort(404) # Search for a post with same uri post_form = BlogPostForm(request.form, obj=self) with Transaction().set_context(blog_id=self.id): if request.method == 'POST' and post_form.validate(): self.title = post_form.title.data self.content = post_form.content.data self.allow_guest_comments = post_form.allow_guest_comments.data self.save() flash('Your post has been updated.') if request.is_xhr: return jsonify(success=True, item=self.serialize()) return redirect( url_for('blog.post.render', user_id=self.nereid_user.id, uri=self.uri)) if request.is_xhr: return jsonify( success=request.method != 'POST', # False for POST, else True errors=post_form.errors or None, ) return render_template('blog_post_edit.jinja', form=post_form, post=self) @classmethod @route('/post/<uri>/-change-state', methods=['POST']) @login_required def change_state_for_uri(cls, uri): "Change the state of the post for uri" return cls.get_post_for_uri(uri).change_state() @route('/post/<int:active_id>/-change-state', methods=['POST']) @login_required def change_state(self): "Change the state of the post" if self.nereid_user != request.nereid_user: abort(404) state = request.form.get('state') assert (state in ('publish', 'archive', 'draft')) getattr(self, str(state))([self]) if request.is_xhr: return jsonify({ 'success': True, 'new_state': self.state, }) flash('Your post is now %s' % self.state) return redirect( url_for('blog.post.render', user_id=self.nereid_user.id, uri=self.uri)) @classmethod @route('/post/<uri>/-change-guest-permission', methods=['POST']) @login_required def change_guest_permission_for_uri(cls, uri): "Change guest permission for uri" return cls.get_post_for_uri(uri).change_guest_permission() @route('/post/<int:active_id>/-change-guest-permission', methods=['POST']) @login_required def change_guest_permission(self): "Change guest permission of the post" if self.nereid_user != request.nereid_user: abort(404) allow_guest_comment = request.form.get('allow_guest_comments') self.allow_guest_comments = True if allow_guest_comment == 'true' \ else False self.save() if request.is_xhr: return jsonify({ 'success': True, }) return redirect( url_for('blog.post.render', user_id=self.nereid_user.id, uri=self.uri)) @classmethod @route('/post/<int:user_id>/<uri>') def render(cls, user_id, uri): "Render the blog post" NereidUser = Pool().get('nereid.user') if 're_captcha_public' in CONFIG.options and request.is_guest_user: comment_form = GuestCommentForm( captcha={'ip_address': request.remote_addr}) else: comment_form = PostCommentForm() user = NereidUser(user_id) posts = cls.search([ ('nereid_user', '=', user.id), ('uri', '=', uri), ]) if not posts: abort(404) # if only one post is found then it is rendered and # if more than one are found then the first one is rendered post = posts[0] if not (post.state == 'Published' or request.nereid_user == post.nereid_user): abort(403) if request.is_xhr: return jsonify(post.serialize()) return render_template('blog_post.jinja', post=post, comment_form=comment_form, poster=user) @classmethod @route('/posts/<int:user_id>') @route('/posts/<int:user_id>/<int:page>') def render_list(cls, user_id, page=1): """Render the blog posts for a user This should render the list of only published posts of the user """ NereidUser = Pool().get('nereid.user') user = NereidUser(user_id) posts = Pagination(cls, [ ('nereid_user', '=', user.id), ('state', '=', 'Published'), ], page, cls.per_page) if request.is_xhr: return jsonify({ 'has_next': posts.has_next, 'has_prev': posts.has_prev, 'items': [post.serialize() for post in posts], }) return render_template('blog_posts.jinja', posts=posts, poster=user) @classmethod @route('/posts/-my') @route('/posts/-my/<int:page>') @login_required def my_posts(self, page=1): """Render all the posts of the logged in user """ posts = Pagination(self, [ ('nereid_user', '=', request.nereid_user.id), ], page, self.per_page) if request.is_xhr: return jsonify({ 'has_next': posts.has_next, 'has_prev': posts.has_prev, 'items': [post.serialize() for post in posts], }) return render_template('my_blog_posts.jinja', posts=posts) @classmethod @route('/post/<int:user_id>/<uri>/-comment', methods=['GET', 'POST']) def add_comment(cls, user_id, uri): ''' Add a comment ''' warnings.warn( "add_comment will be deprecated in 3.2 use render_comment instead.", DeprecationWarning, ) # Comments can only be added to published posts posts = cls.search([ ('nereid_user', '=', user_id), ('uri', '=', uri), ], limit=1) if not posts: abort(404) return posts[0].render_comments() @route('/post/<int:active_id>/-comment', methods=['GET', 'POST']) def render_comments(self): """ Render comments GET: Return json of all the comments of this post. POST: Create new comment for this post. """ if self.state != 'Published': abort(404) # Add re_captcha if the configuration has such an option and user # is guest if 're_captcha_public' in CONFIG.options and request.is_guest_user: comment_form = GuestCommentForm( request.form, captcha={'ip_address': request.remote_addr}) else: comment_form = PostCommentForm(request.form) if request.method == 'GET': if self.nereid_user == request.nereid_user: return jsonify(comments=[ comment.serialize() for comment in self.comments ]) return jsonify(comments=[ comment.serialize() for comment in self.comments if not comment.is_spam ]) # If post does not allow guest comments, # then dont allow guest user to comment if not self.allow_guest_comments and request.is_guest_user: flash('Guests are not allowed to write comments') if request.is_xhr: return jsonify( success=False, errors=['Guests are not allowed to write comments']) return redirect( url_for('blog.post.render', user_id=self.nereid_user.id, uri=self.uri)) if request.method == 'POST' and comment_form.validate(): self.write( [self], { 'comments': [('create', [{ 'nereid_user': current_user.id if not current_user.is_anonymous() else None, 'name': current_user.display_name if not current_user.is_anonymous() else comment_form.name.data, 'content': comment_form.content.data, }])] }) if request.is_xhr: return jsonify(success=True) if comment_form.validate() \ else jsonify(success=False, errors=comment_form.errors) return redirect( url_for('blog.post.render', user_id=self.nereid_user.id, uri=self.uri))
class DiseaseNotification(ModelView, ModelSQL): 'Disease Notification' __name__ = 'gnuhealth.disease_notification' active = fields.Boolean('Active') patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True, states=RO_SAVED) status = fields.Selection(NOTIFICATION_STATES, 'Status', required=True, sort=False) status_display = fields.Function(fields.Char('State'), 'get_selection_display') name = fields.Char('Code', size=18, states={'readonly': True}, required=True) tracking_code = fields.Char('Case Tracking Code', select=True) date_notified = fields.DateTime('Date reported', required=True, states=RO_SAVED) date_received = fields.DateTime( 'Date received', states=RO_NEW, help='Date received the National Surveillance Unit') diagnosis = fields.Many2One('gnuhealth.pathology', 'Suspected Diagnosis', states=RO_STATE_END, required=False) diagnosis_confirmed = fields.Many2One( 'gnuhealth.pathology', 'Confirmed Diagnosis', required=False, states={'invisible': Eval('id', 0) < 0}) symptoms = fields.One2Many('gnuhealth.disease_notification.symptom', 'name', 'Symptoms', states=RO_STATE_END) date_onset = fields.Date('Date of Onset', help='Date of onset of the illness') epi_week_onset = fields.Function( fields.Char('Epi. Week of onset', size=8, help='Week of onset (epidemiological)'), 'epi_week') date_seen = fields.Date('Date Seen', help='Date seen by a medical officer') reporting_facility = fields.Many2One( 'gnuhealth.institution', 'Reporting facility', states={'invisible': Bool(Eval('reporting_facility_other'))}) reporting_facility_other = fields.Char( 'Other Reporting location', help='Used when the report came from an institution not found above', states={'invisible': Bool(Eval('reporting_facility'))}) encounter = fields.Many2One('gnuhealth.encounter', 'Clinical Encounter', domain=[('patient', '=', Eval('patient')), ('start_time', '<', Eval('date_notified'))]) specimen_taken = fields.Boolean('Samples Taken') specimens = fields.One2Many('gnuhealth.disease_notification.specimen', 'notification', 'Samples', states=ONLY_IF_LAB) hospitalized = fields.Boolean('Admitted to hospital') admission_date = fields.Date('Date admitted', states=ONLY_IF_ADMITTED) hospital = fields.Many2One('gnuhealth.institution', 'Hospital', states=ONLY_IF_ADMITTED) ward = fields.Char('Ward', states=ONLY_IF_ADMITTED) deceased = fields.Boolean('Deceased') date_of_death = fields.Date('Date of Death', states=ONLY_IF_DEAD) healthprof = fields.Many2One('gnuhealth.healthprofessional', 'Reported by') comments = fields.Text('Additional comments') comments_short = fields.Function(fields.Char('Comments'), 'short_comment') risk_factors = fields.One2Many( 'gnuhealth.disease_notification.risk_disease', 'notification', 'Risk Factors', help="Other conditions of merit") hx_travel = fields.Boolean('Recent Foreign Travels', help="History of Overseas travel in the last" " 4 - 6 weeks") hx_locations = fields.One2Many( 'gnuhealth.disease_notification.travel', 'notification', 'Places visited', states={'invisible': ~Eval('hx_travel', False)}) age = fields.Function( fields.Char('Age', size=8, help='age at date of onset'), 'get_patient_age') sex = fields.Function(fields.Selection(SEX_OPTIONS, 'Sex'), 'get_patient_field', searcher='search_patient_field') puid = fields.Function(fields.Char('UPI', size=12), 'get_patient_field', searcher='search_patient_field') state_changes = fields.One2Many( 'gnuhealth.disease_notification.statechange', 'notification', 'Status Changes', order=[('create_date', 'DESC')], readonly=True) ir_received = fields.Boolean('IR Received') # medical_record_num = fields.Function(fields.Char('Medical Record Numbers'), # 'get_patient_field', # searcher='search_patient_field') @classmethod def __setup__(cls): super(DiseaseNotification, cls).__setup__() cls._order = [('date_onset', 'DESC')] cls._sql_error_messages = { 'unique_name': 'There is another notification with this code' } cls._sql_constraints = [('name_uniq', 'UNIQUE(name)', 'The code must be unique.')] @classmethod def get_patient_field(cls, instances, name): return dict([(x.id, getattr(x.patient, name)) for x in instances]) @classmethod def get_patient_age(cls, instances, name): ''' Uses the age function in the database to calculate the age at the date specified. :param: instance_refs - a list of tuples with (id, ref_date) ''' c = Transaction().cursor tbl = cls.__table__() qry = "\n".join([ "SET intervalstyle TO 'iso_8601';", "SELECT a.id as id, btrim(lower(" "regexp_replace(AGE(a.date_onset, c.dob)::varchar, " "'([YMD])', '\\1 ', 'g')), 'p ') as showage ", "from " + str(tbl) + " as a ", " inner join gnuhealth_patient as b on a.patient=b.id", " inner join party_party c on b.name=c.id" " where a.id in %s ;" ]) qry_parm = tuple(map(int, instances)) c.execute(qry, (qry_parm, )) outx = c.fetchall() outd = dict([x for x in outx]) return outd @classmethod def order_puid(cls, tables): table, _ = tables[None] return [Column(table, 'patient')] # not really a UPI/PUID sort, but good enough @classmethod def short_comment(cls, instances, name): return dict( map( lambda x: (x.id, x.comments and ' '.join(x.comments.split( '\n'))[:40] or ''), instances)) @classmethod def search_patient_field(cls, field_name, clause): return replace_clause_column(clause, 'patient.%s' % field_name) _rec_name = 'name' # @classmethod # def get_rec_name(cls, records, name): # return dict([(x.id, x.name) for x in records]) @classmethod def search_rec_name(cls, field_name, clause): _, operand, val = clause return ['OR', ('patient.puid', operand, val), ('name', operand, val)] @fields.depends('reporting_facility') def on_change_reporting_facility(self, *arg, **kwarg): return {'reporting_facility_other': ''} @fields.depends('diagnosis', 'name') def on_change_with_name(self): curname = self.name if self.diagnosis: newcode = '%s:' % self.diagnosis.code if curname: newcode = '%s:%s' % (self.diagnosis.code, curname) return newcode elif curname and ':' in curname: return curname[curname.index(':') + 1:] @fields.depends('diagnosis_confirmed', 'status') def on_change_diagnosis_confirmed(self): if self.diagnosis_confirmed and self.status == 'suspected': return {'status': 'confirmed'} else: return {} @fields.depends('encounter') def on_change_with_date_seen(self): return self.encounter.start_time.date() if self.encounter else None @staticmethod def default_healthprof(): healthprof_model = Pool().get('gnuhealth.healthprofessional') return healthprof_model.get_health_professional() @staticmethod def default_status(): return 'waiting' @staticmethod def default_active(): return True @classmethod def create(cls, vlist): pool = Pool() Sequence = pool.get('ir.sequence') Config = pool.get('gnuhealth.sequences') config = Config(1) vlist = [x.copy() for x in vlist] for values in vlist: val_name = values.get('name', '') if not val_name or val_name.endswith(':'): newcode = Sequence.get_id(config.notification_sequence.id) values['name'] = '%s%s' % (values['name'], newcode) elif ':' in val_name and not values.get('diagnosis', False): values['name'] = val_name[val_name.index(':') + 1:] if values.get('state_changes', False): pass else: values['state_changes'] = [('create', [{ 'orig_state': None, 'target_state': values['status'], 'healthprof': values['healthprof'] }])] return super(DiseaseNotification, cls).create(vlist) @classmethod def write(cls, records, values, *args): '''create a NotificationStateChange when the status changes''' healthprof = DiseaseNotification.default_healthprof() to_make = [] irecs = iter((records, values) + args) for recs, vals in zip(irecs, irecs): newstate = vals.get('status', False) if newstate: for rec in recs: if rec.status != newstate: to_make.append({ 'notification': rec.id, 'orig_state': rec.status, 'target_state': newstate, 'healthprof': healthprof }) return_val = super(DiseaseNotification, cls).write(records, values, *args) # nsc = Notification State Change if to_make: nsc = Pool().get('gnuhealth.disease_notification.statechange') nsc.create(to_make) return return_val @classmethod def validate(cls, records): now = {date: date.today(), datetime: datetime.now(), type(None): None} date_fields = [ 'date_onset', 'date_seen', 'date_notified', 'admission_date', 'date_of_death' ] # we need to ensure that none of these fields are in the future for rec in records: if rec.encounter: if rec.encounter.patient != rec.patient: cls.raise_user_error('Invalid encounter selected.' 'Different patient') for fld in date_fields: val = getattr(rec, fld) if val and val > now[type(val)]: val_name = getattr(cls, fld).string cls.raise_user_error('%s cannot be in the future', (val_name, )) @classmethod def copy(cls, records, default=None): if default is None: default = {} default = default.copy() default.update(diagnosis=None, state_changes=[]) if 'name' in default: del default['name'] return super(DiseaseNotification, cls).copy(records, default=default) @classmethod def epi_week(cls, instances, name): def ewcalc(k): return (k.id, epiweek_str(k.date_onset) if k.date_onset else '') if name == 'epi_week_onset': return dict(map(ewcalc, instances)) @classmethod def get_selection_display(cls, instances, field_name): real_field = field_name[:0 - len('_display')] field_selections = cls._fields[real_field].selection xdict = dict(filter(lambda x: x[0], field_selections)) return dict( map(lambda x: (x.id, xdict.get(getattr(x, real_field), '')), instances))
class PatientSESAssessment(ModelSQL, ModelView): 'Socioeconomics and Family Functionality Assessment' __name__ = 'gnuhealth.ses.assessment' STATES = {'readonly': Eval('state') == 'done'} patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True, states = STATES) gender = fields.Function(fields.Selection([ (None, ''), ('m', 'Male'), ('f', 'Female'), ], 'Gender'), 'get_patient_gender', searcher='search_patient_gender') assessment_date = fields.DateTime('Date', help="Assessment date", states = STATES) computed_age = fields.Function(fields.Char( 'Age', help="Computed patient age at the moment of the evaluation"), 'patient_age_at_assessment') health_professional = fields.Many2One( 'gnuhealth.healthprofessional', 'Health Professional', readonly=True, help="Health professional" ) du = fields.Many2One( 'gnuhealth.du', 'DU', help="Domiciliary Unit", states = STATES) ses = fields.Selection([ (None, ''), ('0', 'Lower'), ('1', 'Lower-middle'), ('2', 'Middle'), ('3', 'Middle-upper'), ('4', 'Higher'), ], 'Socioeconomics', help="SES - Socioeconomic Status", sort=False, states = STATES) 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'), ], 'Housing conditions', help="Housing and sanitary living conditions", sort=False, states = STATES) occupation = fields.Many2One('gnuhealth.occupation','Occupation', states = STATES) income = fields.Selection([ (None, ''), ('l', 'Low'), ('m', 'Medium'), ('h', 'High'), ], 'Income', sort=False, states = STATES) fam_apgar_help = fields.Selection([ (None, ''), ('0', 'None'), ('1', 'Moderately'), ('2', 'Very much'), ], 'Help from family', help="Is the patient satisfied with the level of help coming from " \ "the family when there is a problem ?", sort=False, states = STATES) fam_apgar_discussion = fields.Selection([ (None, ''), ('0', 'None'), ('1', 'Moderately'), ('2', 'Very much'), ], 'Problems discussion', help="Is the patient satisfied with the level talking over the " \ "problems as family ?", sort=False, states = STATES) fam_apgar_decisions = fields.Selection([ (None, ''), ('0', 'None'), ('1', 'Moderately'), ('2', 'Very much'), ], 'Decision making', help="Is the patient satisfied with the level of making important " \ "decisions as a group ?", sort=False, states = STATES) fam_apgar_timesharing = fields.Selection([ (None, ''), ('0', 'None'), ('1', 'Moderately'), ('2', 'Very much'), ], 'Time sharing', help="Is the patient satisfied with the level of time that they " \ "spend together ?", sort=False, states = STATES) fam_apgar_affection = fields.Selection([ (None, ''), ('0', 'None'), ('1', 'Moderately'), ('2', 'Very much'), ], 'Family affection', help="Is the patient satisfied with the level of affection coming " \ "from the family ?", sort=False, states = STATES) fam_apgar_score = fields.Integer('Score', help="Total Family APGAR \n" \ "7 - 10 : Functional Family \n" \ "4 - 6 : Some level of disfunction \n" \ "0 - 3 : Severe disfunctional family \n", states = STATES) education = fields.Selection([ (None, ''), ('0', 'None'), ('1', 'Incomplete Primary School'), ('2', 'Primary School'), ('3', 'Incomplete Secondary School'), ('4', 'Secondary School'), ('5', 'University'), ], 'Education Level', help="Education Level", sort=False, states = STATES) notes = fields.Text('Notes', states = STATES) state = fields.Selection([ (None, ''), ('in_progress', 'In progress'), ('done', 'Done'), ], 'State', readonly=True, sort=False) signed_by = fields.Many2One( 'gnuhealth.healthprofessional', 'Signed by', readonly=True, states={'invisible': Equal(Eval('state'), 'in_progress')}, help="Health Professional that finished the patient evaluation") @fields.depends('fam_apgar_help', 'fam_apgar_timesharing', 'fam_apgar_discussion', 'fam_apgar_decisions', 'fam_apgar_affection') def on_change_with_fam_apgar_score(self): fam_apgar_help = int(self.fam_apgar_help or '0') fam_apgar_timesharing = int(self.fam_apgar_timesharing or '0') fam_apgar_discussion = int(self.fam_apgar_discussion or '0') fam_apgar_decisions = int(self.fam_apgar_decisions or '0') fam_apgar_affection = int(self.fam_apgar_affection or '0') total = (fam_apgar_help + fam_apgar_timesharing + fam_apgar_discussion + fam_apgar_decisions + fam_apgar_affection) return total @staticmethod def default_assessment_date(): return datetime.now() @staticmethod def default_state(): return 'in_progress' @staticmethod def default_health_professional(): pool = Pool() HealthProf= pool.get('gnuhealth.healthprofessional') health_professional = HealthProf.get_health_professional() return health_professional # Show the gender and age upon entering the patient # These two are function fields (don't exist at DB level) @fields.depends('patient') def on_change_patient(self): gender=None age='' self.gender = self.patient.gender self.computed_age = self.patient.age occupation=education=du=housing=None if (self.patient and self.patient.name.occupation): occupation = self.patient.name.occupation if (self.patient and self.patient.name.education): education = self.patient.name.education if (self.patient and self.patient.name.du): du = self.patient.name.du if (self.patient and self.patient.name.du): housing = self.patient.name.du.housing self.occupation = occupation self.education = education self.du = du self.housing = housing def get_patient_gender(self, name): return self.patient.gender @classmethod def search_patient_gender(cls, name, clause): res = [] value = clause[2] res.append(('patient.name.gender', clause[1], value)) return res @classmethod @ModelView.button def end_assessment(cls, assessments): assessment_id = assessments[0] # Change the state of the assessment to "Done" HealthProf= Pool().get('gnuhealth.healthprofessional') signing_hp = HealthProf.get_health_professional() cls.write(assessments, { 'state': 'done', 'signed_by': signing_hp, }) def patient_age_at_assessment(self, name): if (self.patient.name.dob and self.assessment_date): rdelta = relativedelta (self.assessment_date.date(), self.patient.name.dob) years_months_days = str(rdelta.years) + 'y ' \ + str(rdelta.months) + 'm ' \ + str(rdelta.days) + 'd' return years_months_days else: return None @classmethod def __setup__(cls): super(PatientSESAssessment, cls).__setup__() cls._buttons.update({ 'end_assessment': {'invisible': Equal(Eval('state'), 'done')} }) cls._order.insert(0, ('assessment_date', 'DESC')) @classmethod def search_rec_name(cls, name, clause): if clause[1].startswith('!') or clause[1].startswith('not '): bool_op = 'AND' else: bool_op = 'OR' return [bool_op, ('patient',) + tuple(clause[1:]), ]
class ImportDataDateTime(ModelSQL): "Import Data DateTime" __name__ = 'test.import_data.datetime' datetime = fields.DateTime('DateTime')
class WebsiteStoreView(ModelSQL, ModelView): """ Magento Website Store View A store needs one or more store views to be browse-able in the front-end. It allows for multiple presentations of a store. Most implementations use store views for different languages """ __name__ = 'magento.store.store_view' name = fields.Char('Name', required=True) code = fields.Char('Code', required=True, readonly=True) magento_id = fields.Integer('Magento ID', readonly=True, required=True) store = fields.Many2One( 'magento.website.store', 'Store', required=True, readonly=True, ) instance = fields.Function(fields.Many2One('magento.instance', 'Instance'), 'get_instance') website = fields.Function( fields.Many2One('magento.instance.website', 'Website'), 'get_website') company = fields.Function(fields.Many2One('company.company', 'Company'), 'get_company') last_order_import_time = fields.DateTime('Last Order Import Time') last_order_export_time = fields.DateTime("Last Order Export Time") #: Last time at which the shipment status was exported to magento last_shipment_export_time = fields.DateTime('Last shipment export time') #: Checking this will make sure that only the done shipments which have a #: carrier and tracking reference are exported. export_tracking_information = fields.Boolean( 'Export tracking information', help='Checking this will make sure' ' that only the done shipments which have a carrier and tracking ' 'reference are exported. This will update carrier and tracking ' 'reference on magento for the exported shipments as well.') def get_instance(self, name): """ Returns instance related to store :param name: Field name """ return self.store.instance.id def get_website(self, name): """ Returns website related to store :param name: Field name """ return self.store.website.id def get_company(self, name): """ Returns company related to store :param name: Field name """ return self.store.company.id @classmethod def __setup__(cls): """ Setup the class before adding to pool """ super(WebsiteStoreView, cls).__setup__() cls._sql_constraints += [ ('magento_id_store_unique', 'UNIQUE(magento_id, store)', 'A store view must be unique in a store') ] cls._error_messages.update({ "states_not_found": 'No order states found for importing orders! ' 'Please configure the order states on magento instance', }) cls._buttons.update({ 'import_orders_button': {}, 'export_order_status_button': {} }) @classmethod def find_or_create(cls, store, values): """ Looks for the store view whose `values` are sent by magento against the store with `store` in tryton. If a record exists for this, return that else create a new one and return :param store: Active record of store :param values: Dictionary of values for store view sent by magento :return: Actice record of record created/found """ store_views = cls.search([('store', '=', store.id), ('magento_id', '=', int(values['store_id'])) ]) if store_views: return store_views[0] return cls.create([{ 'name': values['name'], 'code': values['code'], 'store': store.id, 'magento_id': int(values['store_id']), }])[0] @classmethod @ModelView.button_action('magento.wizard_import_orders') def import_orders_button(cls, store_views): """ Calls wizard to import orders for store view :param store_views: List of active records of store views """ pass @classmethod @ModelView.button_action('magento.wizard_export_order_status') def export_order_status_button(cls, store_views): """ Calls wizard to export order status for store view :param store_views: List of active records of store views """ pass def import_order_from_store_view(self): """ Imports sale from store view :return: List of active record of sale imported """ Sale = Pool().get('sale.sale') MagentoOrderState = Pool().get('magento.order_state') new_sales = [] instance = self.instance with Transaction().set_context({ 'magento_instance': instance.id, 'magento_website': self.website.id, 'magento_store_view': self.id, }): order_states = MagentoOrderState.search([ ('instance', '=', instance.id), ('use_for_import', '=', True) ]) order_states_to_import_in = map(lambda state: state.code, order_states) if not order_states_to_import_in: self.raise_user_error("states_not_found") with magento.Order(instance.url, instance.api_user, instance.api_key) as order_api: # Filter orders with date and store_id using list() # then get info of each order using info() # and call find_or_create_using_magento_data on sale filter = { 'store_id': { '=': self.magento_id }, 'state': { 'in': order_states_to_import_in }, } if self.last_order_import_time: filter.update({ 'updated_at': { 'gteq': self.last_order_import_time }, }) self.write([self], {'last_order_import_time': datetime.utcnow()}) orders = order_api.list(filter) for order in orders: new_sales.append( Sale.find_or_create_using_magento_data( order_api.info(order['increment_id']))) return new_sales def export_order_status(self, store_views=None): """ Export sales orders status to magento. :param store_views: List of active record of store view """ if not store_views: store_views = self.search([]) for store_view in store_views: store_view.export_order_status_for_store_view() def export_order_status_for_store_view(self): """ Export sale orders to magento for the current store view. If last export time is defined, export only those orders which are updated after last export time. :return: List of active records of sales exported """ Sale = Pool().get('sale.sale') exported_sales = [] domain = [('magento_store_view', '=', self.id)] if self.last_order_export_time: domain = [('write_date', '>=', self.last_order_export_time)] sales = Sale.search(domain) self.write([self], {'last_order_export_time': datetime.utcnow()}) for sale in sales: exported_sales.append(sale.export_order_status_to_magento()) return exported_sales @classmethod def import_orders(cls, store_views=None): """ Import orders from magento for store views :param store_views: Active record list of store views """ if not store_views: store_views = cls.search([]) for store_view in store_views: store_view.import_order_from_store_view() @classmethod def export_shipment_status(cls, store_views=None): """ Export Shipment status for shipments related to current store view. This method is called by cron. :param store_views: List of active records of store_view """ if not store_views: store_views = cls.search([]) for store_view in store_views: # Set the instance in context with Transaction().set_context( magento_instance=store_view.instance.id): store_view.export_shipment_status_to_magento() def export_shipment_status_to_magento(self): """ Exports shipment status for shipments to magento, if they are shipped :return: List of active record of shipment """ Shipment = Pool().get('stock.shipment.out') Sale = Pool().get('sale.sale') instance = self.instance sale_domain = [ ('magento_store_view', '=', self.id), ('shipment_state', '=', 'sent'), ('magento_id', '!=', None), ] if self.last_shipment_export_time: sale_domain.append( ('write_date', '>=', self.last_shipment_export_time)) sales = Sale.search(sale_domain) for sale in sales: if not sale.shipments: sales.pop(sale) continue # Get the increment id from the sale reference increment_id = sale.reference[len(instance.order_prefix ):len(sale.reference)] self.write([self], {'last_shipment_export_time': datetime.utcnow()}) for shipment in sale.shipments: try: # Some checks to make sure that only valid shipments are # being exported if shipment.is_tracking_exported_to_magento or \ shipment.state not in ('packed', 'done') or \ shipment.magento_increment_id: sales.pop(sale) continue with magento.Shipment(instance.url, instance.api_user, instance.api_key) as shipment_api: item_qty_map = {} for move in shipment.outgoing_moves: if isinstance(move.origin, SaleLine) \ and move.origin.magento_id: # This is done because there can be multiple # lines with the same product and they need # to be send as a sum of quanitities item_qty_map.setdefault( str(move.origin.magento_id), 0) item_qty_map[str(move.origin.magento_id)] += \ move.quantity shipment_increment_id = shipment_api.create( order_increment_id=increment_id, items_qty=item_qty_map) Shipment.write( sale.shipments, { 'magento_increment_id': shipment_increment_id, }) if self.export_tracking_information and ( shipment.tracking_number and shipment.carrier): shipment.export_tracking_info_to_magento() except xmlrpclib.Fault, fault: if fault.faultCode == 102: # A shipment already exists for this order, # we cannot do anything about it. # Maybe it was already exported earlier or was created # separately on magento # Hence, just continue continue return sales
class PaperArchive(ModelSQL, ModelView): 'Location of PAPER Patient Clinical History' __name__ = 'gnuhealth.paper_archive' patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True, help="Patient associated to this newborn baby") legacy = fields.Char('Legacy Code', help="If existing, please enter" \ " the old / legacy code associated to this Clinical History") location = fields.Many2One( 'gnuhealth.hospital.unit', 'Unit', required=True, help="Location / Unit where this clinical history document" \ " should reside.") hc_status = fields.Selection(( ('archived', 'Archived'), ('borrowed', 'Borrowed'), ('lost', 'Lost'), ), 'Status', required=True, sort=False) current_location = fields.Many2One( 'gnuhealth.hospital.unit', 'Current Location', help="Location / Unit where this clinical history document" \ " should reside.") identification_code = fields.Function(fields.Char('Code'), 'get_patient_history', searcher='search_patient_code') requested_by = fields.Many2One( 'party.party', 'Resquested by', domain=[('is_person', '=', True)], help="Person who last requested the document") request_date = fields.DateTime("Request Date") return_date = fields.DateTime("Returned Date") comments = fields.Char("Comments") @classmethod def __setup__(cls): '''Create constraints for both the legacy number and patient''' super(PaperArchive, cls).__setup__() cls._sql_constraints = [ ('legacy_uniq', 'UNIQUE(legacy)', 'The History already exists !'), ('patient_uniq', 'UNIQUE(patient)', 'The Patient History already exists !'), ] @classmethod def search_patient_code(cls, name, clause): '''Retrieve the Patient Identification Code''' res = [] value = clause[2] res.append(('patient.identification_code', clause[1], value)) return res def get_patient_history(self, name): return self.patient.identification_code
class DateTimeRequired(ModelSQL): 'DateTime Required' __name__ = 'test.datetime_required' datetime = fields.DateTime(string='DateTime', help='Test datetime', required=True)
class DateTimeFormat(ModelSQL): 'DateTime Format' __name__ = 'test.datetime_format' datetime = fields.DateTime(string='DateTime', format='%H:%M')
class Surgery(ModelSQL, ModelView): 'Surgery' __name__ = 'gnuhealth.surgery' def surgery_duration(self, name): if (self.surgery_end_date and self.surgery_date): return self.surgery_end_date - self.surgery_date else: return None def patient_age_at_surgery(self, name): if (self.patient.name.dob and self.surgery_date): rdelta = relativedelta(self.surgery_date.date(), self.patient.name.dob) years_months_days = str(rdelta.years) + 'y ' \ + str(rdelta.months) + 'm ' \ + str(rdelta.days) + 'd' return years_months_days else: return None patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True) admission = fields.Many2One('gnuhealth.appointment', 'Admission') operating_room = fields.Many2One('gnuhealth.hospital.or', 'Operating Room') code = fields.Char('Code', readonly=True, help="Health Center code / sequence") procedures = fields.One2Many( 'gnuhealth.operation', 'name', 'Procedures', help="List of the procedures in the surgery. Please enter the first " "one as the main procedure") supplies = fields.One2Many( 'gnuhealth.surgery_supply', 'name', 'Supplies', help="List of the supplies required for the surgery") pathology = fields.Many2One('gnuhealth.pathology', 'Condition', help="Base Condition / Reason") classification = fields.Selection([ (None, ''), ('o', 'Optional'), ('r', 'Required'), ('u', 'Urgent'), ('e', 'Emergency'), ], 'Urgency', help="Urgency level for this surgery", sort=False) surgeon = fields.Many2One('gnuhealth.healthprofessional', 'Surgeon', help="Surgeon who did the procedure") anesthetist = fields.Many2One('gnuhealth.healthprofessional', 'Anesthetist', help="Anesthetist in charge") surgery_date = fields.DateTime('Date', help="Start of the Surgery") surgery_end_date = fields.DateTime( 'End', states={ 'required': Equal(Eval('state'), 'done'), }, help="Automatically set when the surgery is done." "It is also the estimated end time when confirming the surgery.") surgery_length = fields.Function( fields.TimeDelta('Duration', states={ 'invisible': And(Not(Equal(Eval('state'), 'done')), Not(Equal(Eval('state'), 'signed'))) }, help="Length of the surgery"), 'surgery_duration') state = fields.Selection([ ('draft', 'Draft'), ('confirmed', 'Confirmed'), ('cancelled', 'Cancelled'), ('in_progress', 'In Progress'), ('done', 'Done'), ('signed', 'Signed'), ], 'State', readonly=True, sort=False) signed_by = fields.Many2One( 'gnuhealth.healthprofessional', 'Signed by', readonly=True, states={'invisible': Not(Equal(Eval('state'), 'signed'))}, help="Health Professional that signed this surgery document") # age is deprecated in GNU Health 2.0 age = fields.Char('Estimative Age', help="Use this field for historical purposes, \ when no date of surgery is given") computed_age = fields.Function( fields.Char('Age', help="Computed patient age at the moment of the surgery"), 'patient_age_at_surgery') gender = fields.Function(fields.Selection([ (None, ''), ('m', 'Male'), ('f', 'Female'), ('f-m', 'Female -> Male'), ('m-f', 'Male -> Female'), ], 'Gender'), 'get_patient_gender', searcher='search_patient_gender') description = fields.Char('Description') preop_mallampati = fields.Selection([ (None, ''), ('Class 1', 'Class 1: Full visibility of tonsils, uvula and soft ' 'palate'), ('Class 2', 'Class 2: Visibility of hard and soft palate, ' 'upper portion of tonsils and uvula'), ('Class 3', 'Class 3: Soft and hard palate and base of the uvula are ' 'visible'), ('Class 4', 'Class 4: Only Hard Palate visible'), ], 'Mallampati Score', sort=False) preop_bleeding_risk = fields.Boolean( 'Risk of Massive bleeding', help="Patient has a risk of losing more than 500 " "ml in adults of over 7ml/kg in infants. If so, make sure that " "intravenous access and fluids are available") preop_oximeter = fields.Boolean('Pulse Oximeter in place', help="Pulse oximeter is in place " "and functioning") preop_site_marking = fields.Boolean( 'Surgical Site Marking', help="The surgeon has marked the surgical incision") preop_antibiotics = fields.Boolean( 'Antibiotic Prophylaxis', help="Prophylactic antibiotic treatment within the last 60 minutes") preop_sterility = fields.Boolean( 'Sterility confirmed', help="Nursing team has confirmed sterility of the devices and room") preop_asa = fields.Selection([ (None, ''), ('ps1', 'PS 1 : Normal healthy patient'), ('ps2', 'PS 2 : Patients with mild systemic disease'), ('ps3', 'PS 3 : Patients with severe systemic disease'), ('ps4', 'PS 4 : Patients with severe systemic disease that is' ' a constant threat to life '), ('ps5', 'PS 5 : Moribund patients who are not expected to' ' survive without the operation'), ('ps6', 'PS 6 : A declared brain-dead patient who organs are' ' being removed for donor purposes'), ], 'ASA PS', help="ASA pre-operative Physical Status", sort=False) preop_rcri = fields.Many2One( 'gnuhealth.rcri', 'RCRI', help='Patient Revised Cardiac Risk Index\n' 'Points 0: Class I Very Low (0.4% complications)\n' 'Points 1: Class II Low (0.9% complications)\n' 'Points 2: Class III Moderate (6.6% complications)\n' 'Points 3 or more : Class IV High (>11% complications)') surgical_wound = fields.Selection([ (None, ''), ('I', 'Clean . Class I'), ('II', 'Clean-Contaminated . Class II'), ('III', 'Contaminated . Class III'), ('IV', 'Dirty-Infected . Class IV'), ], 'Surgical wound', sort=False) extra_info = fields.Text('Extra Info') anesthesia_report = fields.Text('Anesthesia Report') institution = fields.Many2One('gnuhealth.institution', 'Institution') report_surgery_date = fields.Function(fields.Date('Surgery Date'), 'get_report_surgery_date') report_surgery_time = fields.Function(fields.Time('Surgery Time'), 'get_report_surgery_time') surgery_team = fields.One2Many( 'gnuhealth.surgery_team', 'name', 'Team Members', help="Professionals Involved in the surgery") postoperative_dx = fields.Many2One( 'gnuhealth.pathology', 'Post-op dx', states={ 'invisible': And(Not(Equal(Eval('state'), 'done')), Not(Equal(Eval('state'), 'signed'))) }, help="Post-operative diagnosis") @staticmethod def default_institution(): HealthInst = Pool().get('gnuhealth.institution') institution = HealthInst.get_institution() return institution @staticmethod def default_surgery_date(): return datetime.now() @staticmethod def default_surgeon(): pool = Pool() HealthProf = pool.get('gnuhealth.healthprofessional') surgeon = HealthProf.get_health_professional() return surgeon @staticmethod def default_state(): return 'draft' def get_patient_gender(self, name): return self.patient.gender @classmethod def search_patient_gender(cls, name, clause): res = [] value = clause[2] res.append(('patient.name.gender', clause[1], value)) return res # Show the gender and age upon entering the patient # These two are function fields (don't exist at DB level) @fields.depends('patient') def on_change_patient(self): gender = None age = '' self.gender = self.patient.gender self.computed_age = self.patient.age @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('code'): config = Config(1) values['code'] = Sequence.get_id( config.surgery_code_sequence.id) return super(Surgery, cls).create(vlist) @classmethod # Update to version 2.0 def __register__(cls, module_name): cursor = Transaction().cursor TableHandler = backend.get('TableHandler') table = TableHandler(cursor, cls, module_name) # Rename the date column to surgery_surgery_date if table.column_exist('date'): table.column_rename('date', 'surgery_date') super(Surgery, cls).__register__(module_name) @classmethod def __setup__(cls): super(Surgery, cls).__setup__() cls._error_messages.update({ 'end_date_before_start': 'End time "%(end_date)s" BEFORE ' 'surgery date "%(surgery_date)s"', 'or_is_not_available': 'Operating Room is not available' }) cls._buttons.update({ 'confirmed': { 'invisible': And(Not(Equal(Eval('state'), 'draft')), Not(Equal(Eval('state'), 'cancelled'))), }, 'cancel': { 'invisible': Not(Equal(Eval('state'), 'confirmed')), }, 'start': { 'invisible': Not(Equal(Eval('state'), 'confirmed')), }, 'done': { 'invisible': Not(Equal(Eval('state'), 'in_progress')), }, 'signsurgery': { 'invisible': Not(Equal(Eval('state'), 'done')), }, }) @classmethod def validate(cls, surgeries): super(Surgery, cls).validate(surgeries) for surgery in surgeries: surgery.validate_surgery_period() def validate_surgery_period(self): Lang = Pool().get('ir.lang') language, = Lang.search([ ('code', '=', Transaction().language), ]) if (self.surgery_end_date and self.surgery_date): if (self.surgery_end_date < self.surgery_date): self.raise_user_error( 'end_date_before_start', { 'surgery_date': Lang.strftime(self.surgery_date, language.code, language.date), 'end_date': Lang.strftime(self.surgery_end_date, language.code, language.date), }) @classmethod def write(cls, surgeries, vals): # Don't allow to write the record if the surgery has been signed if surgeries[0].state == 'signed': cls.raise_user_error( "This surgery is at state Done and has been signed\n" "You can no longer modify it.") return super(Surgery, cls).write(surgeries, vals) ## Method to check for availability and make the Operating Room reservation # for the associated surgery @classmethod @ModelView.button def confirmed(cls, surgeries): surgery_id = surgeries[0] Operating_room = Pool().get('gnuhealth.hospital.or') cursor = Transaction().cursor # Operating Room and end surgery time check if (not surgery_id.operating_room or not surgery_id.surgery_end_date): cls.raise_user_error("Operating Room and estimated end time " "are needed in order to confirm the surgery") or_id = surgery_id.operating_room.id cursor.execute( "SELECT COUNT(*) \ FROM gnuhealth_surgery \ WHERE (surgery_date::timestamp,surgery_end_date::timestamp) \ OVERLAPS (timestamp %s, timestamp %s) \ AND (state = %s or state = %s) \ AND operating_room = CAST(%s AS INTEGER) ", (surgery_id.surgery_date, surgery_id.surgery_end_date, 'confirmed', 'in_progress', str(or_id))) res = cursor.fetchone() if (surgery_id.surgery_end_date < surgery_id.surgery_date): cls.raise_user_error("The Surgery end date must later than the \ Start") if res[0] > 0: cls.raise_user_error('or_is_not_available') else: cls.write(surgeries, {'state': 'confirmed'}) # Cancel the surgery and set it to draft state # Free the related Operating Room @classmethod @ModelView.button def cancel(cls, surgeries): surgery_id = surgeries[0] Operating_room = Pool().get('gnuhealth.hospital.or') cls.write(surgeries, {'state': 'cancelled'}) # Start the surgery @classmethod @ModelView.button def start(cls, surgeries): surgery_id = surgeries[0] Operating_room = Pool().get('gnuhealth.hospital.or') cls.write( surgeries, { 'state': 'in_progress', 'surgery_date': datetime.now(), 'surgery_end_date': datetime.now() }) Operating_room.write([surgery_id.operating_room], {'state': 'occupied'}) # Finnish the surgery # Free the related Operating Room @classmethod @ModelView.button def done(cls, surgeries): surgery_id = surgeries[0] Operating_room = Pool().get('gnuhealth.hospital.or') cls.write(surgeries, { 'state': 'done', 'surgery_end_date': datetime.now() }) Operating_room.write([surgery_id.operating_room], {'state': 'free'}) # Sign the surgery document, and the surgical act. @classmethod @ModelView.button def signsurgery(cls, surgeries): surgery_id = surgeries[0] # Sign, change the state of the Surgery to "Signed" # and write the name of the signing health professional signing_hp = Pool().get( 'gnuhealth.healthprofessional').get_health_professional() if not signing_hp: cls.raise_user_error( "No health professional associated to this user !") cls.write(surgeries, {'state': 'signed', 'signed_by': signing_hp}) def get_report_surgery_date(self, name): Company = Pool().get('company.company') timezone = None company_id = Transaction().context.get('company') if company_id: company = Company(company_id) if company.timezone: timezone = pytz.timezone(company.timezone) dt = self.surgery_date return datetime.astimezone(dt.replace(tzinfo=pytz.utc), timezone).date() def get_report_surgery_time(self, name): Company = Pool().get('company.company') timezone = None company_id = Transaction().context.get('company') if company_id: company = Company(company_id) if company.timezone: timezone = pytz.timezone(company.timezone) dt = self.surgery_date return datetime.astimezone(dt.replace(tzinfo=pytz.utc), timezone).time()
class DateTime(ModelSQL): 'DateTime' __name__ = 'test.datetime' datetime = fields.DateTime(string='DateTime', help='Test datetime', required=False)
class TriageEntry(ModelSQL, ModelView): 'Triage Entry' __name__ = 'gnuhealth.triage.entry' firstname = fields.Char('First Name', states=REQD_IF_NOPATIENT) lastname = fields.Char('Last Name', states=REQD_IF_NOPATIENT) sex = fields.Selection([(None, '')] + SEX_OPTIONS, 'Sex', states=REQD_IF_NOPATIENT) age = fields.Char('Age', states=REQD_IF_NOPATIENT) sex_display = fields.Function(fields.Selection(SEX_OPTIONS, 'Sex'), 'get_sex_age_display') age_display = fields.Function(fields.Char('Age'), 'get_sex_age_display') id_type = fields.Selection(ID_TYPES, 'ID Type', states={ 'required': Bool(Eval('id_number')), 'readonly': Bool(Eval('patient')) }, sort=False) id_number = fields.Char( 'ID Number', states={'readonly': Or(Bool(Eval('patient')), Eval('done', False))}) id_display = fields.Function(fields.Char('UPI/MRN'), 'get_id_display', searcher='search_id') patient = fields.Many2One('gnuhealth.patient', 'Patient', states={ 'readonly': Or(~Eval('can_do_details', False), Eval('done', False)) }) priority = fields.Selection(TRIAGE_PRIO, 'ESI Priority', sort=False, help='Emergency Severity Index Triage Level', states={ 'invisible': ~(Eval('id', 0) > 0), 'readonly': Or(~Eval('can_do_details', False), Eval('done', False)) }) medical_alert = fields.Function(fields.Boolean( 'Medical Alert', states={ 'invisible': Or(Eval('can_do_details', False), ~In(Eval('status'), ['triage', 'pending']), ~In(Eval('priority'), ['99', '77'])) }), 'get_medical_alert', setter='set_medical_alert') injury = fields.Boolean('Injury', states=SIGNED_STATES) review = fields.Boolean('Review', states=SIGNED_STATES) status = fields.Selection(TRIAGE_STATUS, 'Status', sort=False, states={ 'readonly': Or(~Eval('can_do_details', False), Eval('done', False)) }) status_display = fields.Function(fields.Char('Status'), 'get_status_display') complaint = fields.Char('Primary Complaint', states=SIGNED_STATES) notes = fields.Text('Notes (edit)', states=SIGNED_STATES) note_entries = fields.One2Many('gnuhealth.triage.note', 'triage_entry', 'Note entries') note_display = fields.Function(fields.Text('Notes'), 'get_note_display') upi = fields.Function(fields.Char('UPI'), 'get_patient_party_field') name = fields.Function(fields.Char('Name'), 'get_name', searcher='search_name') patient_search = fields.Function( fields.One2Many('gnuhealth.patient', None, 'Patients'), 'patient_search_result') queue_entry = fields.One2Many('gnuhealth.patient.queue_entry', 'triage_entry', 'Queue Entry', size=1) encounter = fields.Many2One('gnuhealth.encounter', 'Encounter') # Vital Signs systolic = fields.Integer('Systolic Pressure', states=SIGNED_STATES) diastolic = fields.Integer('Diastolic Pressure', states=SIGNED_STATES) bpm = fields.Integer('Heart Rate (bpm)', help='Heart rate expressed in beats per minute', states=SIGNED_STATES) respiratory_rate = fields.Integer( 'Respiratory Rate', help='Respiratory rate expressed in breaths per minute', states=SIGNED_STATES) osat = fields.Integer('Oxygen Saturation', help='Oxygen Saturation(arterial).', states=SIGNED_STATES) temperature = fields.Float(u'Temperature (°C)', digits=(4, 1), help='Temperature in degrees celsius', states=SIGNED_STATES) # domain=[('temperature', '>', 25), ('temperature', '<', 50)]) childbearing_age = fields.Function(fields.Boolean('Childbearing Age'), 'get_childbearing_age') pregnant = fields.Boolean('Pregnant', states=STATE_NO_MENSES) lmp = fields.Date('Last Menstrual Period', states=STATE_NO_MENSES, help='Date last menstrual period started') glucose = fields.Float( 'Glucose (mmol/l)', digits=(5, 1), help='mmol/l. Reading from glucose meter', states=SIGNED_STATES, domain=[ 'OR', ('glucose', '=', None), ['AND', ('glucose', '>', 0), ('glucose', '<', 55.1)] ]) height = fields.Numeric('Height (cm)', digits=(4, 1), states=SIGNED_STATES) weight = fields.Numeric('Weight (kg)', digits=(3, 2), states=SIGNED_STATES) uri_ph = fields.Numeric('pH', digits=(1, 1), states=SIGNED_STATES) uri_specific_gravity = fields.Numeric('Specific Gravity', digits=(1, 3), states=SIGNED_STATES) uri_protein = fields.Selection('uri_selection', 'Protein', sort=False, states=SIGNED_STATES) uri_blood = fields.Selection('uri_selection', 'Blood', sort=False, states=SIGNED_STATES) uri_glucose = fields.Selection('uri_selection', 'Glucose', sort=False, states=SIGNED_STATES) uri_nitrite = fields.Selection('uri_nitrite_selection', 'Nitrite', sort=False, states=SIGNED_STATES) uri_bilirubin = fields.Selection('uri_selection', 'Bilirubin', sort=False, states=SIGNED_STATES) uri_leuko = fields.Selection('uri_selection', 'Leukocytes', sort=False, states=SIGNED_STATES) uri_ketone = fields.Selection('uri_selection', 'Ketone', sort=False, states=SIGNED_STATES) uri_urobili = fields.Selection('uri_selection', 'Urobilinogen', sort=False, states=SIGNED_STATES) malnutrition = fields.Boolean( 'Malnourished', help='Check this box if the patient show signs of malnutrition.', states=SIGNED_STATES) dehydration = fields.Selection( [(None, 'No'), ('mild', 'Mild'), ('moderate', 'Moderate'), ('severe', 'Severe')], 'Dehydration', sort=False, help='If the patient show signs of dehydration.', states=SIGNED_STATES) symp_fever = fields.Boolean('Fever', states=SIGNED_STATES) symp_respiratory = fields.Boolean('Respiratory', help="breathing problems", states=SIGNED_STATES) symp_jaundice = fields.Boolean('Jaundice', states=SIGNED_STATES) symp_rash = fields.Boolean('Rash', states=SIGNED_STATES) symp_hemorrhagic = fields.Boolean("Hemorrhagic", states=SIGNED_STATES) symp_neurological = fields.Boolean("Neurological", states=SIGNED_STATES) symp_arthritis = fields.Boolean("Arthralgia/Arthritis", states=SIGNED_STATES) symp_vomitting = fields.Boolean("Vomitting", states=SIGNED_STATES) symp_diarrhoea = fields.Boolean("Diarrhoea", states=SIGNED_STATES) recent_travel_contact = fields.Char( "Countries visited/Contact with traveller", states=SIGNED_STATES, help="Countries visited or from which there was contact with a " "traveller within the last six weeks") institution = fields.Many2One('gnuhealth.institution', 'Institution', states={'readonly': True}) _history = True # enable revision control from core can_do_details = fields.Function(fields.Boolean('Can do triage details'), 'get_do_details_perm') first_contact_time = fields.Function(fields.Text('First Contact Time'), 'get_first_time_contact') done = fields.Boolean('Done', states={'invisible': True}) end_time = fields.DateTime('End Time', help='Date and time triage ended', states={ 'readonly': Or(~Eval('can_do_details', False), Eval('done', False)) }) post_appointment = fields.Many2One('gnuhealth.appointment', 'Appointment') # signed_by = fields.Many2One('gnuhealth.healthprofessional'', 'Signed By') # sign_time = fields.DateTime('Signed on') total_time = fields.Function( fields.Char('Triage Time', states={'invisible': ~Eval('done', False)}), 'get_triage_time') @classmethod def __setup__(cls): super(TriageEntry, cls).__setup__() cls._buttons.update({ 'set_done': { 'readonly': ~Eval('can_do_details', False), 'invisible': Or(In(Eval('status'), ['pending', 'triage']), Eval('done', False)) }, 'go_referral': { 'readonly': ~Eval('can_do_details', False), 'invisible': ~In(Eval('status'), ['refer', 'referin']) } }) @classmethod def _swapnote(cls, vdict): '''swaps out the value in the notes field for an entry that creates a new gnuhealth.triage.note model instance''' new_note = vdict.get('notes', '') if new_note.strip(): new_note = new_note.strip() noteobj = ('create', [{'note': new_note}]) vdict.setdefault('note_entries', []).append(noteobj) vdict[ 'notes'] = u'' # toDo: remove this for next release and use vdict.pop return vdict @classmethod def make_priority_updates(cls, triage_entries, values_to_write): if ('priority' in values_to_write and 'queue_entry' not in values_to_write): prio = int(values_to_write['priority']) queue_model = Pool().get('gnuhealth.patient.queue_entry') qentries = queue_model.search([('triage_entry', 'in', triage_entries)]) values_to_write['queue_entry'] = [('write', map(int, qentries), { 'priority': prio })] # force end-time to now if none entered and the prompt ignored if (values_to_write.get('done', False) and not values_to_write.get('end_time', False)): values_to_write['end_time'] = datetime.now() return triage_entries, cls._swapnote(values_to_write) @classmethod def create(cls, vlist): # add me to the queue when created for vdict in vlist: if not vdict.get('queue_entry'): if vdict.get('medical_alert') is True: vqprio = MED_ALERT else: try: vqprio = int(vdict.get('priority', TRIAGE_MAX_PRIO)) except TypeError: vqprio = int(TRIAGE_MAX_PRIO) vdict['queue_entry'] = [('create', [{ 'busy': False, 'priority': vqprio }])] vdict = cls._swapnote(vdict) # in case there's a note now return super(TriageEntry, cls).create(vlist) @classmethod def write(cls, records, values, *args): # update queue priority when mine updated # but only if it's higher or there's no appointment records, values = cls.make_priority_updates(records, values) newargs = [] if args: arglist = iter(args) for r, v in zip(arglist, arglist): r, v = cls.make_priority_updates(r, v) newargs.extend([r, v]) return super(TriageEntry, cls).write(records, values, *newargs) @staticmethod def default_priority(): return str(TRIAGE_MAX_PRIO) @staticmethod def default_status(): return 'pending' def get_triage_time(self, name): endtime = self.end_time if self.done else datetime.now() return get_elapsed_time(self.create_date, endtime) def get_name(self, name): if name == 'name': if self.patient: return self.patient.name.name else: return '%s, %s' % (self.lastname, self.firstname) return '' @classmethod def search_name(cls, name, clause): fld, operator, operand = clause return [ 'OR', ('patient.name.name', operator, operand), ('firstname', operator, operand), ('lastname', operator, operand) ] @classmethod def search_id(cls, name, clause): fld, operator, operand = clause return [ 'OR' ('patient.name.upi', operator, operand), ('patient.medical_record_num', operator, operand), ('id_number', operator, operand) ] @classmethod def get_patient_party_field(cls, instances, name): out = dict([(i.id, '') for i in instances]) if name == 'upi': out.update([(i.id, i.patient.puid) for i in instances if i.patient]) return out def get_id_display(self, name): idtypedict = dict(ID_TYPES) if self.patient: return '{} / {}'.format(self.patient.puid, self.patient.medical_record_num) elif self.id_number and self.id_type: return ': '.join( [idtypedict.get(self.id_type, '??'), self.id_number]) else: return '' def patient_search_result(self, name): # ToDo: perform search against patient/party and return # the ones that match. # the domain should include : # lastname, firstname, sex, id_type, id_number return [] def get_status_display(self, name): return TRIAGE_STATUS_LOOKUP.get(self.status) def get_childbearing_age(self, name): if self.patient: return self.patient.childbearing_age elif self.sex == 'm': return False else: age = self.age if age.isdigit(): age = int(age) elif age[:-1].isdigit(): age = int(age[:-1]) else: age = 7 # hack to make a default false for BHC if age < MENARCH[0] or age > MENARCH[1]: return False return True def get_sex_age_display(self, name): field = name[:3] if self.patient: return getattr(self.patient, field) else: return getattr(self, field) @fields.depends('sex', 'patient') def on_change_with_childbearing_age(self, *a, **k): if self.patient: return self.patient.childbearing_age else: if self.sex == 'm': return False else: return True @classmethod def get_medical_alert(cls, instances, name): out = [(i.id, i.priority == MED_ALERT) for i in instances] return dict(out) @classmethod def set_medical_alert(cls, instances, name, value): to_write = [] if value is False: return for i in instances: if i.priority > MED_ALERT: to_write.append(i) cls.write(to_write, {'priority': MED_ALERT}) @classmethod def get_do_details_perm(cls, instances, name): user_has_perm = get_model_field_perm(cls.__name__, name, 'create', default_deny=False) outval = dict([(x.id, user_has_perm) for x in instances]) return outval @staticmethod def default_can_do_details(): user_has_perm = get_model_field_perm('gnuhealth.triage.entry', 'can_do_details', 'create', default_deny=False) return user_has_perm @staticmethod def default_childbearing_age(): return True @staticmethod def uri_selection(): return [(None, '')] + URINALYSIS['default'] @staticmethod def uri_nitrite_selection(): return [(None, '')] + URINALYSIS['nitrite'] def get_first_time_contact(self, name): '''This method gets the date and time this person was first made contact with by the attending staff''' return localtime(self.create_date).strftime('%F %T') @staticmethod def default_institution(): HI = Pool().get('gnuhealth.institution') return HI.get_institution() def get_note_display(self, name): notes = [] if self.note_entries: return u'\n---\n'.join( map(lambda x: u' :\n'.join([x.byline, x.note]), self.note_entries)) else: return '' @classmethod @ModelView.button_action('health_triage_queue.act_triage_referral_starter') def go_referral(cls, queue_entries): pass @classmethod @ModelView.button def set_done(cls, entries): '''set done=True on the triage entry''' save_data = {'done': True} for entry in entries: if not entry.end_time: cls.raise_user_warning( 'triage_end_date_warn1', 'End time has not been set.\nThe current Date and time ' 'will be used.') save_data.update(end_time=datetime.now()) cls.write(entries, save_data)
class ImagingTestRequest(Workflow, ModelSQL, ModelView): 'Imaging Test Request' __name__ = 'gnuhealth.imaging.test.request' patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True) date = fields.DateTime('Date', required=True) requested_test = fields.Many2One('gnuhealth.imaging.test', 'Test', required=True) doctor = fields.Many2One('gnuhealth.healthprofessional', 'Doctor', required=True) state = fields.Selection([ ('draft', 'Draft'), ('requested', 'Requested'), ('done', 'Done'), ], 'State', readonly=True) comment = fields.Text('Comment') request = fields.Char('Request', readonly=True) urgent = fields.Boolean('Urgent') @classmethod def __setup__(cls): super(ImagingTestRequest, cls).__setup__() cls._transitions |= set( (('draft', 'requested'), ('requested', 'done'))) cls._buttons.update({ 'requested': { 'invisible': ~Eval('state').in_(['draft']), }, 'generate_results': { 'invisible': ~Eval('state').in_(['requested']) } }) cls._order.insert(0, ('date', 'DESC')) cls._order.insert(1, ('request', 'DESC')) @staticmethod def default_date(): return datetime.now() @staticmethod def default_state(): return 'draft' @staticmethod def default_doctor(): 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_healthprof=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_healthprofessional WHERE \ name = %s LIMIT 1', (partner_id[0], )) doctor_id = cursor.fetchone() return int(doctor_id[0]) @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('request'): config = Config(1) values['request'] = Sequence.get_id( config.imaging_request_sequence.id) return super(ImagingTestRequest, cls).create(vlist) @classmethod def copy(cls, tests, default=None): if default is None: default = {} default = default.copy() default['request'] = None default['date'] = cls.default_date() return super(ImagingTestRequest, cls).copy(tests, default=default) @classmethod @ModelView.button @Workflow.transition('requested') def requested(cls, requests): pass @classmethod @ModelView.button_action('health_imaging.wizard_generate_result') def generate_results(cls, requests): pass @classmethod @Workflow.transition('done') def done(cls, requests): pass
class Consulta(ModelSQL, ModelView): 'Consulta' _name = 'cefiro.consulta' _description = __doc__ #_rec_name = 'horaIni' name = fields.Char('Nombre') libres = [] psicologos = fields.Many2Many('cefiro.encuentropsi', 'evento', 'persona', u'Psicólogos') pacientes = fields.Many2Many('cefiro.encuentro', 'evento', 'persona', 'Pacientes') estudiantes = fields.Many2Many('cefiro.encuentroest', 'evento', 'persona', 'Estudiantes') informe = fields.One2One('cefiro.consultainforme', 'consulta', 'informe', 'Informe') horaIni = fields.DateTime('Fecha y hora de inicio', required=True) horaFin = fields.DateTime('Fecha y hora de fin', required=True) consulLibres = fields.Function( fields.One2Many('cefiro.consultorio', None, 'Consultorios libres', on_change_with=['horaIni', 'horaFin']), 'get_libres') #consulLibresInt = fields.Function(fields.One2Many('cefiro.consultorio',None,'Consultorios libres',on_change_with=['consulLbres']),'get_libresInt') consultorio = fields.Many2One('cefiro.consultorio', 'Consultorio', required=True, domain=[('id', 'in', Eval('consulLibres'))]) def get_libres(self, ids, name): res = {} objConsul = Pool().get('cefiro.consultorio') for elem in self.browse(ids): res[elem.id] = elem.libres # if consultorio_ids: # res[elem.id].extend(consultorio_ids) return res # def on_change_with_consulLibres(self,values): # return [] def on_change_with_consulLibres(self, values): objConsultorio = Pool().get('cefiro.consultorio') objConsulta = Pool().get('cefiro.consulta') consultoriosTotId = objConsultorio.search([]) res = [] for cons in objConsultorio.browse(consultoriosTotId): estaVacio = True consultasIDs = cons.consultas listaDic = objConsulta.read(consultasIDs) for dic in listaDic: i1 = values.get('horaIni') f1 = values.get('horaFin') i2 = dic.get('horaIni') f2 = dic.get('horaFin') if not ((i1 == None) or (f1 == None)): if not ((f2 < i1) or (f1 < i2)): estaVacio = False if estaVacio: res.append(cons.id) self.libres = res # objConsulta.write(self.id,{'libres':Eval('res')}) return res
class InpatientRegistration(ModelSQL, ModelView): 'Patient admission History' __name__ = 'gnuhealth.inpatient.registration' name = fields.Char('Registration Code', readonly=True, select=True) patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True, select=True) admission_type = fields.Selection([ ('routine', 'Routine'), ('maternity', 'Maternity'), ('elective', 'Elective'), ('urgent', 'Urgent'), ('emergency', 'Emergency'), ], 'Admission type', required=True, select=True) hospitalization_date = fields.DateTime('Hospitalization date', required=True, select=True) discharge_date = fields.DateTime('Expected Discharge Date', required=True, select=True) attending_physician = fields.Many2One('gnuhealth.physician', 'Attending Physician', select=True) operating_physician = fields.Many2One('gnuhealth.physician', 'Operating Physician') admission_reason = fields.Many2One('gnuhealth.pathology', 'Reason for Admission', help="Reason for Admission", select=True) bed = fields.Many2One('gnuhealth.hospital.bed', 'Hospital Bed', states={ 'required': Not(Bool(Eval('name'))), 'readonly': Bool(Eval('name')), }, depends=['name']) nursing_plan = fields.Text('Nursing Plan') medications = fields.One2Many('gnuhealth.inpatient.medication', 'name', 'Medications') therapeutic_diets = fields.One2Many('gnuhealth.inpatient.diet', 'name', 'Therapeutic Diets') diet_belief = fields.Many2One( 'gnuhealth.diet.belief', 'Belief', help="Enter the patient belief or religion to choose the \ proper diet") diet_vegetarian = fields.Selection(( ('none', 'None'), ('vegetarian', 'Vegetarian'), ('lacto', 'Lacto vegetarian'), ('lactoovo', 'Lacto-ovo vegetarian'), ('pescetarian', 'Pescetarian'), ('vegan', 'Vegan'), ), 'Vegetarian', sort=False, required=True) nutrition_notes = fields.Text('Nutrition notes / directions') discharge_plan = fields.Text('Discharge Plan') info = fields.Text('Extra Info') state = fields.Selection(( ('free', 'free'), ('cancelled', 'cancelled'), ('confirmed', 'confirmed'), ('hospitalized', 'hospitalized'), ), 'Status', select=True) bed_transfers = fields.One2Many('gnuhealth.bed.transfer', 'name', 'Transfer History', readonly=True) @classmethod def __setup__(cls): super(InpatientRegistration, cls).__setup__() cls._sql_constraints = [('name_uniq', 'unique(name)', 'The Registration code already exists')] cls._error_messages.update({ 'bed_is_not_available': 'Bed is not available', 'destination_bed_unavailable': 'Destination bed unavailable' }) cls._buttons.update({ 'confirmed': { 'invisible': And(Not(Equal(Eval('state'), 'free')), Not(Equal(Eval('state'), 'cancelled'))), }, 'cancel': { 'invisible': Not(Equal(Eval('state'), 'confirmed')), }, 'discharge': { 'invisible': Not(Equal(Eval('state'), 'hospitalized')), }, 'admission': { 'invisible': Not(Equal(Eval('state'), 'confirmed')), }, }) ## Method to check for availability and make the hospital bed reservation @classmethod @ModelView.button def confirmed(cls, registrations): registration_id = registrations[0] Bed = Pool().get('gnuhealth.hospital.bed') cursor = Transaction().cursor bed_id = registration_id.bed.id cursor.execute( "SELECT COUNT(*) \ FROM gnuhealth_inpatient_registration \ WHERE (hospitalization_date::timestamp,discharge_date::timestamp) \ OVERLAPS (timestamp %s, timestamp %s) \ AND (state = %s or state = %s) \ AND bed = CAST(%s AS INTEGER) ", (registration_id.hospitalization_date, registration_id.discharge_date, 'confirmed', 'hospitalized', str(bed_id))) res = cursor.fetchone() if (registration_id.discharge_date.date() < registration_id.hospitalization_date.date()): cls.raise_user_error("The Discharge date must later than the \ Admission") if res[0] > 0: cls.raise_user_error('bed_is_not_available') else: cls.write(registrations, {'state': 'confirmed'}) Bed.write([registration_id.bed], {'state': 'reserved'}) @classmethod @ModelView.button def discharge(cls, registrations): registration_id = registrations[0] Bed = Pool().get('gnuhealth.hospital.bed') cls.write(registrations, {'state': 'free'}) Bed.write([registration_id.bed], {'state': 'free'}) @classmethod @ModelView.button def cancel(cls, registrations): registration_id = registrations[0] Bed = Pool().get('gnuhealth.hospital.bed') cls.write(registrations, {'state': 'cancelled'}) Bed.write([registration_id.bed], {'state': 'free'}) @classmethod @ModelView.button def admission(cls, registrations): registration_id = registrations[0] Bed = Pool().get('gnuhealth.hospital.bed') if (registration_id.hospitalization_date.date() != datetime.today().date()): cls.raise_user_error("The Admission date must be today") else: cls.write(registrations, {'state': 'hospitalized'}) Bed.write([registration_id.bed], {'state': 'occupied'}) @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.inpatient_registration_sequence.id) return super(InpatientRegistration, cls).create(vlist) @staticmethod def default_state(): return 'free' # Allow searching by the hospitalization code or patient name def get_rec_name(self, name): if self.patient: return self.name + ': ' + self.patient.name.name + ' ' + \ self.patient.name.lastname else: return self.name @classmethod def search_rec_name(cls, name, clause): field = None for field in ('name', 'patient'): registrations = cls.search([(field, ) + clause[1:]], limit=1) if registrations: break if registrations: return [(field, ) + clause[1:]] return [(cls._rec_name, ) + clause[1:]]
class GnuHealthPatientLabTest(ModelSQL, ModelView): 'Patient Lab Test' __name__ = 'gnuhealth.patient.lab.test' name = fields.Many2One('gnuhealth.lab.test_type', 'Test Type', required=True, select=True) date = fields.DateTime('Date', select=True) state = fields.Selection([ ('draft', 'Draft'), ('tested', 'Tested'), ('ordered', 'Ordered'), ('cancel', 'Cancel'), ], 'State', readonly=True, select=True) patient_id = fields.Many2One('gnuhealth.patient', 'Patient', required=True, select=True) doctor_id = fields.Many2One('gnuhealth.healthprofessional', 'Doctor', help="Doctor who Request the lab test.", select=True) request = fields.Integer('Request', readonly=True) urgent = fields.Boolean('Urgent') @classmethod def __setup__(cls): super(GnuHealthPatientLabTest, cls).__setup__() cls._order.insert(0, ('date', 'DESC')) cls._order.insert(1, ('request', 'DESC')) cls._order.insert(2, ('name', 'ASC')) @staticmethod def default_date(): return datetime.now() @staticmethod def default_state(): return 'draft' @staticmethod def default_doctor_id(): User = Pool().get('res.user') user = User(Transaction().user) uid = int(user.id) parties = Pool().get('party.party').search([ ('internal_user', '=', uid)]) if parties: doctors = Pool().get('gnuhealth.healthprofessional').search([ ('name', '=', parties[0].id)]) if doctors: return doctors[0].id else: return False @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('request'): config = Config(1) values['request'] = Sequence.get_id( config.lab_request_sequence.id) return super(GnuHealthPatientLabTest, cls).create(vlist) @classmethod def copy(cls, tests, default=None): if default is None: default = {} default = default.copy() default['request'] = None default['date'] = cls.default_date() return super(GnuHealthPatientLabTest, cls).copy(tests, default=default)
class Cron(DeactivableMixin, ModelSQL, ModelView): "Cron" __name__ = "ir.cron" interval_number = fields.Integer('Interval Number', required=True) interval_type = fields.Selection([ ('minutes', 'Minutes'), ('hours', 'Hours'), ('days', 'Days'), ('weeks', 'Weeks'), ('months', 'Months'), ], "Interval Type", sort=False, required=True) minute = fields.Integer("Minute", domain=['OR', ('minute', '=', None), [('minute', '>=', 0), ('minute', '<=', 59)], ], states={ 'invisible': Eval('interval_type').in_(['minutes']), }, depends=['interval_type']) hour = fields.Integer("Hour", domain=['OR', ('hour', '=', None), [('hour', '>=', 0), ('hour', '<=', 23)], ], states={ 'invisible': Eval('interval_type').in_(['minutes', 'hours']), }, depends=['interval_type']) weekday = fields.Many2One( 'ir.calendar.day', "Day of Week", states={ 'invisible': Eval('interval_type').in_( ['minutes', 'hours', 'days']), }, depends=['interval_type']) day = fields.Integer("Day", domain=['OR', ('day', '=', None), ('day', '>=', 0), ], states={ 'invisible': Eval('interval_type').in_( ['minutes', 'hours', 'days', 'weeks']), }, depends=['interval_type']) next_call = fields.DateTime("Next Call", select=True) method = fields.Selection([ ('ir.trigger|trigger_time', "Run On Time Triggers"), ('ir.queue|clean', "Clean Task Queue"), ], "Method", required=True) @classmethod def __setup__(cls): super(Cron, cls).__setup__() cls._buttons.update({ 'run_once': { 'icon': 'tryton-launch', }, }) @classmethod def __register__(cls, module_name): super().__register__(module_name) table_h = cls.__table_handler__(module_name) # Migration from 5.0: remove fields for column in ['name', 'user', 'request_user', 'number_calls', 'repeat_missed', 'model', 'function', 'args']: table_h.drop_column(column) # Migration from 5.0: remove required on next_call table_h.not_null_action('next_call', 'remove') @staticmethod def check_xml_record(crons, values): return True @classmethod def view_attributes(cls): return [( '//label[@id="time_label"]', 'states', { 'invisible': Eval('interval_type') == 'minutes', }), ] def compute_next_call(self, now): return (now + relativedelta(**{self.interval_type: self.interval_number}) + relativedelta( microsecond=0, second=0, minute=( self.minute if self.interval_type != 'minutes' else None), hour=( self.hour if self.interval_type not in {'minutes', 'hours'} else None), day=( self.day if self.interval_type not in { 'minutes', 'hours', 'days', 'weeks'} else None), weekday=( int(self.weekday.index) if self.weekday and self.interval_type not in {'minutes', 'hours', 'days'} else None))) @dualmethod @ModelView.button def run_once(cls, crons): pool = Pool() for cron in crons: model, method = cron.method.split('|') Model = pool.get(model) getattr(Model, method)() @classmethod def run(cls, db_name): transaction = Transaction() logger.info('cron started for "%s"', db_name) now = datetime.datetime.now() retry = config.getint('database', 'retry') with transaction.start(db_name, 0, context={'_skip_warnings': True}): transaction.database.lock(transaction.connection, cls._table) crons = cls.search(['OR', ('next_call', '<=', now), ('next_call', '=', None), ]) for cron in crons: name = '<Cron %s@%s %s>' % (cron.id, db_name, cron.method) logger.info("%s started", name) for count in range(retry, -1, -1): if count != retry: time.sleep(0.02 * (retry - count)) try: with processing(name): cron.run_once() cron.next_call = cron.compute_next_call(now) cron.save() transaction.commit() except Exception as e: transaction.rollback() if (isinstance(e, backend.DatabaseOperationalError) and count): continue logger.error('%s failed', name, exc_info=True) break while transaction.tasks: task_id = transaction.tasks.pop() run_task(db_name, task_id) logger.info('cron finished for "%s"', db_name)
class Newborn(ModelSQL, ModelView): 'Newborn Information' __name__ = 'gnuhealth.newborn' STATES = {'readonly': Eval('state') == 'signed'} name = fields.Char('Newborn ID', states=STATES) patient = fields.Many2One('gnuhealth.patient', 'Baby', required=True, states=STATES, help="Patient associated to this newborn") mother = fields.Many2One('gnuhealth.patient', 'Mother', states=STATES) newborn_name = fields.Char('Name at Birth', states=STATES) birth_date = fields.DateTime('DoB', required=True, help="Date and Time of birth", states=STATES) photo = fields.Binary('Picture', states=STATES) # Sex / Gender at birth. sex = fields.Selection([ ('m', 'Male'), ('f', 'Female'), ], 'Sex',sort=False, required=True, help="Sex at birth. It might differ from the current patient" \ " gender. This is the biological sex.", states = STATES) state = fields.Selection([ (None, ''), ('draft', 'draft'), ('signed', 'Signed'), ], 'State', readonly=True, sort=False) cephalic_perimeter = fields.Integer( 'CP', help="Cephalic Perimeter in centimeters (cm)", states=STATES) length = fields.Integer('Length', help="Length in centimeters (cm)", states=STATES) weight = fields.Integer('Weight', help="Weight in grams (g)", states=STATES) apgar1 = fields.Integer('APGAR 1st minute', states=STATES) apgar5 = fields.Integer('APGAR 5th minute', states=STATES) apgar_scores = fields.One2Many('gnuhealth.neonatal.apgar', 'name', 'APGAR scores', states=STATES) meconium = fields.Boolean('Meconium', states=STATES) #Deprecated. Use Patient conditions directly congenital_diseases = fields.One2Many('gnuhealth.patient.disease', 'newborn_id', 'Congenital diseases', states=STATES) reanimation_stimulation = fields.Boolean('Stimulation', states=STATES) reanimation_aspiration = fields.Boolean('Aspiration', states=STATES) reanimation_intubation = fields.Boolean('Intubation', states=STATES) reanimation_mask = fields.Boolean('Mask', states=STATES) reanimation_oxygen = fields.Boolean('Oxygen', states=STATES) test_vdrl = fields.Boolean('VDRL', states=STATES) test_toxo = fields.Boolean('Toxoplasmosis', states=STATES) test_chagas = fields.Boolean('Chagas', states=STATES) test_billirubin = fields.Boolean('Billirubin', states=STATES) test_audition = fields.Boolean('Audition', states=STATES) test_metabolic = fields.Boolean( 'Metabolic ("heel stick screening")', help="Test for Fenilketonuria, Congenital Hypothyroidism, " "Quistic Fibrosis, Galactosemia", states=STATES) neonatal_ortolani = fields.Boolean('Positive Ortolani', states=STATES) neonatal_barlow = fields.Boolean('Positive Barlow', states=STATES) neonatal_hernia = fields.Boolean('Hernia', states=STATES) neonatal_ambiguous_genitalia = fields.Boolean('Ambiguous Genitalia', states=STATES) neonatal_erbs_palsy = fields.Boolean('Erbs Palsy', states=STATES) neonatal_hematoma = fields.Boolean('Hematomas', states=STATES) neonatal_talipes_equinovarus = fields.Boolean('Talipes Equinovarus', states=STATES) neonatal_polydactyly = fields.Boolean('Polydactyly', states=STATES) neonatal_syndactyly = fields.Boolean('Syndactyly', states=STATES) neonatal_moro_reflex = fields.Boolean('Moro Reflex', states=STATES) neonatal_grasp_reflex = fields.Boolean('Grasp Reflex', states=STATES) neonatal_stepping_reflex = fields.Boolean('Stepping Reflex', states=STATES) neonatal_babinski_reflex = fields.Boolean('Babinski Reflex', states=STATES) neonatal_blink_reflex = fields.Boolean('Blink Reflex', states=STATES) neonatal_sucking_reflex = fields.Boolean('Sucking Reflex', states=STATES) neonatal_swimming_reflex = fields.Boolean('Swimming Reflex', states=STATES) neonatal_tonic_neck_reflex = fields.Boolean('Tonic Neck Reflex', states=STATES) neonatal_rooting_reflex = fields.Boolean('Rooting Reflex', states=STATES) neonatal_palmar_crease = fields.Boolean('Transversal Palmar Crease', states=STATES) #Deprecated. Use Patient medication direcly medication = fields.One2Many('gnuhealth.patient.medication', 'newborn_id', 'Medication') healthprof = fields.Many2One('gnuhealth.healthprofessional', 'Health Professional', help="Health professional", readonly=True) signed_by = fields.Many2One( 'gnuhealth.healthprofessional', 'Signed by', readonly=True, states={'invisible': Not(Equal(Eval('state'), 'signed'))}, help="Health Professional that signed this document") dismissed = fields.DateTime('Discharged', states=STATES) notes = fields.Text('Notes', states=STATES) # Deprecated. the following fields will be removed in 2.8 # Decease information on fetus / newborn are linked now in # the obstetrics evaluation (prenatal) or patient if result of pregnancy # was a live birth. # The information is no longer shown at the view. # Fields to be removed : bd, died_at_delivery, died_at_the_hospital # died_being_transferred, tod, cod bd = fields.Boolean('Stillbirth') died_at_delivery = fields.Boolean('Died at delivery room') died_at_the_hospital = fields.Boolean('Died at the hospital') died_being_transferred = fields.Boolean( 'Died being transferred', help="The baby died being transferred to another health institution") tod = fields.DateTime('Time of Death') cod = fields.Many2One('gnuhealth.pathology', 'Cause of death') @staticmethod def default_healthprof(): pool = Pool() HealthProf = pool.get('gnuhealth.healthprofessional') healthprof = HealthProf.get_health_professional() return healthprof @staticmethod def default_state(): return 'draft' @classmethod def __setup__(cls): super(Newborn, cls).__setup__() t = cls.__table__() cls._sql_constraints = [ ('name_uniq', Unique(t, t.name), 'The Newborn ID must be unique'), ('patient_uniq', Unique(t, t.patient), 'There is already a newborn record for this patient'), ] cls._buttons.update( {'sign_newborn': { 'invisible': Equal(Eval('state'), 'signed') }}) @classmethod @ModelView.button def sign_newborn(cls, newborns): pool = Pool() HealthProfessional = pool.get('gnuhealth.healthprofessional') Appointment = pool.get('gnuhealth.appointment') newborn_id = newborns[0] patient_app = [] # Change the state of the newborn to "Done" signing_hp = HealthProfessional.get_health_professional() cls.write(newborns, { 'state': 'signed', 'signed_by': signing_hp, }) @classmethod def __register__(cls, module_name): cursor = Transaction().cursor TableHandler = backend.get('TableHandler') table = TableHandler(cursor, cls, module_name) #Rename responsible -> healthprof if table.column_exist('responsible'): table.column_rename('responsible', 'healthprof') super(Newborn, cls).__register__(module_name) @classmethod def write(cls, newborns, values): pool = Pool() cursor = Transaction().cursor Patient = pool.get('gnuhealth.patient') Party = pool.get('party.party') party = [] patient = [] cursor = Transaction().cursor for newborn in newborns: newborn_patient_id = newborn.patient.id person = Patient.browse([newborn_patient_id])[0].name pat = Patient.browse([newborn_patient_id])[0] # Update the birth date on the party model upon WRITING it on the # newborn model born_date = datetime.date(newborn.birth_date) party.append(person) Party.write(party, {'dob': born_date}) # Update the biological sex on the patient model upon WRITING # it on the newborn model if values.get('sex'): biological_sex = values.get('sex') patient.append(pat) Patient.write(patient, {'biological_sex': biological_sex}) return super(Newborn, cls).write(newborns, values) @classmethod def create(cls, vlist): pool = Pool() vlist = [x.copy() for x in vlist] Patient = pool.get('gnuhealth.patient') Party = pool.get('party.party') party = [] patient = [] cursor = Transaction().cursor for values in vlist: newborn_patient_id = values['patient'] person = Patient.browse([newborn_patient_id])[0].name pat = Patient.browse([newborn_patient_id])[0] # Update the birth date on the party model upon CREATING it on the # newborn model born_date = datetime.date(values['birth_date']) party.append(person) Party.write(party, {'dob': born_date}) # Update the biological sex on the patient model upon CREATING # it on the newborn model if values.get('sex'): biological_sex = values.get('sex') patient.append(pat) Patient.write(patient, {'biological_sex': biological_sex}) return super(Newborn, cls).create(vlist)
class SupportRequest (ModelSQL, ModelView): 'Support Request Registration' __name__ = 'gnuhealth.support_request' _rec_name = 'code' code = fields.Char('Code',help='Request Code', readonly=True) operator = fields.Many2One( 'gnuhealth.healthprofessional', 'Operator', help="Operator who took the call / support request") requestor = fields.Many2One('party.party', 'Requestor', domain=[('is_person', '=', True)], help="Related party (person)") patient = fields.Many2One('gnuhealth.patient', 'Patient') evaluation = fields.Many2One('gnuhealth.patient.evaluation', 'Evaluation', domain=[('patient', '=', Eval('patient'))], depends=['patient'], help='Related Patient Evaluation') request_date = fields.DateTime('Date', required=True, help="Date and time of the call for help") operational_sector = fields.Many2One('gnuhealth.operational_sector', 'O. Sector',help="Operational Sector") latitude = fields.Numeric('Latidude', digits=(3, 14)) longitude = fields.Numeric('Longitude', digits=(4, 14)) address = fields.Text("Address", help="Free text address / location") urladdr = fields.Char( 'OSM Map', help="Maps the location on Open Street Map") healthcenter = fields.Many2One('gnuhealth.institution','Calling Institution') patient_sex = fields.Function( fields.Char('Sex'), 'get_patient_sex') patient_age = fields.Function( fields.Char('Age'), 'get_patient_age') complaint = fields.Function( fields.Char('Chief Complaint'), 'get_patient_complaint') urgency = fields.Selection([ (None, ''), ('low', 'Low'), ('urgent', 'Urgent'), ('emergency', 'Emergency'), ], 'Urgency', sort=False) place_occurrance = fields.Selection([ (None, ''), ('home', 'Home'), ('street', 'Street'), ('institution', 'Institution'), ('school', 'School'), ('commerce', 'Commercial Area'), ('recreational', 'Recreational Area'), ('transportation', 'Public transportation'), ('sports', 'Sports event'), ('publicbuilding', 'Public Building'), ('unknown', 'Unknown'), ], 'Origin', help="Place of occurrance",sort=False) event_type = fields.Selection([ (None, ''), ('event1', 'Acute Coronary Syndrome'), ('event2', 'Acute pain'), ('event3', 'Acute illness'), ('event4', 'Allergic reaction'), ('event5', 'Bullying, battering'), ('event6', 'Gastrointestinal event'), ('event7', 'Endocrine event (diabetes, adrenal crisis, ..)'), ('event8', 'Choke'), ('event9', 'Domestic violence'), ('event10', 'Environmental event (weather, animals, ...)'), ('event11', 'Sexual assault'), ('event12', 'Drug intoxication'), ('event13', 'Robbery, violent assault'), ('event14', 'Respiratory distress'), ('event15', 'Pregnancy related event'), ('event16', 'Gas intoxication'), ('event17', 'Food intoxication'), ('event18', 'Neurological event (stroke, TIA, seizure, ...)'), ('event19', 'Chronic illness'), ('event20', 'Near drowning'), ('event21', 'Eye, Ear and Nose event'), ('event22', 'Fall'), ('event23', 'Deceased person'), ('event24', 'Psychiatric event'), ('event25', 'Suicide attempt'), ('event26', 'Fire'), ('event27', 'Transportation accident'), ('event28', 'Traumatic Injuries'), ('event29', 'Explosion'), ('event30', 'Other specified'), ], 'Event type') event_specific = fields.Many2One ('gnuhealth.pathology','Incident') multiple_casualties = fields.Boolean('Multiple Casualties') request_actions = fields.One2Many( 'gnuhealth.support_request.log', 'sr', 'Activities', help='Support request activity log') ambulances = fields.One2Many( 'gnuhealth.ambulance.support', 'sr', 'Ambulances', help='Ambulances requested in this Support Request') request_extra_info = fields.Text('Details') state = fields.Selection([ (None, ''), ('open', 'Open'), ('closed', 'Closed'), ], 'State', sort=False, readonly=True) @staticmethod def default_request_date(): return datetime.now() def get_patient_sex(self, name): if self.patient: return self.patient.gender def get_patient_age(self, name): if self.patient: return self.patient.name.age def get_patient_complaint(self, name): if self.evaluation: if self.evaluation.chief_complaint: return self.evaluation.chief_complaint @staticmethod def default_operator(): pool = Pool() HealthProf= pool.get('gnuhealth.healthprofessional') operator = HealthProf.get_health_professional() return operator @staticmethod def default_state(): return 'open' @fields.depends('latitude', 'longitude') 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 Accident / Injury # are given, then those parameters will be used. ret_url = '' if (self.latitude and self.longitude): ret_url = 'http://openstreetmap.org/?mlat=' + \ str(self.latitude) + '&mlon=' + str(self.longitude) return ret_url @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('code'): config = Config(1) values['code'] = Sequence.get_id( config.support_request_code_sequence.id) return super(SupportRequest, cls).create(vlist) @classmethod def __setup__(cls): super(SupportRequest, cls).__setup__() t = cls.__table__() cls._sql_constraints = [ ('code_uniq', Unique(t,t.code), 'This Request Code already exists'), ] cls._buttons.update({ 'open_support': {'invisible': Equal(Eval('state'), 'open')}, 'close_support': {'invisible': Equal(Eval('state'), 'closed')}, }) @classmethod @ModelView.button def open_support(cls, srs): cls.write(srs, { 'state': 'open'}) @classmethod @ModelView.button def close_support(cls, srs): cls.write(srs, { 'state': 'closed'})
class PediatricSymptomsChecklist(ModelSQL, ModelView): 'Pediatric Symptoms Checklist' __name__ = 'gnuhealth.patient.psc' patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True) evaluation_date = fields.Many2One( 'gnuhealth.appointment', 'Appointment', help="Enter or select the date / ID of the appointment related to " "this evaluation") evaluation_start = fields.DateTime('Date', required=True) user_id = fields.Many2One('res.user', 'Health Professional', readonly=True) notes = fields.Text('Notes') psc_aches_pains = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Complains of aches and pains', sort=False) psc_spend_time_alone = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Spends more time alone', sort=False) psc_tires_easily = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Tires easily, has little energy', sort=False) psc_fidgety = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Fidgety, unable to sit still', sort=False) psc_trouble_with_teacher = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Has trouble with teacher', sort=False) psc_less_interest_in_school = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Less interested in school', sort=False) psc_acts_as_driven_by_motor = fields.Selection( [ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Acts as if driven by a motor', sort=False) psc_daydreams_too_much = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Daydreams too much', sort=False) psc_distracted_easily = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Distracted easily', sort=False) psc_afraid_of_new_situations = fields.Selection( [ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Is afraid of new situations', sort=False) psc_sad_unhappy = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Feels sad, unhappy', sort=False) psc_irritable_angry = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Is irritable, angry', sort=False) psc_feels_hopeless = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Feels hopeless', sort=False) psc_trouble_concentrating = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Has trouble concentrating', sort=False) psc_less_interested_in_friends = fields.Selection( [ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Less interested in friends', sort=False) psc_fights_with_others = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Fights with other children', sort=False) psc_absent_from_school = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Absent from school', sort=False) psc_school_grades_dropping = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'School grades dropping', sort=False) psc_down_on_self = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Is down on him or herself', sort=False) psc_visit_doctor_finds_ok = fields.Selection( [ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Visits the doctor with doctor finding nothing wrong', sort=False) psc_trouble_sleeping = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Has trouble sleeping', sort=False) psc_worries_a_lot = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Worries a lot', sort=False) psc_wants_to_be_with_parents = fields.Selection( [ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Wants to be with you more than before', sort=False) psc_feels_is_bad_child = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Feels he or she is bad', sort=False) psc_takes_unnecesary_risks = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Takes unnecessary risks', sort=False) psc_gets_hurt_often = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Gets hurt frequently', sort=False) psc_having_less_fun = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Seems to be having less fun', sort=False) psc_act_as_younger = fields.Selection( [ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Acts younger than children his or her age', sort=False) psc_does_not_listen_to_rules = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Does not listen to rules', sort=False) psc_does_not_show_feelings = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Does not show feelings', sort=False) psc_does_not_get_people_feelings = fields.Selection( [ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Does not get people feelings', sort=False) psc_teases_others = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Teases others', sort=False) psc_blames_others = fields.Selection( [ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Blames others for his or her troubles', sort=False) psc_takes_things_from_others = fields.Selection( [ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Takes things that do not belong to him or her', sort=False) psc_refuses_to_share = fields.Selection([ (None, ''), ('0', 'Never'), ('1', 'Sometimes'), ('2', 'Often'), ], 'Refuses to share', sort=False) psc_total = fields.Integer('PSC Total') @staticmethod def default_user_id(): User = Pool().get('res.user') user = User(Transaction().user) return int(user.id) @staticmethod def default_psc_total(): return 0 @fields.depends( 'psc_aches_pains', 'psc_spend_time_alone', 'psc_tires_easily', 'psc_fidgety', 'psc_trouble_with_teacher', 'psc_less_interest_in_school', 'psc_acts_as_driven_by_motor', 'psc_daydreams_too_much', 'psc_distracted_easily', 'psc_afraid_of_new_situations', 'psc_sad_unhappy', 'psc_irritable_angry', 'psc_feels_hopeless', 'psc_trouble_concentrating', 'psc_less_interested_in_friends', 'psc_fights_with_others', 'psc_absent_from_school', 'psc_school_grades_dropping', 'psc_down_on_self', 'psc_visit_doctor_finds_ok', 'psc_trouble_sleeping', 'psc_worries_a_lot', 'psc_wants_to_be_with_parents', 'psc_feels_is_bad_child', 'psc_takes_unnecesary_risks', 'psc_gets_hurt_often', 'psc_having_less_fun', 'psc_act_as_younger', 'psc_does_not_listen_to_rules', 'psc_does_not_show_feelings', 'psc_does_not_get_people_feelings', 'psc_teases_others', 'psc_takes_things_from_others', 'psc_refuses_to_share') def on_change_with_psc_total(self): psc_aches_pains = self.psc_aches_pains or '0' psc_spend_time_alone = self.psc_spend_time_alone or '0' psc_tires_easily = self.psc_tires_easily or '0' psc_fidgety = self.psc_fidgety or '0' psc_trouble_with_teacher = self.psc_trouble_with_teacher or '0' psc_less_interest_in_school = self.psc_less_interest_in_school or '0' psc_acts_as_driven_by_motor = self.psc_acts_as_driven_by_motor or '0' psc_daydreams_too_much = self.psc_daydreams_too_much or '0' psc_distracted_easily = self.psc_distracted_easily or '0' psc_afraid_of_new_situations = self.psc_afraid_of_new_situations or '0' psc_sad_unhappy = self.psc_sad_unhappy or '0' psc_irritable_angry = self.psc_irritable_angry or '0' psc_feels_hopeless = self.psc_feels_hopeless or '0' psc_trouble_concentrating = self.psc_trouble_concentrating or '0' psc_less_interested_in_friends = \ self.psc_less_interested_in_friends or '0' psc_fights_with_others = self.psc_fights_with_others or '0' psc_absent_from_school = self.psc_absent_from_school or '0' psc_school_grades_dropping = self.psc_school_grades_dropping or '0' psc_down_on_self = self.psc_down_on_self or '0' psc_visit_doctor_finds_ok = self.psc_visit_doctor_finds_ok or '0' psc_trouble_sleeping = self.psc_trouble_sleeping or '0' psc_worries_a_lot = self.psc_worries_a_lot or '0' psc_wants_to_be_with_parents = self.psc_wants_to_be_with_parents or '0' psc_feels_is_bad_child = self.psc_feels_is_bad_child or '0' psc_takes_unnecesary_risks = self.psc_takes_unnecesary_risks or '0' psc_gets_hurt_often = self.psc_gets_hurt_often or '0' psc_having_less_fun = self.psc_having_less_fun or '0' psc_act_as_younger = self.psc_act_as_younger or '0' psc_does_not_listen_to_rules = self.psc_does_not_listen_to_rules or '0' psc_does_not_show_feelings = self.psc_does_not_show_feelings or '0' psc_does_not_get_people_feelings = \ self.psc_does_not_get_people_feelings or '0' psc_teases_others = self.psc_teases_others or '0' psc_takes_things_from_others = self.psc_takes_things_from_others or '0' psc_refuses_to_share = self.psc_refuses_to_share or '0' psc_total = int(psc_aches_pains) + int(psc_spend_time_alone) + \ int(psc_tires_easily) + int(psc_fidgety) + \ int(psc_trouble_with_teacher) + \ int(psc_less_interest_in_school) + \ int(psc_acts_as_driven_by_motor) + \ int(psc_daydreams_too_much) + int(psc_distracted_easily) + \ int(psc_afraid_of_new_situations) + int(psc_sad_unhappy) + \ int(psc_irritable_angry) + int(psc_feels_hopeless) + \ int(psc_trouble_concentrating) + \ int(psc_less_interested_in_friends) + \ int(psc_fights_with_others) + int(psc_absent_from_school) + \ int(psc_school_grades_dropping) + int(psc_down_on_self) + \ int(psc_visit_doctor_finds_ok) + int(psc_trouble_sleeping) + \ int(psc_worries_a_lot) + int(psc_wants_to_be_with_parents) + \ int(psc_feels_is_bad_child) + int(psc_takes_unnecesary_risks) + \ int(psc_gets_hurt_often) + int(psc_having_less_fun) + \ int(psc_act_as_younger) + int(psc_does_not_listen_to_rules) + \ int(psc_does_not_show_feelings) + \ int(psc_does_not_get_people_feelings) + \ int(psc_teases_others) + \ int(psc_takes_things_from_others) + \ int(psc_refuses_to_share) return psc_total
class OphthalmologyEvaluation(ModelSQL, ModelView): 'Ophthalmology Evaluation' __name__ = 'gnuhealth.ophthalmology.evaluation' STATES = {'readonly': Eval('state') == 'done'} patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True) visit_date = fields.DateTime('Date', help="Date of Consultation") computed_age = fields.Function( fields.Char( 'Age', help="Computed patient age at the moment of the evaluation"), 'patient_age_at_evaluation') gender = fields.Function(fields.Selection([ (None, ''), ('m', 'Male'), ('f', 'Female'), ], 'Gender'), 'get_patient_gender', searcher='search_patient_gender') health_professional = fields.Many2One( 'gnuhealth.healthprofessional', 'Health Professional', readonly=True, help="Health professional / Ophthalmologist / OptoMetrist") # there are two types of charts, a meter chart.. 6/.. val # and ft chart.. 200/... snellen_chart = [ (None, ''), ('6_6', '6/6'), ('6_9', '6/9'), ('6_12', '6/12'), ('6_18', '6/18'), ('6_24', '6/24'), ('6_36', '6/36'), ('6_60', '6/60'), ('5_60', '5/60'), ('4_60', '4/60'), ('3_60', '3/60'), ('2_60', '2/60'), ('1_60', '1/60'), ('1_meter_fc', '1 Meter FC'), ('1_2_meter_fc', '1/2 Meter FC'), ('hmfc', 'HMCF'), ('p_l', 'P/L'), ] # Near vision chart near_vision_chart = [ (None, ''), ('N6', 'N6'), ('N8', 'N8'), ('N12', 'N12'), ('N18', 'N18'), ('N24', 'N24'), ('N36', 'N36'), ('N60', 'N60'), ] # vision test using snellen chart rdva = fields.Selection(snellen_chart, 'RDVA', help="Right Eye Vision of Patient without aid", sort=False, states=STATES) ldva = fields.Selection(snellen_chart, 'LDVA', help="Left Eye Vision of Patient without aid", sort=False, states=STATES) # vision test using pinhole accurate manual testing rdva_pinhole = fields.Selection(snellen_chart, 'RDVA', help="Right Eye Vision Using Pin Hole", sort=False, states=STATES) ldva_pinhole = fields.Selection(snellen_chart, 'LDVA', help="Left Eye Vision Using Pin Hole", sort=False, states=STATES) # vison testing with glasses just to assess what the patient sees with # his existing aid # useful esp with vision syndromes that are not # happening because of the lens rdva_aid = fields.Selection(snellen_chart, 'RDVA AID', help="Vision with glasses or contact lens", sort=False, states=STATES) ldva_aid = fields.Selection(snellen_chart, 'LDVA AID', help="Vision with glasses or contact lens", sort=False, states=STATES) # spherical rspherical = fields.Float('SPH', help='Right Eye Spherical', states=STATES) lspherical = fields.Float('SPH', help='Left Eye Spherical', states=STATES) # cylinder rcylinder = fields.Float('CYL', help='Right Eye Cylinder', states=STATES) lcylinder = fields.Float('CYL', help='Left Eye Cylinder', states=STATES) #axis raxis = fields.Float('Axis', help='Right Eye Axis', states=STATES) laxis = fields.Float('Axis', help='Left Eye Axis', states=STATES) # near vision testing .... you will get it when u cross 40 :) # its also thinning of the lens.. the focus falls behind the retina # in case of distant vision the focus does not reach retina rnv_add = fields.Float('NV Add', help='Right Eye Best Corrected NV Add', states=STATES) lnv_add = fields.Float('NV Add', help='Left Eye Best Corrected NV Add', states=STATES) rnv = fields.Selection(near_vision_chart, 'RNV', help="Right Eye Near Vision", sort=False, states=STATES) lnv = fields.Selection(near_vision_chart, 'LNV', help="Left Eye Near Vision", sort=False, states=STATES) # after the above tests the optometrist or doctor comes to a best conclusion # best corrected visual acuity # the above values are from autorefraction # the doctors decision is final # and there could be changes in values of cylinder, spherical and axis # these values will go into final prescription of glasses or contact lens # by default these values should be auto populated # and should be modifiable by an ophthalmologist rbcva_spherical = fields.Float('SPH', help='Right Eye Best Corrected Spherical', states=STATES) lbcva_spherical = fields.Float('SPH', help='Left Eye Best Corrected Spherical', states=STATES) rbcva_cylinder = fields.Float('CYL', help='Right Eye Best Corrected Cylinder', states=STATES) lbcva_cylinder = fields.Float('CYL', help='Left Eye Best Corrected Cylinder', states=STATES) rbcva_axis = fields.Float('Axis', help='Right Eye Best Corrected Axis', states=STATES) lbcva_axis = fields.Float('Axis', help='Left Eye Best Corrected Axis', states=STATES) rbcva = fields.Selection(snellen_chart, 'RBCVA', help="Right Eye Best Corrected VA", sort=False, states=STATES) lbcva = fields.Selection(snellen_chart, 'LBCVA', help="Left Eye Best Corrected VA", sort=False, states=STATES) rbcva_nv_add = fields.Float('BCVA - Add', help='Right Eye Best Corrected NV Add', states=STATES) lbcva_nv_add = fields.Float('BCVA - Add', help='Left Eye Best Corrected NV Add', states=STATES) rbcva_nv = fields.Selection(near_vision_chart, 'RBCVANV', help="Right Eye Best Corrected Near Vision", sort=False, states=STATES) lbcva_nv = fields.Selection(near_vision_chart, 'LBCVANV', help="Left Eye Best Corrected Near Vision", sort=False, states=STATES) #some other tests of the eyes #useful for diagnosis of glaucoma a disease that builds up #pressure inside the eye and destroy the retina #its also called the silent vision stealer #intra ocular pressure #there are three ways to test iop # SCHIOTZ # NONCONTACT TONOMETRY # GOLDMANN APPLANATION TONOMETRY #notes by the ophthalmologist or optometrist notes = fields.Text('Notes', states=STATES) #Intraocular Pressure iop_method = fields.Selection( [ (None, ''), ('nct', 'Non-contact tonometry'), ('schiotz', 'Schiotz tonometry'), ('goldmann', 'Goldman tonometry'), ], 'Method', help='Tonometry / Intraocular pressure reading method', states=STATES) riop = fields.Float('RIOP', digits=(2, 1), help="Right Intraocular Pressure in mmHg", states=STATES) liop = fields.Float('LIOP', digits=(2, 1), help="Left Intraocular Pressure in mmHg", states=STATES) findings = fields.One2Many('gnuhealth.ophthalmology.findings', 'name', 'Findings', states=STATES) state = fields.Selection([ (None, ''), ('in_progress', 'In progress'), ('done', 'Done'), ], 'State', readonly=True, sort=False) signed_by = fields.Many2One( 'gnuhealth.healthprofessional', 'Signed by', readonly=True, states={'invisible': Equal(Eval('state'), 'in_progress')}, help="Health Professional that finished the patient evaluation") def patient_age_at_evaluation(self, name): if (self.patient.name.dob and self.visit_date): rdelta = relativedelta(self.visit_date.date(), self.patient.name.dob) years_months_days = str(rdelta.years) + 'y ' \ + str(rdelta.months) + 'm ' \ + str(rdelta.days) + 'd' return years_months_days else: return None def get_patient_gender(self, name): return self.patient.gender @classmethod def search_patient_gender(cls, name, clause): res = [] value = clause[2] res.append(('patient.name.gender', clause[1], value)) return res @fields.depends('rdva') def on_change_with_rbcva(self): return self.rdva @fields.depends('ldva') def on_change_with_lbcva(self): return self.ldva @fields.depends('rcylinder') def on_change_with_rbcva_cylinder(self): return self.rcylinder @fields.depends('lcylinder') def on_change_with_lbcva_cylinder(self): return self.lcylinder @fields.depends('raxis') def on_change_with_rbcva_axis(self): return self.raxis @fields.depends('laxis') def on_change_with_lbcva_axis(self): return self.laxis @fields.depends('rspherical') def on_change_with_rbcva_spherical(self): return self.rspherical @fields.depends('lspherical') def on_change_with_lbcva_spherical(self): return self.lspherical @fields.depends('rnv_add') def on_change_with_rbcva_nv_add(self): return self.rnv_add @fields.depends('lnv_add') def on_change_with_lbcva_nv_add(self): return self.lnv_add @fields.depends('rnv') def on_change_with_rbcva_nv(self): return self.rnv @fields.depends('lnv') def on_change_with_lbcva_nv(self): return self.lnv @staticmethod def default_visit_date(): return datetime.now() @staticmethod def default_state(): return 'in_progress' @staticmethod def default_health_professional(): pool = Pool() HealthProf = pool.get('gnuhealth.healthprofessional') health_professional = HealthProf.get_health_professional() return health_professional # Show the gender and age upon entering the patient # These two are function fields (don't exist at DB level) @fields.depends('patient') def on_change_patient(self): gender = None age = '' self.gender = self.patient.gender self.computed_age = self.patient.age @classmethod @ModelView.button def end_evaluation(cls, evaluations): evaluation_id = evaluations[0] # Change the state of the evaluation to "Done" HealthProf = Pool().get('gnuhealth.healthprofessional') signing_hp = HealthProf.get_health_professional() cls.write(evaluations, { 'state': 'done', 'signed_by': signing_hp, }) @classmethod def __setup__(cls): super(OphthalmologyEvaluation, cls).__setup__() cls._buttons.update( {'end_evaluation': { 'invisible': Equal(Eval('state'), 'done') }})
class ShipmentOut(metaclass=PoolMeta): __name__ = 'stock.shipment.out' systemlogics_modula = fields.Boolean('SystemLogics Modula') systemlogics_modula_completed = fields.Boolean( 'SystemLogics Modula Completed') systemlogics_modula_sended = fields.Boolean('SystemLogics Modula Sended') ship_create_date = fields.Function(fields.DateTime('Create Date'), 'get_ship_create_date') @staticmethod def default_systemlogics_modula(): return False @staticmethod def default_systemlogics_modula_completed(): return False @staticmethod def default_systemlogics_modula_sended(): return False def get_ship_create_date(self, name): return self.create_date @classmethod def copy(cls, shipments, default=None): if default is None: default = {} default = default.copy() default['systemlogics_modula'] = False default['systemlogics_modula_completed'] = False default['systemlogics_modula_sended'] = False return super(ShipmentOut, cls).copy(shipments, default=default) @classmethod def generate_systemlogics_modula_file(cls, shipments): SystemLogicsModula = Pool().get('systemlogics.modula') # Force not get a rollback to generate XML file shipment_ids = [s.id for s in shipments] Transaction().commit() # Search shipment ID to sure not have a rollback shipments = cls.search([ ('id', 'in', shipment_ids), ], order=[('systemlogics_modula_completed', 'DESC') ]) SystemLogicsModula.imp_ordini(shipments, template='IMP_ORDINI_OUT', type_='P') @classmethod def check_systemlogics_modula(cls, shipments): pool = Pool() Configuration = pool.get('stock.configuration') config = Configuration(1) if not (Transaction().context.get( 'generate_systemlogics_modula', config.try_generate_systemlogics_modula)): return to_write = [] values = {'systemlogics_modula': True} for shipment in shipments: if hasattr(shipment, 'review'): if shipment.review: continue systemLogics = False completed = True for move in shipment.inventory_moves: if move.from_location.systemlogics_modula: systemLogics = True else: completed = False if systemLogics: values = values.copy() if completed: values['systemlogics_modula_completed'] = True else: values['systemlogics_modula_completed'] = False to_write.extend(([shipment], values)) if to_write: cls.write(*to_write) @classmethod def generate_systemlogics_modula(cls, shipments): '''Generate SystemLogics Modula''' Configuration = Pool().get('stock.configuration') config = Configuration(1) if not (Transaction().context.get( 'generate_systemlogics_modula', config.try_generate_systemlogics_modula)): return to_write = [] shipments_modula = [] value = {'systemlogics_modula_sended': True} for shipment in shipments: if hasattr(shipment, 'review'): if shipment.review: continue shipments_modula.append(shipment) to_write.extend(([shipment], value)) if shipments_modula: cls.write(*to_write) cls.generate_systemlogics_modula_file(shipments_modula) @classmethod def assign(cls, shipments): super(ShipmentOut, cls).assign(shipments) assigned = [s for s in shipments if s.state == 'assigned'] cls.check_systemlogics_modula(assigned) cls.generate_systemlogics_modula(assigned) @classmethod def check_systemlogics_modula_scheduler(cls, args=None): ''' This method is intended to be called from ir.cron args: warehouse ids [ids] ''' pool = Pool() Configuration = pool.get('stock.configuration') config = Configuration(1) domain = [ ('state', '=', 'assigned'), ('systemlogics_modula', '=', False), ] if args: domain.append(('warehouse', 'in', args), ) shipments = cls.search(domain) len_ships = len(shipments) logger.info('Scheduler Try Check Systemlogics Modula. Total: %s' % (len_ships)) if len_ships: blocs = 1 slice_systemlogics_modula = (config.slice_systemlogics_modula or len_ships) with Transaction().set_context(generate_systemlogics_modula=True): for sub_shipments in grouped_slice(shipments, slice_systemlogics_modula): logger.info('Start bloc %s of %s.' % (blocs, len_ships / slice_systemlogics_modula)) cls.check_systemlogics_modula(list(sub_shipments)) logger.info('End bloc %s.' % blocs) blocs += 1 logger.info('End Scheduler Try Check Systemlogics Modula.') @classmethod def generate_systemlogics_modula_scheduler(cls, args=None): ''' This method is intended to be called from ir.cron args: warehouse ids [ids] ''' pool = Pool() Configuration = pool.get('stock.configuration') config = Configuration(1) domain = [ ('state', '=', 'assigned'), ('systemlogics_modula', '=', True), ('systemlogics_modula_sended', '=', False), ] if args: domain.append(('warehouse', 'in', args), ) shipments = cls.search(domain) len_ships = len(shipments) logger.info('Scheduler Try generate Systemlogics Modula. Total: %s' % (len_ships)) if len_ships: blocs = 1 slice_systemlogics_modula = (config.slice_systemlogics_modula or len_ships) with Transaction().set_context(generate_systemlogics_modula=True): for sub_shipments in grouped_slice(shipments, slice_systemlogics_modula): logger.info('Start bloc %s of %s.' % (blocs, len_ships / slice_systemlogics_modula)) cls.generate_systemlogics_modula(list(sub_shipments)) logger.info('End bloc %s.' % blocs) blocs += 1 logger.info('End Scheduler Try generate Systemlogics Modula.')
class Address(metaclass=PoolMeta): __name__ = 'party.address' building_address = fields.Function(fields.Char("Building Address"), 'get_building_address') last_visit_time = fields.Function(fields.DateTime("Last Visit Time"), 'get_last_visit', searcher='search_last_visit') last_visit_type = fields.Function(fields.Many2One('sale.direct.visit.type', "Last Visit Type"), 'get_last_visit', searcher='search_last_visit') last_visit_notes = fields.Function(fields.Text("Last Visit Notes"), 'get_last_visit', searcher='search_last_visit') revisit_required = fields.Function(fields.Boolean("Revisit Required"), 'get_last_visit', searcher='search_last_visit') revisit_today = fields.Function(fields.Boolean("Revisit Today"), 'get_last_visit', searcher='search_last_visit') revisit_due = fields.Function(fields.DateTime("Revisit Due"), 'get_last_visit', setter='set_revisit_due', searcher='search_last_visit') @classmethod def __setup__(cls): super().__setup__() cls._buttons.update({ 'visit': {}, 'register_order': {}, }) cls.party.required = False cls.party.states['readonly'] &= Eval('party') def get_building_address(self, name): if self.street: street, *locality = self.street.splitlines() if locality: locality = locality[0] else: street = locality = None if self.country: country = self.country.code else: country = None result = ', '.join( filter(None, [ self.name, street, locality, self.postal_code, self.city, country ])) if not result: result = self.party.rec_name return result @classmethod def order_building_address(cls, tables): address, _ = tables[None] return [ address.country, address.subdivision, address.city, address.street, address.name, address.party, ] @classmethod def get_last_visit(cls, addresses, names): address = cls.__table__() cursor = Transaction().connection.cursor() last_visit = cls.last_visit_query() columns = [address.id] for name in names: columns.append(getattr(last_visit, name)) result = defaultdict(dict) for sub_ids in grouped_slice(addresses): red_sql = reduce_ids(address.id, sub_ids) cursor.execute(*address.join( last_visit, 'LEFT', condition=address.id == last_visit.address).select(*columns, where=red_sql)) for row in cursor.fetchall(): address_id = row[0] for count, name in enumerate(names, 1): result[name][address_id] = row[count] return result @classmethod def search_last_visit(cls, name, clause): _, operator, value = clause Operator = fields.SQL_OPERATORS[operator] last_visit = cls.last_visit_query() query = last_visit.select(last_visit.address, where=Operator(getattr(last_visit, name), value)) return [('id', 'in', query)] @classmethod def set_revisit_due(cls, addresses, name, value): pool = Pool() Visit = pool.get('sale.direct.visit') to_save = [] visits = cls.get_last_visit(addresses, ['id']) for visit_id in visits['id'].values(): visit = Visit(visit_id) visit.revisit_time = value to_save.append(visit) if to_save: Visit.save(to_save) @classmethod def order(cls, tables, name): address, _ = tables[None] key = 'last_visit' if key not in tables: last_visit = cls.last_visit_query() query = address.join(last_visit, 'LEFT', condition=address.id == last_visit.address) tables[key] = { None: (query.right, query.condition), } else: last_visit, _ = tables[key][None] return [getattr(last_visit, name)] @classmethod def order_last_visit_time(cls, tables): return cls.order(tables, 'last_visit_time') @classmethod def order_revisit_due(cls, tables): return cls.order(tables, 'revisit_due') @classmethod def get_visit_address(cls, address): existing_address = cls.search_visit_address(address) if existing_address: return existing_address new_address = cls( name=address.name, street=address.street, city=address.city, country=address.country, subdivision=address.subdivision, ) new_address.save() return new_address @classmethod def search_visit_address(cls, address): domain = [] for name in {'name', 'street', 'city', 'subdivision', 'country'}: value = getattr(address, name, None) if hasattr(value, 'id'): domain.append((name, '=', getattr(value, 'id'))) else: domain.append((name, '=', value)) addresses = cls.search(domain, limit=1) if addresses: return addresses[0] @classmethod def last_visit_query(cls): pool = Pool() Visit = pool.get('sale.direct.visit') tomorrow = Literal( datetime.combine(date.today() + timedelta(days=1), datetime.min.time())) visit = Visit.__table__() last_visit = visit.select( visit.address.as_('address'), Max(visit.time).as_('time'), group_by=[visit.address], ) visit = Visit.__table__() revisit_required = (visit.revisit_time != Null) revisit_today = (visit.revisit_time < tomorrow) return visit.join( last_visit, condition=((visit.address == last_visit.address) & (visit.time == last_visit.time))).select( visit.id.as_('id'), visit.address.as_('address'), visit.time.as_('last_visit_time'), visit.type.as_('last_visit_type'), visit.notes.as_('last_visit_notes'), revisit_required.as_('revisit_required'), revisit_today.as_('revisit_today'), visit.revisit_time.as_('revisit_due'), ) @classmethod def write(cls, *args): pool = Pool() Party = pool.get('party.party') # Skip error when setting the address party actions = iter(args) for addresses, values in zip(actions, actions): for address in addresses: if 'party' in values and address.party is None: address.party = Party(values['party']) super(Address, cls).write(*args) @classmethod @ModelView.button_action('sale_direct.perform_visit_wizard') def visit(cls, addresses): pass @classmethod @ModelView.button_action('sale_direct.register_order_wizard') def register_order(cls, addresses): pass
class BaseComponent(ModelSQL, ModelView): '''All components should inherit from this class. It's not a real model as it is not registered in the pool. It defines the fields needed for a model to be a valid component. ''' active = fields.Boolean('Active', select=True) encounter = fields.Many2One('gnuhealth.encounter', 'Encounter', readonly=True, required=True) start_time = fields.DateTime('Start', required=True, states=SIGNED_STATES) end_time = fields.DateTime('Finish', states=SIGNED_STATES) sign_time = fields.DateTime('Signed', readonly=True, states=SIGNED_VISIBLE) signed_by = fields.Many2One('gnuhealth.healthprofessional', 'Signed by', readonly=True, states=SIGNED_VISIBLE) performed_by = fields.Many2One('gnuhealth.healthprofessional', 'Clinician', states=SIGNED_STATES) warning = fields.Boolean( 'Warning', help="Check this box to alert the supervisor about this session." " It will be shown in red in the encounter", states=SIGNED_STATES) notes = fields.Text('Notes', states=SIGNED_STATES) critical_info = fields.Char('Summary', readonly=True, depends=['notes']) report_info = fields.Function(fields.Text('Report'), 'get_report_info') is_union_comp = fields.Function(fields.Boolean('Unified component'), 'get_is_union') byline = fields.Function( fields.Char('Byline', help='date started, clinician and signed by'), 'get_byline') @classmethod def __setup__(cls): super(BaseComponent, cls).__setup__() cls._order = [('start_time', 'ASC')] cls._order_name = 'start_time' cls.critical_info.depends = cls.get_critical_info_fields() cls._error_messages = { 'bad_start_time': 'Component cannot start before the encounter', 'bad_end_time': 'End time cannot be before start time' } @staticmethod def default_performed_by(): HealthProfessional = Pool().get('gnuhealth.healthprofessional') return HealthProfessional.get_health_professional() @staticmethod def default_active(): return True @classmethod def get_critical_info_fields(cls): ''' return the list of field names that are used to calculate the critical_info summary field ''' return ['notes'] def make_critical_info(self): # return a single line, no more than 140 chars to describe the details # of what's happening in the measurements in this component return "" def get_report_info(self, name): # return details of the data contained in this component as plain text # no length limit return "" def get_is_union(self, name): return False @classmethod def pre_sign(cls, components): '''sets the healthprof and the sign_time for the current instance''' health_prof_model = Pool().get('gnuhealth.healthprofessional') signtime = datetime.now() for comp in components: comp.signed_by = health_prof_model.get_health_professional() comp.sign_time = signtime def pre_save(self): self.critical_info = self.make_critical_info() def on_change_with_critical_info(self, *arg, **kwarg): return self.make_critical_info() @staticmethod def default_start_time(): return datetime.now() @classmethod def get_byline(cls, instances, name): def mk_byline(obj): dt = obj.start_time.strftime('%Y-%m-%d %H:%M') if obj.performed_by: nom = obj.performed_by.name.name else: nom = "-Unspecified-" if obj.signed_by and obj.signed_by != obj.performed_by: nom = u'%s (signed by %s)' % (nom, obj.signed_by.name.name) return u'on %s by %s' % (dt, nom) return dict([(i.id, mk_byline(i)) for i in instances]) @classmethod @ModelView.button def mark_done(cls, components): pass # save the component and set the state to done @classmethod def validate(cls, records): for rec in records: # start time can't be before encounter start time if rec.start_time < rec.encounter.start_time: cls.raise_user_error('bad_start_time') if rec.end_time and rec.end_time < rec.start_time: cls.raise_user_error('bad_end_time')
class Entry(metaclass=PoolMeta): __name__ = 'lims.entry' last_release_date = fields.Function(fields.DateTime('Last Release date'), 'get_last_release_date') qty_lines_pending_invoicing = fields.Function( fields.Integer('Lines pending invoicing'), 'get_qty_lines_pending_invoicing') @classmethod def get_last_release_date(cls, entries, name): cursor = Transaction().connection.cursor() pool = Pool() ResultsVersion = pool.get('lims.results_report.version') ResultsDetail = pool.get('lims.results_report.version.detail') NotebookLine = pool.get('lims.notebook.line') Notebook = pool.get('lims.notebook') Fraction = pool.get('lims.fraction') Sample = pool.get('lims.sample') result = {} for e in entries: cursor.execute( 'SELECT MAX(rd.release_date) ' 'FROM "' + ResultsVersion._table + '" rv ' 'INNER JOIN "' + ResultsDetail._table + '" rd ' 'ON rv.id = rd.report_version ' 'INNER JOIN "' + NotebookLine._table + '" nl ' 'ON nl.results_report = rv.results_report ' 'INNER JOIN "' + Notebook._table + '" n ' 'ON n.id = nl.notebook ' 'INNER JOIN "' + Fraction._table + '" f ' 'ON f.id = n.fraction ' 'INNER JOIN "' + Sample._table + '" s ' 'ON s.id = f.sample ' 'WHERE s.entry = %s ' 'AND rd.state = \'released\' ' 'AND rd.type != \'preliminary\'', (str(e.id), )) result[e.id] = cursor.fetchone()[0] return result @classmethod def get_qty_lines_pending_invoicing(cls, entries, name): cursor = Transaction().connection.cursor() pool = Pool() Sample = pool.get('lims.sample') Fraction = pool.get('lims.fraction') Service = pool.get('lims.service') InvoiceLine = pool.get('account.invoice.line') result = {} for e in entries: result[e.id] = 0 cursor.execute( 'SELECT srv.id ' 'FROM "' + Service._table + '" srv ' 'INNER JOIN "' + Fraction._table + '" f ' 'ON f.id = srv.fraction ' 'INNER JOIN "' + Sample._table + '" s ' 'ON s.id = f.sample ' 'WHERE s.entry = %s', (str(e.id), )) services_ids = [x[0] for x in cursor.fetchall()] if not services_ids: continue origins = '\', \''.join( ['lims.service,' + str(s) for s in services_ids]) cursor.execute('SELECT COUNT(*) ' 'FROM "' + InvoiceLine._table + '" ' 'WHERE origin IN (\'' + origins + '\') ' 'AND invoice IS NULL') result[e.id] = cursor.fetchone()[0] return result @classmethod def on_hold(cls, entries): super().on_hold(entries) cls.create_invoice_lines(entries) @classmethod def create_invoice_lines(cls, entries): Service = Pool().get('lims.service') with Transaction().set_context(_check_access=False): services = Service.search([ ('entry', 'in', [e.id for e in entries]), ('annulled', '=', False), ]) for service in services: service.create_invoice_line() @classmethod def view_toolbar_get(cls): if not Transaction().context.get('ready_for_invoicing', False): return super().view_toolbar_get() # Entries Ready for Invoicing uses specific keywords prints = cls.get_entries_ready_for_invoicing_keyword('form_print') actions = cls.get_entries_ready_for_invoicing_keyword('form_action') relates = cls.get_entries_ready_for_invoicing_keyword('form_relate') result = { 'print': prints, 'action': actions, 'relate': relates, 'exports': [], } return result @classmethod def get_entries_ready_for_invoicing_keyword(cls, keyword): """ Method copied from ActionKeyword. It search for specific keywords for Entries Ready for Invoicing: lims.entry,-2 """ pool = Pool() ActionKeyword = pool.get('ir.action.keyword') Action = pool.get('ir.action') key = (keyword, ('lims.entry', -2)) keywords = ActionKeyword._get_keyword_cache.get(key) if keywords is not None: return keywords clause = [ ('keyword', '=', keyword), ('model', '=', 'lims.entry,-2'), ('action.active', '=', True), ] action_keywords = ActionKeyword.search(clause, order=[]) types = defaultdict(list) for action_keyword in action_keywords: type_ = action_keyword.action.type types[type_].append(action_keyword.action.id) keywords = [] for type_, action_ids in types.items(): for value in Action.get_action_values(type_, action_ids): value['keyword'] = keyword keywords.append(value) keywords.sort(key=operator.itemgetter('name')) ActionKeyword._get_keyword_cache.set(key, keywords) return keywords
class PatientReferral(ModelSQL, ModelView): 'Referral' __name__ = 'gnuhealth.patient.referral' name = fields.Many2One('gnuhealth.patient', 'Patient', required=True, states={ 'readonly': Or(Bool(Eval('from_triage')), Eval('signed_local', False), Bool(Eval('from_encounter'))) }) sex = fields.Function(fields.Selection(SEX_OPTIONS, 'Sex'), 'get_patient_data', searcher='search_patient_data') sex_display = fields.Function(fields.Char('Sex'), 'get_patient_data') puid = fields.Function(fields.Char('UPI'), 'get_patient_data', searcher='search_patient_data') age = fields.Function(fields.Char('Current Age'), 'get_patient_data') dob = fields.Function(fields.Date('Date of Birth'), 'get_patient_data', searcher='search_patient_data') referral_date = fields.DateTime('Date/Time', required=True, states=RO_THERE) referred_by = fields.Many2One('gnuhealth.healthprofessional', 'Referred by', states=RO_THERE) reason = fields.Text('Reason for referral', states=RO_THERE) results = fields.Text('Consultant Notes/Findings', states=RO_HERE) from_institution = fields.Many2One('gnuhealth.institution', 'Referred From', required=True, states=RO_THERE) to_institution = fields.Many2One('gnuhealth.institution', 'Refer To', required=True, states=RO_THERE) to_specialty = fields.Many2One('gnuhealth.specialty', 'Specialty', states=RO_THERE) referral_type = fields.Selection(plus_none(REFERRAL_TYPES), 'Type', sort=False, states=RO_THERE) service_requested = fields.Selection(plus_none(REFERRAL_SERVICE), 'Treatment Required', sort=False, states=RO_THERE) from_triage = fields.Many2One('gnuhealth.triage.entry', 'Triage Entry', states=RO_THERE) from_encounter = fields.Many2One('gnuhealth.encounter', 'Encounter', states=RO_THERE, domain=[('patient', '=', Eval('name'))]) to_appointment = fields.Many2One('gnuhealth.appointment', 'Appointment', domain=[('institution', '=', Eval('to_institution'))], states=RO_HERE) is_local = fields.Function(fields.Boolean('Originated here'), 'get_is_local', searcher='search_target_local') is_target = fields.Function(fields.Boolean('Referred here'), 'get_is_target', searcher='search_target_local') referer = fields.Many2One('gnuhealth.healthprofessional', 'Signed By', states={ 'readonly': True, 'invisible': ~Bool(Eval('referer_sign_date')) }) referer_sign_date = fields.DateTime('Sign date', states={ 'readonly': True, 'invisible': ~Bool(Eval('referer')) }) signed_local = fields.Function(fields.Boolean('Referer Signed'), 'get_signed', searcher='search_signed') referee = fields.Many2One('gnuhealth.healthprofessional', 'Referee', states={ 'readonly': True, 'invisible': ~Bool(Eval('referee_sign_date')) }) referee_sign_date = fields.DateTime('Referee Sign Date', states={ 'readonly': True, 'invisible': ~Bool(Eval('referee')) }) signed_target = fields.Function(fields.Boolean('Referee Signed'), 'get_signed', searcher='search_signed') @classmethod def __setup__(cls): super(PatientReferral, cls).__setup__() cls._error_messages.update({ 'health_professional_warning': 'No health professional ' 'associated with this user' }) cls._buttons.update({ 'btn_sign_local': { 'invisible': Eval('signed_local', False), 'readonly': ~Bool(Eval('name', False)) }, 'btn_sign_target': { 'invisible': Or(Eval('signed_target', False), ~Eval('signed_local', False), ~Eval('is_target', False)) } }) def get_patient_data(self, name): return getattr(self.name, name) @classmethod def search_patient_data(*arg, **kwarg): pass @staticmethod def default_referral_date(): return datetime.now() @staticmethod def default_referred_by(): HP = Pool().get('gnuhealth.healthprofessional') hp = HP.get_health_professional() return hp @staticmethod def default_from_institution(): HI = Pool().get('gnuhealth.institution') hi = HI.get_institution() return hi @staticmethod def default_is_local(): return True # always local when new @classmethod def get_is_local(cls, instances, name): HI = Pool().get('gnuhealth.institution') hi = HI.get_institution() local_list = [(i.id, int(i.from_institution) == hi) for i in instances] return dict(local_list) @classmethod def get_is_target(cls, instances, name): HI = Pool().get('gnuhealth.institution') hi = HI.get_institution() target_list = [(i.id, int(i.to_institution) == hi) for i in instances] return dict(target_list) @classmethod def search_target_local(cls, name, clause): fld, operator, operand = clause HI = Pool().get('gnuhealth.institution') hi = HI.get_institution() field_name = { 'is_local': 'from_institution', 'is_target': 'to_institution' }[name] find_made_here = False if operand is True: if operator in ('=', 'in', 'is', '<=', '>=', 'like', 'ilike'): find_made_here = True else: find_made_here = False else: if operator in ('!=', 'not in', 'not like'): find_made_here = True else: find_made_here = False if find_made_here: return [(field_name, '=', hi)] else: return [(field_name, '!=', hi)] @classmethod def do_sign(cls, referrals, sign_local=True): HP = Pool().get('gnuhealth.healthprofessional') signing_hp = HP.get_health_professional() if not signing_hp: cls.raise_user_error('health_professional_warning') local_sign = [] target_sign = [] now = datetime.now() for referral in referrals: if sign_local: local_sign.append(referral) else: target_sign.append(referral) if local_sign: local_write = {'referer': signing_hp, 'referer_sign_date': now} cls.write(local_sign, local_write) if target_sign: target_write = {'referee': signing_hp, 'referee_sign_date': now} cls.write(target_sign, target_write) @classmethod @ModelView.button def btn_sign_local(cls, referrals): cls.do_sign(referrals, True) @classmethod @ModelView.button def btn_sign_target(cls, referrals): cls.do_sign(referrals, False) @classmethod def get_signed(cls, instances, name): if name == 'signed_local': field_name = 'referer_sign_date' else: field_name = 'referee_sign_date' outlist = [(int(i), getattr(i, field_name) is not None) for i in instances] return dict(outlist) @classmethod def search_signed(cls, name, clause): fld, operator, operand = clause operdict = {'=': '!=', '!=': '='} if name == 'signed_local': field_name = 'referer_sign_date' else: field_name = 'referee_sign_date' if operator in operdict: if operand is True: return [(field_name, operdict[operator], None)] else: return [(field_name, operator, None)] @staticmethod def default_signed_local(): return False @staticmethod def default_signed_target(): return False
class Email(ResourceAccessMixin, ModelSQL, ModelView): "Email" __name__ = 'ir.email' user = fields.Function(fields.Char("User"), 'get_user') at = fields.Function(fields.DateTime("At"), 'get_at') recipients = fields.Char("Recipients", readonly=True) recipients_secondary = fields.Char("Secondary Recipients", readonly=True) recipients_hidden = fields.Char("Hidden Recipients", readonly=True) addresses = fields.One2Many('ir.email.address', 'email', "Addresses", readonly=True) subject = fields.Char("Subject", readonly=True) body = fields.Text("Body", readonly=True) @classmethod def __setup__(cls): super().__setup__() cls._order.insert(0, ('create_date', 'DESC')) cls.__rpc__.update({ 'send': RPC(readonly=False, result=int), 'complete': RPC(check_access=False), }) del cls.__rpc__['create'] def get_user(self, name): return self.create_uid.rec_name def get_at(self, name): return self.create_date.replace(microsecond=0) @classmethod def send(cls, to='', cc='', bcc='', subject='', body='', files=None, record=None, reports=None, attachments=None): pool = Pool() User = pool.get('res.user') ActionReport = pool.get('ir.action.report') Attachment = pool.get('ir.attachment') transaction = Transaction() user = User(transaction.user) Model = pool.get(record[0]) record = Model(record[1]) body_html = HTML_EMAIL % { 'subject': subject, 'body': body, 'signature': user.signature or '', } content = MIMEMultipart('alternative') if html2text: body_text = HTML_EMAIL % { 'subject': subject, 'body': body, 'signature': '', } converter = html2text.HTML2Text() body_text = converter.handle(body_text) if user.signature: body_text += '\n-- \n' + converter.handle(user.signature) part = MIMEText(body_text, 'plain', _charset='utf-8') content.attach(part) part = MIMEText(body_html, 'html', _charset='utf-8') content.attach(part) if files or reports or attachments: msg = MIMEMultipart('mixed') msg.attach(content) if files is None: files = [] else: files = list(files) for report_id in (reports or []): report = ActionReport(report_id) Report = pool.get(report.report_name, type='report') ext, content, _, title = Report.execute([record.id], { 'action_id': report.id, }) name = '%s.%s' % (title, ext) if isinstance(content, str): content = content.encode('utf-8') files.append((name, content)) if attachments: files += [(a.name, a.data) for a in Attachment.browse(attachments)] for name, data in files: mimetype, _ = mimetypes.guess_type(name) if mimetype: attachment = MIMENonMultipart(*mimetype.split('/')) attachment.set_payload(data) encode_base64(attachment) else: attachment = MIMEApplication(data) attachment.add_header('Content-Disposition', 'attachment', filename=('utf-8', '', name)) msg.attach(attachment) else: msg = content from_ = config.get('email', 'from') set_from_header(msg, from_, user.email or from_) msg['To'] = ', '.join(formataddr(a) for a in getaddresses([to])) msg['Cc'] = ', '.join(formataddr(a) for a in getaddresses([cc])) msg['Subject'] = Header(subject, 'utf-8') to_addrs = list( filter( None, map(str.strip, _get_emails(to) + _get_emails(cc) + _get_emails(bcc)))) sendmail_transactional(from_, to_addrs, msg, datamanager=SMTPDataManager(strict=True)) email = cls(recipients=to, recipients_secondary=cc, recipients_hidden=bcc, addresses=[{ 'address': a } for a in to_addrs], subject=subject, body=body, resource=record) email.save() with Transaction().set_context(_check_access=False): attachments_ = [] for name, data in files: attachments_.append( Attachment(resource=email, name=name, data=data)) Attachment.save(attachments_) return email @classmethod def complete(cls, text, limit): limit = int(limit) if not limit > 0: raise ValueError('limit must be > 0: %r' % (limit, )) emails = getaddresses([text]) if not emails: return [] name, email = map(str.strip, emails[-1]) if not name and not email: return [] s = StringMatcher() try: s.set_seq2(_formataddr((name, email))) except UnicodeEncodeError: return [] def generate(name, email): for name, email in cls._match(name, email): try: address = _formataddr((name, email)) except UnicodeEncodeError: continue s.set_seq1(address) yield (s.ratio(), address, ', '.join( map(_formataddr, emails[:-1] + [(name, email)]))) return heapq.nlargest(limit, generate(name, email)) @classmethod def _match(cls, name, email): pool = Pool() User = pool.get('res.user') domain = ['OR'] for field in ['name', 'login', 'email']: for value in [name, email]: if value and len(value) >= 3: domain.append( (field, 'ilike', '%' + escape_wildcard(value) + '%')) for user in User.search([ ('email', '!=', ''), domain, ], order=[]): yield user.name, user.email
class InpatientMedication(ModelSQL, ModelView): 'Inpatient Medication' __name__ = 'gnuhealth.inpatient.medication' name = fields.Many2One('gnuhealth.inpatient.registration', 'Registration Code') 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', required=True) end_treatment = fields.DateTime('End', help='Date of start of Treatment') dose = fields.Float('Dose', help='Amount of medication (eg, 250 mg) per dose', required=True) dose_unit = fields.Many2One( 'gnuhealth.dose.unit', 'dose unit', required=True, help='Unit of measure for the medication to be taken') route = fields.Many2One('gnuhealth.drug.route', 'Administration Route', required=True, help='Drug administration route code.') form = fields.Many2One('gnuhealth.drug.form', 'Form', required=True, help='Drug form, such as tablet or gel') qty = fields.Integer( 'x', required=True, 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.One2Many('gnuhealth.inpatient.medication.admin_time', 'name', "Admin times") log_history = fields.One2Many('gnuhealth.inpatient.medication.log', 'name', "Log History") 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') 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') def on_change_with_is_active(self): is_active = True if (self.discontinued or self.course_completed): is_active = False return is_active 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
class RCRI(ModelSQL, ModelView): 'Revised Cardiac Risk Index' __name__ = 'gnuhealth.rcri' patient = fields.Many2One('gnuhealth.patient', 'Patient ID', required=True) rcri_date = fields.DateTime('Date', required=True) health_professional = fields.Many2One( 'gnuhealth.healthprofessional', 'Health Professional', help="Health professional /" "Cardiologist who signed the assesment RCRI") rcri_high_risk_surgery = fields.Boolean( 'High Risk surgery', help='Includes andy suprainguinal vascular, intraperitoneal,' ' or intrathoracic procedures') rcri_ischemic_history = fields.Boolean( 'History of ischemic heart disease', help="history of MI or a positive exercise test, current \ complaint of chest pain considered to be secondary to myocardial \ ischemia, use of nitrate therapy, or ECG with pathological \ Q waves; do not count prior coronary revascularization procedure \ unless one of the other criteria for ischemic heart disease is \ present") rcri_congestive_history = fields.Boolean( 'History of congestive heart disease') rcri_diabetes_history = fields.Boolean( 'Preoperative Diabetes', help="Diabetes Mellitus requiring treatment with Insulin") rcri_cerebrovascular_history = fields.Boolean( 'History of Cerebrovascular disease') rcri_kidney_history = fields.Boolean( 'Preoperative Kidney disease', help="Preoperative serum creatinine >2.0 mg/dL (177 mol/L)") rcri_total = fields.Integer( 'Score', help='Points 0: Class I Very Low (0.4% complications)\n' 'Points 1: Class II Low (0.9% complications)\n' 'Points 2: Class III Moderate (6.6% complications)\n' 'Points 3 or more : Class IV High (>11% complications)') rcri_class = fields.Selection([ (None, ''), ('I', 'I'), ('II', 'II'), ('III', 'III'), ('IV', 'IV'), ], 'RCRI Class', sort=False) @fields.depends('rcri_high_risk_surgery', 'rcri_ischemic_history', 'rcri_congestive_history', 'rcri_diabetes_history', 'rcri_cerebrovascular_history', 'rcri_kidney_history') def on_change_with_rcri_total(self): total = 0 if self.rcri_high_risk_surgery: total = total + 1 if self.rcri_ischemic_history: total = total + 1 if self.rcri_congestive_history: total = total + 1 if self.rcri_diabetes_history: total = total + 1 if self.rcri_kidney_history: total = total + 1 if self.rcri_cerebrovascular_history: total = total + 1 return total @fields.depends('rcri_high_risk_surgery', 'rcri_ischemic_history', 'rcri_congestive_history', 'rcri_diabetes_history', 'rcri_cerebrovascular_history', 'rcri_kidney_history') def on_change_with_rcri_class(self): rcri_class = '' total = 0 if self.rcri_high_risk_surgery: total = total + 1 if self.rcri_ischemic_history: total = total + 1 if self.rcri_congestive_history: total = total + 1 if self.rcri_diabetes_history: total = total + 1 if self.rcri_kidney_history: total = total + 1 if self.rcri_cerebrovascular_history: total = total + 1 if total == 0: rcri_class = 'I' if total == 1: rcri_class = 'II' if total == 2: rcri_class = 'III' if (total > 2): rcri_class = 'IV' return rcri_class @staticmethod def default_rcri_date(): return datetime.now() @staticmethod def default_rcri_total(): return 0 @staticmethod def default_rcri_class(): return 'I' def get_rec_name(self, name): res = 'Points: ' + str(self.rcri_total) + ' (Class ' + \ str(self.rcri_class) + ')' return res @classmethod def __setup__(cls): super(RCRI, cls).__setup__() cls._order.insert(0, ('rcri_date', 'DESC')) @classmethod def search_rec_name(cls, name, clause): if clause[1].startswith('!') or clause[1].startswith('not '): bool_op = 'AND' else: bool_op = 'OR' return [ bool_op, ('patient', ) + tuple(clause[1:]), ]
class PatientAmbulatoryCare(ModelSQL, ModelView): 'Patient Ambulatory Care' __name__ = 'gnuhealth.patient.ambulatory_care' name = fields.Char('ID', readonly=True) patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True) base_condition = fields.Many2One('gnuhealth.pathology', 'Base Condition') evaluation = fields.Many2One('gnuhealth.patient.evaluation', 'Related Evaluation', domain=[('patient', '=', Eval('patient'))], depends=['patient']) ordering_professional = fields.Many2One('gnuhealth.healthprofessional', 'Ordering Physician') health_professional = fields.Many2One('gnuhealth.healthprofessional', 'Health Professional', readonly=True) procedures = fields.One2Many('gnuhealth.ambulatory_care_procedure', 'name', 'Procedures', help="List of the procedures in this session. Please enter the first " "one as the main procedure") session_number = fields.Integer('Session #', required=True) session_start = fields.DateTime('Start', required=True) # Vital Signs 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).') temperature = fields.Float('Temperature', help='Temperature in celsius') warning = fields.Boolean('Warning', help="Check this box to alert the " "supervisor about this session. It will be shown in red in the " "session list") #Glycemia glycemia = fields.Integer('Glycemia', help='Blood Glucose level') evolution = fields.Selection([ ('initial', 'Initial'), ('n', 'Status Quo'), ('i', 'Improving'), ('w', 'Worsening'), ], 'Evolution', required=True, help="Check your judgement of current " "patient condition", sort=False) session_end = fields.DateTime('End', required=True) next_session = fields.DateTime('Next Session') session_notes = fields.Text('Notes', required=True) @classmethod def __setup__(cls): super(PatientAmbulatoryCare, cls).__setup__() cls._error_messages.update({ 'health_professional_warning': 'No health professional associated to this user', }) cls._order.insert(0, ('session_start', 'DESC')) @classmethod def validate(cls, records): super(PatientAmbulatoryCare, cls).validate(records) for record in records: record.check_health_professional() def check_health_professional(self): if not self.health_professional: self.raise_user_error('health_professional_warning') @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.ambulatory_care_sequence.id) return super(PatientAmbulatoryCare, cls).create(vlist) @staticmethod def default_health_professional(): 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_healthprof=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_healthprofessional WHERE \ name = %s LIMIT 1', (partner_id[0],)) doctor_id = cursor.fetchone() return int(doctor_id[0]) @staticmethod def default_session_start(): return datetime.now() @classmethod def copy(cls, ambulatorycares, default=None): if default is None: default = {} default = default.copy() default['name'] = None default['session_start'] = cls.default_session_start() default['session_end'] = cls.default_session_start() return super(PatientAmbulatoryCare, cls).copy(ambulatorycares, default=default)