Source code for wardboard

# -*- coding: utf-8 -*-
# Part of Open eObs. See LICENSE file for full copyright and licensing details.
"""
Defines models for the `Wardboard` view.
"""
import logging

from openerp import SUPERUSER_ID, api
from openerp.osv import orm, fields, osv

_logger = logging.getLogger(__name__)


[docs]class wardboard_swap_beds(orm.TransientModel): """ Allows a :class:`patient<base.nh_clinical_patient>` to swap beds with another patient on the same ward. """ _name = 'wardboard.swap_beds' _columns = { 'patient1_id': fields.many2one('nh.clinical.patient', 'Current Patient'), 'patient2_id': fields.many2one('nh.clinical.patient', 'Patient To Swap With'), 'ward_location_id': fields.many2one('nh.clinical.location', "Ward"), 'location1_id': fields.many2one('nh.clinical.location', "Current Patient's Location"), 'location2_id': fields.many2one('nh.clinical.location', "Location To Swap With"), }
[docs] def do_swap(self, cr, uid, ids, context=None): """ Swaps the bed :class:`locations<base.nh_clinical_location>` of two :class:`patients<base.nh_clinical_patient>`. :param ids: list of ids for records :type ids: list :returns: ``True`` :rtype: bool """ data = self.browse(cr, uid, ids[0]) values = { 'location1_id': data.location1_id.id, 'location2_id': data.location2_id.id } activity_pool = self.pool['nh.activity'] swap_pool = self.pool['nh.clinical.patient.swap_beds'] swap_id = swap_pool.create_activity( cr, uid, {}, values, context=context) activity_pool.complete(cr, uid, swap_id, context=context)
[docs] def onchange_location2(self, cr, uid, ids, location2_id, context=None): """ Returns dictionary containing the :class:`patient<base.nh_clinical_patient>` id of patient in the :class:`location<base.nh_clinical_location>` of the ``location2_id`` parameter. :param location2_id: location id :type location2_id: int :returns: dictionary containing patient id :rtype: dict """ if not location2_id: return {'value': {'patient2_id': False}} patient_pool = self.pool['nh.clinical.patient'] patient_id = patient_pool.search( cr, uid, [['current_location_id', '=', location2_id]], context=context) if not patient_id: return {'value': {'patient2_id': False, 'location2_id': False}} return {'value': {'patient2_id': patient_id[0]}}
[docs]class wardboard_patient_placement(orm.TransientModel): """ Moves :class:`patient<base.nh_clinical_patient>` from a bed :class:`location<base.nh_clinical_location>` to another vacant bed location. """ _name = "wardboard.patient.placement" _columns = { 'patient_id': fields.many2one('nh.clinical.patient', 'Patient'), 'ward_location_id': fields.many2one('nh.clinical.location', "Ward"), 'bed_src_location_id': fields.many2one('nh.clinical.location', "Source Bed"), 'bed_dst_location_id': fields.many2one('nh.clinical.location', "Destination Bed") }
[docs] def do_move(self, cr, uid, ids, context=None): """ Moves the :class:`patient<base.nh_clinical_patient>` from their current bed location to a destination bed location. :param ids: record ids :type ids: list :returns: ``True`` :rtype: bool """ wiz = self.browse(cr, uid, ids[0], context=context) spell_pool = self.pool['nh.clinical.spell'] move_pool = self.pool['nh.clinical.patient.move'] activity_pool = self.pool['nh.activity'] spell_id = spell_pool.get_by_patient_id( cr, uid, wiz.patient_id.id, context=context) spell = spell_pool.browse(cr, uid, spell_id, context=context) # move to location move_activity_id = move_pool.create_activity( cr, SUPERUSER_ID, {'parent_id': spell.activity_id.id}, {'patient_id': wiz.patient_id.id, 'location_id': wiz.bed_dst_location_id.id}, context=context) activity_pool.complete(cr, uid, move_activity_id, context=context) activity_pool.submit( cr, uid, spell.activity_id.id, {'location_id': wiz.bed_dst_location_id.id}, context=context)
[docs]class wardboard_device_session_start(orm.TransientModel): """ Starts a :class:`device<devices.nh_clinical_device>` session. """ _name = "wardboard.device.session.start" _columns = { 'patient_id': fields.many2one('nh.clinical.patient', 'Patient'), 'device_category_id': fields.many2one('nh.clinical.device.category', 'Device Category'), 'device_type_id': fields.many2one('nh.clinical.device.type', "Device Type"), 'device_id': fields.many2one('nh.clinical.device', "Device"), 'location': fields.char('Location', size=50) }
[docs] def onchange_device_category_id(self, cr, uid, ids, device_category_id, context=None): """ Returns domain dictionary containing the :class:`type<devices.nh_clinical_device_type>` id of the :class:`device<devices.nh_clinical_device>`. :param device_category_id: :class:`category<devices.nh_clinical_device_category>` id of the device :type device_category_id: int :returns: domain dictionary containing ``device_type_id`` :rtype: dict """ response = False if device_category_id: response = {'value': {'device_id': False, 'device_type_id': False}} ids = self.pool['nh.clinical.device.type'].search( cr, uid, [('category_id', '=', device_category_id)]) response.update( {'domain': {'device_type_id': [('id', 'in', ids)]}}) return response
[docs] def onchange_device_type_id(self, cr, uid, ids, device_type_id, context=None): """ Given a device :class:`type<devices.nh_clinical_device_type>` id, it returns a domain dictionary containing the :class:`device<devices.nh_clinical_device>` id. :param device_type_id: type id of the device :type device_type_id: int :returns: domain dictionary containing ``device_id`` :rtype: dict """ response = False if device_type_id: response = {'value': {'device_id': False}} ids = self.pool['nh.clinical.device'].search( cr, uid, [('type_id', '=', device_type_id)]) response.update( {'domain': {'device_id': [('id', 'in', ids), ('is_available', '=', True)]}}) return response
[docs] def onchange_device_id(self, cr, uid, ids, device_id, context=None): """ Given a device :class:`device<devices.nh_clinical_device>` id, it returns a domain dictionary containing the :class:`type<devices.nh_clinical_device_type>` id. :param device_id: id of the device :type device_id: int :returns: domain dictionary containing ``device_id`` :rtype: dict """ device_pool = self.pool['nh.clinical.device'] if not device_id: return {} device = device_pool.browse(cr, uid, device_id, context=context) return {'value': {'device_type_id': device.type_id.id}}
[docs] def do_start(self, cr, uid, ids, context=None): """ Starts a :class:`session<devices.nh_clinical_device_session>` for a device. :param ids: record ids :type ids: list :returns: ``True`` :rtype: bool """ session_pool = self.pool['nh.clinical.device.session'] activity_pool = self.pool['nh.activity'] wiz = self.browse(cr, uid, ids[0], context=context) spell_pool = self.pool['nh.clinical.spell'] spell_id = spell_pool.get_by_patient_id( cr, uid, wiz.patient_id.id, context=context) spell = spell_pool.browse(cr, uid, spell_id, context=context) device_activity_id = session_pool.create_activity( cr, uid, {'parent_id': spell.activity_id.id}, {'patient_id': wiz.patient_id.id, 'device_type_id': wiz.device_type_id.id, 'device_id': wiz.device_id.id if wiz.device_id else False}) activity_pool.start(cr, uid, device_activity_id, context=context) activity_pool.submit( cr, uid, device_activity_id, {'location': wiz.location}, context=context)
[docs]class wardboard_device_session_complete(orm.TransientModel): """ Completes a :class:`session<devices.nh_clinical_device_session>` for a device. """ _name = "wardboard.device.session.complete" _columns = { 'session_id': fields.many2one('nh.clinical.device.session', 'Session'), 'removal_reason': fields.char('Removal reason', size=100), 'planned': fields.selection((('planned', 'Planned'), ('unplanned', 'Unplanned')), 'Planned?') }
[docs] def do_complete(self, cr, uid, ids, context=None): """ Completed a :class:`session<devices.nh_clinical_device_session>` for a device. :param ids: record ids :type ids: list :returns: Odoo `action` definition :rtype: dict """ activity_pool = self.pool['nh.activity'] wiz = self.browse(cr, uid, ids[0]) activity_pool.submit( cr, uid, wiz.session_id.activity_id.id, {'removal_reason': wiz.removal_reason, 'planned': wiz.planned}, context=context) activity_pool.complete( cr, uid, wiz.session_id.activity_id.id, context=context) spell_activity_id = wiz.session_id.activity_id.parent_id.id wardboard_pool = self.pool['nh.clinical.wardboard'] wardboard_id = wardboard_pool.search( cr, uid, [['spell_activity_id', '=', spell_activity_id]])[0] view_id = self.pool['ir.model.data'].get_object_reference( cr, uid, 'nh_eobs', 'view_wardboard_form')[1] return { 'type': 'ir.actions.act_window', 'res_model': 'nh.clinical.wardboard', 'res_id': wardboard_id, 'view_mode': 'form', 'view_type': 'form', 'target': 'current', 'context': context, 'view_id': view_id }
[docs]class nh_clinical_device_session(orm.Model): """ Extends :class:`session<devices.nh_clinical_device_session>`. """ _inherit = "nh.clinical.device.session"
[docs] def device_session_complete(self, cr, uid, ids, context=None): """ Returns an Odoo form window action for :class:`device session complete<wardboard_device_session_complete>` for the view ``view_wardboard_device_session_complete_form``. :param ids: record ids :type ids: list :returns: Odoo `action` definition :rtype: dict """ device_session = self.browse(cr, uid, ids[0], context=context) res_id = self.pool['wardboard.device.session.complete'].create( cr, uid, {'session_id': device_session.id}) view_id = self.pool['ir.model.data'].get_object_reference( cr, uid, 'nh_eobs', 'view_wardboard_device_session_complete_form')[1] return { 'name': "Complete Device Session: %s" % device_session.patient_id.full_name, 'type': 'ir.actions.act_window', 'res_model': 'wardboard.device.session.complete', 'res_id': res_id, 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': context, 'view_id': view_id }
[docs]class nh_clinical_wardboard(orm.Model): """ Represents a :class:`patient<base.nh_clinical_patient>` with basic patient information (`admission`, `spell`, `location`, etc.). Also includes :class:`observation<observations.nh_clinical_patient_observation>` data such as :class:`EWS<ews.nh_clinical_patient_observation_ews>` etc. Wardboard overrides the init method and others to provide an implementation that is backed by database views. When accessing fields on a wardboard, rather than allowing Odoo's ORM to retrieve them from database tables, we are using function fields that call methods that execute hand-written SQL queries on database views. We ensure that the database views these methods use are created when the model is first loaded and it's `init` method is called. Calling create on wardboard fails. Instead you should just browse for them as if they already exist, using the spell id as the id (or multiple spell ids). The id(s) used are passed to the function fields to retrieve the data, so a wardboard record with the correct id simply has the means to get the data you'd expect, it does not need to be formally created. """ _name = "nh.clinical.wardboard" _description = "Wardboard" _auto = False _table = "nh_clinical_wardboard" _trend_strings = [('up', 'up'), ('down', 'down'), ('same', 'same'), ('none', 'none'), ('one', 'one')] _rec_name = 'full_name' def _get_logo(self, cr, uid, ids, fields_name, arg, context=None): res = {} for board in self.browse(cr, uid, ids, context=context): res[board.id] = board.patient_id.partner_id.company_id.logo return res _clinical_risk_selection = [['NoScore', 'No Score Yet'], ['High', 'High Risk'], ['Medium', 'Medium Risk'], ['Low', 'Low Risk'], ['None', 'No Risk']] _boolean_selection = [('yes', 'Yes'), ('no', 'No')] def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): res = super(nh_clinical_wardboard, self).fields_view_get( cr, user, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) if view_type == 'form' and res['fields'].get('o2target'): user_pool = self.pool['res.users'] user_ids = user_pool.search( cr, user, [['groups_id.name', 'in', ['NH Clinical Doctor Group', 'NH Clinical Shift Coordinator Group']]], context=context ) res['fields']['o2target']['readonly'] = not (user in user_ids) return res def _get_started_device_session_ids(self, cr, uid, ids, field_name, arg, context=None): res = {}.fromkeys(ids, False) sql = """select spell_id, ids from wb_activity_data where data_model='nh.clinical.device.session' and state in ('started') and spell_id in (%s)""" \ % ", ".join([str(spell_id) for spell_id in ids]) cr.execute(sql) res.update({r['spell_id']: r['ids'] for r in cr.dictfetchall()}) return res def _get_terminated_device_session_ids(self, cr, uid, ids, field_name, arg, context=None): res = {}.fromkeys(ids, False) sql = """select spell_id, ids from wb_activity_data where data_model='nh.clinical.device.session' and state in ('completed', 'cancelled') and spell_id in (%s)""" % ", ".join( [str(spell_id) for spell_id in ids]) cr.execute(sql) res.update({r['spell_id']: r['ids'] for r in cr.dictfetchall()}) return res def _get_recently_discharged_uids(self, cr, uid, ids, field_name, arg, context=None): res = {}.fromkeys(ids, False) if ids: sql = """select spell_id, user_ids, ward_user_ids from last_discharge_users where spell_id in (%s)""" % ", ".join( [str(spell_id) for spell_id in ids]) cr.execute(sql) res.update( {r['spell_id']: list(set(r['user_ids'] + r['ward_user_ids'])) for r in cr.dictfetchall()}) return res def _get_data_ids_multi(self, cr, uid, ids, field_names, arg, context=None): res = {i: {field_name: [] for field_name in field_names} for i in ids} for field_name in field_names: model_name = self._columns[field_name]._obj sql = """select spell_id, ids from wb_activity_data where data_model='%s' and spell_id in (%s) and state='completed'""" \ % (model_name, ", ".join( [str(spell_id) for spell_id in ids])) cr.execute(sql) rows = cr.dictfetchall() for row in rows: res[row['spell_id']][field_name] = row['ids'] return res def _get_transferred_user_ids(self, cr, uid, ids, field_names, arg, context=None): res = {}.fromkeys(ids, False) if ids: sql = """select spell_id, user_ids, ward_user_ids from last_transfer_users where spell_id in (%s)""" % ", ".join( [str(spell_id) for spell_id in ids]) cr.execute(sql) res.update( {r['spell_id']: list( set(r['user_ids'] + r['ward_user_ids'])) for r in cr.dictfetchall()}) return res def _transferred_user_ids_search(self, cr, uid, obj, name, args, domain=None, context=None): arg1, op, arg2 = args[0] arg2 = arg2 if isinstance(arg2, (list, tuple)) else [arg2] all_ids = self.search(cr, uid, []) wb_user_map = self._get_transferred_user_ids( cr, uid, all_ids, 'transferred_user_ids', None, context=context) wb_ids = [k for k, v in wb_user_map.items() if set( v or []) & set(arg2 or [])] return [('id', 'in', wb_ids)] def _recently_discharged_uids_search(self, cr, uid, obj, name, args, domain=None, context=None): arg1, op, arg2 = args[0] arg2 = arg2 if isinstance(arg2, (list, tuple)) else [arg2] all_ids = self.search(cr, uid, []) user_ids = self._get_recently_discharged_uids( cr, uid, all_ids, 'recently_discharged_uids', None, context=context) wb_ids = [k for k, v in user_ids.items() if set( v or []) & set(arg2 or [])] return [('id', 'in', wb_ids)] _columns = { 'patient_id': fields.many2one('nh.clinical.patient', 'Patient', required=1, ondelete='restrict'), 'company_logo': fields.function(_get_logo, type='binary', string='Logo'), 'spell_activity_id': fields.many2one('nh.activity', 'Spell Activity'), 'spell_date_started': fields.datetime('Spell Start Date'), 'time_since_admission': fields.text('Time since Admission'), 'move_date': fields.datetime('Time since Last Movement'), 'spell_date_terminated': fields.datetime('Spell Discharge Date'), 'recently_discharged': fields.boolean('Recently Discharged'), 'spell_state': fields.char('Spell State', size=50), 'pos_id': fields.many2one('nh.clinical.pos', 'POS'), 'spell_code': fields.text('Spell Code'), 'full_name': fields.text("Family Name"), 'given_name': fields.text("Given Name"), 'middle_names': fields.text("Middle Names"), 'family_name': fields.text("Family Name"), 'location': fields.text("Location"), 'initial': fields.text("Patient Name Initial"), 'clinical_risk': fields.selection(_clinical_risk_selection, "Clinical Risk"), 'ward_id': fields.many2one('nh.clinical.location', 'Ward'), 'location_id': fields.many2one('nh.clinical.location', "Location"), 'location_full_name': fields.related( 'location_id', 'full_name', type='char', size=150, string='Location Name'), 'sex': fields.text("Sex"), 'dob': fields.datetime("DOB"), 'hospital_number': fields.text('Hospital Number'), 'nhs_number': fields.text('NHS Number'), 'age': fields.integer("Age"), 'date_scheduled': fields.datetime("Date Scheduled"), 'next_diff': fields.text("Time to Next Obs"), 'frequency': fields.text("Frequency"), 'ews_score_string': fields.text("Latest Score"), 'ews_score': fields.integer("Latest Score"), 'ews_trend_string': fields.selection(_trend_strings, "Score Trend String"), 'ews_trend': fields.integer("Score Trend"), 'mrsa': fields.selection(_boolean_selection, "MRSA"), 'diabetes': fields.selection(_boolean_selection, "Diabetes"), 'palliative_care': fields.selection(_boolean_selection, "Palliative Care"), 'post_surgery': fields.selection(_boolean_selection, "Post Surgery"), 'critical_care': fields.selection(_boolean_selection, "Critical Care"), 'pbp_monitoring': fields.selection( _boolean_selection, "Postural Blood Pressure Monitoring"), 'height': fields.float("Height"), 'o2target': fields.many2one('nh.clinical.o2level', 'O2 Target'), 'uotarget_vol': fields.integer('Target Volume'), 'uotarget_unit': fields.selection( [[1, 'ml/hour'], [2, 'L/day']], 'Unit'), 'consultant_names': fields.text("Consulting Doctors"), 'terminated_device_session_ids': fields.function( _get_terminated_device_session_ids, type='many2many', relation='nh.clinical.device.session', string='Device Session History'), 'started_device_session_ids': fields.function( _get_started_device_session_ids, type='many2many', relation='nh.clinical.device.session', string='Started Device Sessions'), 'spell_ids': fields.function( _get_data_ids_multi, multi='spell_ids', type='many2many', relation='nh.clinical.spell', string='Spells'), 'move_ids': fields.function( _get_data_ids_multi, multi='move_ids', type='many2many', relation='nh.clinical.patient.move', string='Patient Moves'), 'o2target_ids': fields.function( _get_data_ids_multi, multi='o2target_ids', type='many2many', relation='nh.clinical.patient.o2target', string='O2 Targets'), 'uotarget_ids': fields.function( _get_data_ids_multi, multi='uotarget_ids', type='many2many', relation='nh.clinical.patient.uotarget', string='Urine Output Targets'), 'mrsa_ids': fields.function( _get_data_ids_multi, multi='mrsa_ids', type='many2many', relation='nh.clinical.patient.mrsa', string='MRSA'), 'diabetes_ids': fields.function( _get_data_ids_multi, multi='diabetes_ids', type='many2many', relation='nh.clinical.patient.diabetes', string='Diabetes'), 'pbp_monitoring_ids': fields.function( _get_data_ids_multi, multi='pbp_monitoring_ids', type='many2many', relation='nh.clinical.patient.pbp_monitoring', string='PBP Monitoring'), 'palliative_care_ids': fields.function( _get_data_ids_multi, multi='palliative_care_ids', type='many2many', relation='nh.clinical.patient.palliative_care', string='Palliative Care'), 'post_surgery_ids': fields.function( _get_data_ids_multi, multi='post_surgery_ids', type='many2many', relation='nh.clinical.patient.post_surgery', string='Post Surgery'), 'critical_care_ids': fields.function( _get_data_ids_multi, multi='critical_care_ids', type='many2many', relation='nh.clinical.patient.critical_care', string='Critical Care'), 'pbp_ids': fields.function( _get_data_ids_multi, multi='pbp_ids', type='many2many', relation='nh.clinical.patient.observation.pbp', string='PBP Obs'), 'ews_ids': fields.function( _get_data_ids_multi, multi='ews_ids', type='many2many', relation='nh.clinical.patient.observation.ews', string='EWS Obs'), 'gcs_ids': fields.function( _get_data_ids_multi, multi='gcs_ids', type='many2many', relation='nh.clinical.patient.observation.gcs', string='GCS Obs'), 'pain_ids': fields.function( _get_data_ids_multi, multi='pain_ids', type='many2many', relation='nh.clinical.patient.observation.pain', string='Pain Obs'), 'urine_output_ids': fields.function( _get_data_ids_multi, multi='urine_output_ids', type='many2many', relation='nh.clinical.patient.observation.urine_output', string='Urine Output Flag'), 'ews_list_ids': fields.function( _get_data_ids_multi, multi='ews_list_ids', type='many2many', relation='nh.clinical.patient.observation.ews', string='EWS Obs List'), 'transferred_user_ids': fields.function( _get_transferred_user_ids, type='many2many', relation='res.users', fnct_search=_transferred_user_ids_search, string='Recently Transferred Access'), 'recently_discharged_uids': fields.function( _get_recently_discharged_uids, type='many2many', relation='res.users', fnct_search=_recently_discharged_uids_search, string='Recently Discharged Access'), } _order = 'location asc' def _get_cr_groups(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): res = [['NoScore', 'No Score Yet'], ['High', 'High Risk'], ['Medium', 'Medium Risk'], ['Low', 'Low Risk'], ['None', 'No Risk']] fold = {r[0]: False for r in res} return res, fold _group_by_full = { 'clinical_risk': _get_cr_groups, }
[docs] def onchange_palliative_care(self, cr, uid, ids, pc, ps, cc, context=None): """ Checks if any of the other special circumstances parameters are ``True`` and returns a warning if that is the case. :param pc: palliative care value :type pc: str :param ps: post surgery value :type ps: str :param cc: critical care value :type cc: str :returns: dictionary containing warning and/or values :rtype: dict """ res = {} if pc == 'no': return res # wb = self.browse(cr, uid, ids[0], context=context) if ps == 'yes': res['warning'] = { 'title': 'Warning', 'message': 'You must deactivate Post Surgery status first' } res['value'] = { 'palliative_care': 'no' } return res if cc == 'yes': res['warning'] = { 'title': 'Warning', 'message': 'You must deactivate Critical Care status first' } res['value'] = { 'palliative_care': 'no' } return res return res
[docs] def onchange_critical_care(self, cr, uid, ids, pc, ps, cc, context=None): """ Checks if any of the other special circumstances parameters are ``True`` and returns a warning if that is the case. :param pc: palliative care value :type pc: str :param ps: post surgery value :type ps: str :param cc: critical care value :type cc: str :returns: dictionary containing warning and/or values :rtype: dict """ res = {} if cc == 'no': return res # wb = self.browse(cr, uid, ids[0], context=context) if ps == 'yes': res['warning'] = { 'title': 'Warning', 'message': 'You must deactivate Post Surgery status first' } res['value'] = { 'critical_care': 'no' } return res if pc == 'yes': res['warning'] = { 'title': 'Warning', 'message': 'You must deactivate Palliative Care status first' } res['value'] = { 'critical_care': 'no' } return res return res
[docs] def onchange_post_surgery(self, cr, uid, ids, pc, ps, cc, context=None): """ Checks if any of the other special circumstances parameters are ``True`` and returns a warning if that is the case. :param pc: palliative care value :type pc: str :param ps: post surgery value :type ps: str :param cc: critical care value :type cc: str :returns: dictionary containing warning and/or values :rtype: dict """ res = {} if ps == 'no': return res if pc == 'yes': res['warning'] = { 'title': 'Warning', 'message': 'You must deactivate Palliative Care status first' } res['value'] = { 'post_surgery': 'no' } return res if cc == 'yes': res['warning'] = { 'title': 'Warning', 'message': 'You must deactivate Critical Care status first' } res['value'] = { 'post_surgery': 'no' } return res return res
[docs] def device_session_start(self, cr, uid, ids, context=None): """ Returns an Odoo form window action for :class:`device session start<wardboard_device_session_start>` for the view ``view_wardboard_device_session_start_form``. :param ids: records ids :type ids: list :returns: Odoo form window action :rtype: dict """ wardboard = self.browse(cr, uid, ids[0], context=context) res_id = self.pool['wardboard.device.session.start'].create( cr, uid, {'patient_id': wardboard.patient_id.id, 'device_id': None}) view_id = self.pool['ir.model.data'].get_object_reference( cr, uid, 'nh_eobs', 'view_wardboard_device_session_start_form')[1] return { 'name': "Start Device Session: %s" % wardboard.full_name, 'type': 'ir.actions.act_window', 'res_model': 'wardboard.device.session.start', 'res_id': res_id, 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': context, 'view_id': view_id }
[docs] def open_previous_spell(self, cr, uid, ids, context=None): """ Returns an Odoo form window action for :class:`wardboard<nh_clinical_wardboard>` for the view ``view_wardboard_form_discharged`` to open a previous :class:`spell<spell.nh_clinical_spell>`. :param ids: records ids :type ids: list :returns: Odoo form window action :rtype: dict """ activity_pool = self.pool['nh.activity'] wb = self.browse(cr, uid, ids[0], context=context) activity_ids = activity_pool.search(cr, uid, [ ['data_model', '=', 'nh.clinical.spell'], ['patient_id', '=', wb.patient_id.id], ['sequence', '<', wb.spell_activity_id.sequence], ['state', '=', 'completed'] ], order='sequence desc', context=context) if not activity_ids: raise osv.except_osv( 'No previous spell!', 'This is the oldest spell available for this patient.') spell_id = activity_pool.browse( cr, uid, activity_ids[0], context=context).data_ref.id view_id = self.pool['ir.model.data'].get_object_reference( cr, uid, 'nh_eobs', 'view_wardboard_form_discharged')[1] return { 'name': 'Previous Spell', 'type': 'ir.actions.act_window', 'res_model': 'nh.clinical.wardboard', 'res_id': spell_id, 'view_mode': 'form', 'view_type': 'form', 'target': 'current', 'context': context, 'view_id': view_id }
[docs] def wardboard_swap_beds(self, cr, uid, ids, context=None): """ Returns an Odoo form window action for :class:`swap beds<wardboard_swap_beds>` for the view ``view_wardboard_swap_beds_form``. :param ids: records ids :type ids: list :returns: Odoo form window action :rtype: dict """ swap_beds_pool = self.pool['wardboard.swap_beds'] wb = self.browse(cr, uid, ids[0]) res_id = swap_beds_pool.create(cr, uid, { 'patient1_id': wb.patient_id.id, 'location1_id': wb.location_id.id, 'ward_location_id': wb.location_id.parent_id.id}, context=context) view_id = self.pool['ir.model.data'].get_object_reference( cr, uid, 'nh_eobs', 'view_wardboard_swap_beds_form')[1] return { 'name': "Swap Beds", 'type': 'ir.actions.act_window', 'res_model': 'wardboard.swap_beds', 'res_id': res_id, 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': context, 'view_id': view_id }
[docs] def wardboard_patient_placement(self, cr, uid, ids, context=None): """ Returns an Odoo form window action for :class:`patient placement<wardboard_patient_placement>` for the view ``view_wardboard_patient_placement_form``. Raises an exception if :class:`patient<base.nh_clinical_patient>` isn't placed in a bed. :param ids: records ids :type ids: list :raises: :class:`osv.except_osv<openerp.osv.osv.except_orm>` :returns: Odoo form window action :rtype: dict """ wardboard = self.browse(cr, uid, ids[0], context=context) if wardboard.location_id.usage != 'bed': raise osv.except_osv( "Patient Board Error!", "Patient must be placed to bed before moving!") sql = """ with recursive route(level, path, parent_id, id) as ( select 0, id::text, parent_id, id from nh_clinical_location where parent_id is null union select level + 1, path||','||location.id, location.parent_id, location.id from nh_clinical_location location join route on location.parent_id = route.id ) select route.id as location_id, ('{'||path||'}')::int[] as parent_ids from route where id = %s order by path """ % wardboard.location_id.id cr.execute(sql) parent_ids = (cr.dictfetchone() or {}).get('parent_ids') ward_location_ids = self.pool['nh.clinical.location'].search( cr, uid, [['id', 'in', parent_ids], ['usage', '=', 'ward']]) ward_location_id = ward_location_ids and ward_location_ids[0] or False res_id = self.pool['wardboard.patient.placement'].create( cr, uid, {'patient_id': wardboard.patient_id.id, 'ward_location_id': ward_location_id if ward_location_id else wardboard.location_id.parent_id.id, 'bed_src_location_id': wardboard.location_id.id, 'bed_dst_location_id': None }, context=context) view_id = self.pool['ir.model.data'].get_object_reference( cr, uid, 'nh_eobs', 'view_wardboard_patient_placement_form')[1] return { 'name': "Move Patient: %s" % wardboard.full_name, 'type': 'ir.actions.act_window', 'res_model': 'wardboard.patient.placement', 'res_id': res_id, 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': context, 'view_id': view_id }
[docs] def wardboard_prescribe(self, cr, uid, ids, context=None): """ Returns an Odoo form window action for :class:`wardboard<nh_clinical_wardboard>` for the view ``view_wardboard_prescribe_form``. :param ids: records ids :type ids: list :returns: Odoo form window action :rtype: dict """ wardboard = self.browse(cr, uid, ids[0], context=context) model_data_pool = self.pool['ir.model.data'] model_data_ids = model_data_pool.search( cr, uid, [('name', '=', 'view_wardboard_prescribe_form')], context=context) view_id = model_data_pool.read( cr, uid, model_data_ids, ['res_id'], context=context)[0]['res_id'] return { 'name': wardboard.full_name, 'type': 'ir.actions.act_window', 'res_model': 'nh.clinical.wardboard', 'res_id': ids[0], 'view_mode': 'form', 'view_type': 'form', 'target': 'current', 'context': context, 'view_id': int(view_id) }
[docs] def wardboard_chart(self, cr, uid, ids, context=None): """ Returns an Odoo form window action for :class:`wardboard<nh_clinical_wardboard>` for the view ``view_wardboard_chart_form``. :param ids: records ids :type ids: list :returns: Odoo form window action :rtype: dict """ wardboard = self.browse(cr, uid, ids[0], context=context) model_data_pool = self.pool['ir.model.data'] model_data_ids = model_data_pool.search( cr, uid, [('name', '=', 'view_wardboard_chart_form')], context=context) view_id = model_data_pool.read( cr, uid, model_data_ids, ['res_id'], context=context)[0]['res_id'] return { 'name': wardboard.full_name, 'type': 'ir.actions.act_window', 'res_model': 'nh.clinical.wardboard', 'res_id': ids[0], 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': context, 'view_id': int(view_id) }
[docs] def wardboard_ews(self, cr, uid, ids, context=None): """ Returns an Odoo tree window action for `completed` :class:`ews<ews.nh_clinical_patient_observation_ews>`. :param ids: records ids :type ids: list :returns: Odoo form window action :rtype: dict """ wardboard = self.browse(cr, uid, ids[0], context=context) return { 'name': wardboard.full_name, 'type': 'ir.actions.act_window', 'res_model': 'nh.clinical.patient.observation.ews', 'view_mode': 'tree', 'view_type': 'tree', 'domain': [('patient_id', '=', wardboard.patient_id.id), ('state', '=', 'completed')], 'target': 'new', 'context': context }
[docs] def wardboard_place(self, cr, uid, ids, context=None): """ Returns an Odoo form window action for :class:`patient placement<operations.nh_clinical_patient_placement>` for the view ``view_patient_placement_complete``. :param ids: records ids :type ids: list :returns: Odoo form window action :rtype: dict """ wardboard = self.browse(cr, uid, ids[0], context=context) model_data_pool = self.pool['ir.model.data'] model_data_ids = model_data_pool.search( cr, uid, [('name', '=', 'view_patient_placement_complete')], context=context) view_id = model_data_pool.read( cr, uid, model_data_ids, ['res_id'], context)[0]['res_id'] res_activity_id = self.pool['nh.activity'].search(cr, uid, [ ['parent_id', '=', wardboard.spell_activity_id.id], ['data_model', '=', 'nh.clinical.patient.placement'], ['state', 'not in', ['completed', 'cancelled']]], context=context) res_id = self.pool['nh.clinical.patient.placement'].search( cr, uid, [['activity_id', 'in', res_activity_id]], context=context) context.update({'active_id': res_activity_id[0]}) return { 'name': wardboard.full_name + ' Placement', 'type': 'ir.actions.act_window', 'res_model': 'nh.clinical.patient.placement', 'view_mode': 'form', 'view_type': 'form', 'res_id': res_id[0], 'target': 'new', 'view_id': int(view_id), 'context': context }
[docs] def write(self, cr, uid, ids, vals, context=None): """ Extends Odoo's :meth:`write()<openerp.models.Model.write>`. :returns: ``True`` :rtype: bool """ activity_pool = self.pool['nh.activity'] for wb in self.browse(cr, uid, ids, context=context): if 'mrsa' in vals: mrsa_pool = self.pool['nh.clinical.patient.mrsa'] mrsa_id = mrsa_pool.create_activity(cr, SUPERUSER_ID, { 'parent_id': wb.spell_activity_id.id, }, { 'patient_id': wb.spell_activity_id.patient_id.id, 'status': vals['mrsa'] == 'yes' }, context=context) activity_pool.complete(cr, uid, mrsa_id, context=context) if 'diabetes' in vals: diabetes_pool = self.pool['nh.clinical.patient.diabetes'] diabetes_id = diabetes_pool.create_activity(cr, SUPERUSER_ID, { 'parent_id': wb.spell_activity_id.id, }, { 'patient_id': wb.spell_activity_id.patient_id.id, 'status': vals['diabetes'] == 'yes' }, context=context) activity_pool.complete(cr, uid, diabetes_id, context=context) if 'pbp_monitoring' in vals: pbpm_pool = self.pool['nh.clinical.patient.pbp_monitoring'] pbpm_id = pbpm_pool.create_activity(cr, SUPERUSER_ID, { 'parent_id': wb.spell_activity_id.id, }, { 'patient_id': wb.spell_activity_id.patient_id.id, 'status': vals['pbp_monitoring'] == 'yes' }, context=context) activity_pool.complete(cr, uid, pbpm_id, context=context) if 'o2target' in vals: o2target_pool = self.pool['nh.clinical.patient.o2target'] o2target_id = o2target_pool.create_activity(cr, SUPERUSER_ID, { 'parent_id': wb.spell_activity_id.id, }, { 'patient_id': wb.spell_activity_id.patient_id.id, 'level_id': vals['o2target'] }, context=context) activity_pool.complete(cr, uid, o2target_id, context=context) if 'palliative_care' in vals: pc_pool = self.pool['nh.clinical.patient.palliative_care'] pc_id = pc_pool.create_activity(cr, SUPERUSER_ID, { 'parent_id': wb.spell_activity_id.id, }, { 'patient_id': wb.spell_activity_id.patient_id.id, 'status': vals['palliative_care'] == 'yes' }, context=context) activity_pool.complete(cr, uid, pc_id, context=context) return True
@api.model def get_by_spell_activity_id(self, spell_activity_id): wardboard = \ self.search([('spell_activity_id', '=', spell_activity_id)]) wardboard.ensure_one() return wardboard def init(self, cr): settings_pool = self.pool['nh.clinical.settings'] nh_eobs_sql = self.pool['nh.clinical.sql'] dt_period = \ settings_pool.get_setting(cr, 1, 'discharge_transfer_period') last_discharge_users = \ nh_eobs_sql.get_last_discharge_users('{0}d'.format(dt_period)) last_transfer_users = \ nh_eobs_sql.get_last_transfer_users('{0}d'.format(dt_period)) wardboard = nh_eobs_sql.get_wardboard('{0}d'.format(dt_period)) wb_transfer_ranked = nh_eobs_sql.get_wb_transfer_ranked_sql() cr.execute(""" -- materialized views drop materialized view if exists ews0 cascade; drop materialized view if exists ews1 cascade; drop materialized view if exists ews2 cascade; drop materialized view if exists ward_locations cascade; drop materialized view if exists param cascade; drop materialized view if exists pbp cascade; create or replace view -- activity per spell, data_model, state wb_activity_ranked as( select spell.id as spell_id, activity.*, split_part(activity.data_ref, ',', 2)::int as data_id, rank() over (partition by spell.id, activity.data_model, activity.state order by activity.sequence desc) from nh_clinical_spell spell inner join nh_activity activity on activity.spell_activity_id = spell.activity_id ); create or replace view -- ews per spell, data_model, state wb_ews_ranked as( select * from ( select spell.id as spell_id, activity.*, split_part(activity.data_ref, ',', 2)::int as data_id, rank() over (partition by spell.id, activity.data_model, activity.state order by activity.sequence desc) from nh_clinical_spell spell inner join nh_activity activity on activity.spell_activity_id = spell.activity_id and activity.data_model = 'nh.clinical.patient.observation.ews' left join nh_clinical_patient_observation_ews ews on ews.activity_id = activity.id where activity.state = 'scheduled' or (activity.state != 'scheduled' and ews.clinical_risk != 'Unknown')) sub_query where rank < 3 ); create or replace view wb_spell_ranked as( select * from ( select spell.id as spell_id, activity.*, split_part(activity.data_ref, ',', 2)::int as data_id, rank() over (partition by spell.id, activity.data_model, activity.state order by activity.sequence desc) from nh_clinical_spell spell inner join nh_activity activity on activity.id = spell.activity_id) sub_query where rank = 1 ); create or replace view -- transfer per spell, data_model, state wb_transfer_ranked as({wb_transfer_ranked}); create or replace view -- discharge per spell, data_model, state wb_discharge_ranked as( select * from ( select spell.id as spell_id, activity.*, split_part(activity.data_ref, ',', 2)::int as data_id, rank() over (partition by spell.id, activity.data_model, activity.state order by activity.sequence desc) from nh_clinical_spell spell inner join nh_activity activity on activity.spell_activity_id = spell.activity_id and activity.data_model = 'nh.clinical.patient.discharge') sub_query where rank = 1 ); create materialized view ward_locations as( with recursive ward_loc(id, parent_id, path, ward_id) as ( select lc.id, lc.parent_id, ARRAY[lc.id] as path, lc.id as ward_id from nh_clinical_location as lc where lc.usage = 'ward' union all select l.id, l.parent_id, w.path || ARRAY[l.id] as path, w.path[1] as ward_id from ward_loc as w, nh_clinical_location as l where l.parent_id = w.id) select * from ward_loc ); create or replace view wb_activity_latest as( with max_sequence as( select spell.id as spell_id, activity.data_model, activity.state, max(activity.sequence) as sequence from nh_clinical_spell spell inner join nh_activity activity on activity.patient_id = spell.patient_id group by spell_id, activity.data_model, activity.state ) select max_sequence.spell_id, activity.state, array_agg(activity.id) as ids from nh_activity activity inner join max_sequence on max_sequence.data_model = activity.data_model and max_sequence.state = activity.state and max_sequence.sequence = activity.sequence group by max_sequence.spell_id, activity.state ); create or replace view -- activity data ids per spell/patient_id, data_model, state wb_activity_data as( select spell.id as spell_id, spell.patient_id, activity.data_model, activity.state, array_agg(split_part(activity.data_ref, ',', 2)::int order by split_part(activity.data_ref, ',', 2)::int desc) as ids from nh_clinical_spell spell inner join nh_activity spell_activity on spell_activity.id = spell.activity_id inner join nh_activity activity on activity.parent_id = spell_activity.id group by spell_id, spell.patient_id, activity.data_model, activity.state ); create materialized view ews0 as( select activity.parent_id as spell_activity_id, activity.patient_id, activity.spell_id, activity.state, activity.date_scheduled, ews.id, ews.score, ews.frequency, ews.clinical_risk, case when activity.date_scheduled < now() at time zone 'UTC' then 'overdue: ' else '' end as next_diff_polarity, case activity.date_scheduled is null when false then justify_hours(greatest(now() at time zone 'UTC',activity.date_scheduled) - least(now() at time zone 'UTC', activity.date_scheduled)) else interval '0s' end as next_diff_interval, activity.rank from wb_ews_ranked activity left join nh_clinical_patient_observation_ews ews on activity.data_id = ews.id where activity.rank = 1 and activity.state = 'scheduled' ); create materialized view ews1 as( select activity.parent_id as spell_activity_id, activity.patient_id, activity.spell_id, activity.state, activity.date_scheduled, activity.date_terminated, ews.id, ews.score, ews.frequency, ews.clinical_risk, case when activity.date_scheduled < now() at time zone 'UTC' then 'overdue: ' else '' end as next_diff_polarity, case activity.date_scheduled is null when false then justify_hours(greatest(now() at time zone 'UTC',activity.date_scheduled) - least(now() at time zone 'UTC', activity.date_scheduled)) else interval '0s' end as next_diff_interval, activity.rank from wb_ews_ranked activity inner join nh_clinical_patient_observation_ews ews on activity.data_id = ews.id where activity.rank = 1 and activity.state = 'completed' ); create materialized view ews2 as( select activity.parent_id as spell_activity_id, activity.patient_id, activity.spell_id, activity.state, activity.date_scheduled, ews.id, ews.score, ews.frequency, ews.clinical_risk, case when activity.date_scheduled < now() at time zone 'UTC' then 'overdue: ' else '' end as next_diff_polarity, case activity.date_scheduled is null when false then justify_hours(greatest(now() at time zone 'UTC',activity.date_scheduled) - least(now() at time zone 'UTC', activity.date_scheduled)) else interval '0s' end as next_diff_interval, activity.rank from wb_ews_ranked activity inner join nh_clinical_patient_observation_ews ews on activity.data_id = ews.id where activity.rank = 2 and activity.state = 'completed' ); create or replace view consulting_doctors as( select spell.id as spell_id, array_to_string(array_agg(doctor.name), ' / ') as names from nh_clinical_spell spell inner join con_doctor_spell_rel on con_doctor_spell_rel.spell_id = spell.id inner join res_partner doctor on con_doctor_spell_rel.doctor_id = doctor.id group by spell.id ); create materialized view param as( select activity.spell_id, height.height, diabetes.status as diabetes, mrsa.status as mrsa, pc.status, o2target_level.id as o2target_level_id, ps.status as post_surgery, psactivity.date_terminated as post_surgery_date, cc.status as critical_care, ccactivity.date_terminated as critical_care_date, uotarget.volume as uotarget_vol, uotarget.unit as uotarget_unit from wb_activity_latest activity left join nh_clinical_patient_observation_height height on activity.ids && array[height.activity_id] left join nh_clinical_patient_diabetes diabetes on activity.ids && array[diabetes.activity_id] left join nh_clinical_patient_o2target o2target on activity.ids && array[o2target.activity_id] left join nh_clinical_o2level o2target_level on o2target_level.id = o2target.level_id left join nh_clinical_patient_mrsa mrsa on activity.ids && array[mrsa.activity_id] left join nh_clinical_patient_palliative_care pc on activity.ids && array[pc.activity_id] left join nh_clinical_patient_post_surgery ps on activity.ids && array[ps.activity_id] left join nh_clinical_patient_uotarget uotarget on activity.ids && array[uotarget.activity_id] left join nh_activity psactivity on psactivity.id = ps.activity_id left join nh_clinical_patient_critical_care cc on activity.ids && array[cc.activity_id] left join nh_activity ccactivity on ccactivity.id = cc.activity_id where activity.state = 'completed' ); create materialized view pbp as( select activity.spell_id, pbp.status from wb_activity_latest activity left join nh_clinical_patient_pbp_monitoring pbp on activity.ids && array[pbp.activity_id] where activity.state = 'completed' ); create or replace view last_movement_users as( select spell.id as spell_id, array_agg(distinct users.id) as user_ids, array_agg(distinct users2.id) as ward_user_ids from nh_clinical_spell spell inner join wb_activity_ranked activity on activity.id = spell.activity_id and activity.rank = 1 inner join wb_activity_ranked move on move.parent_id = activity.id and move.rank = 1 and move.state = 'completed' and move.data_model = 'nh.clinical.patient.move' inner join nh_clinical_patient_move move_data on move_data.activity_id = move.id inner join nh_clinical_location location on location.id = move_data.from_location_id inner join ward_locations wl on wl.id = location.id left join user_location_rel ulrel on ulrel.location_id = location.id left join res_users users on users.id = ulrel.user_id left join user_location_rel ulrel2 on ulrel2.location_id = wl.ward_id left join res_users users2 on users2.id = ulrel2.user_id where now() at time zone 'UTC' - move.date_terminated < interval '1d' group by spell.id ); create or replace view last_discharge_users as({last_discharge_users}); create or replace view last_transfer_users as({last_transfer_users}); create or replace view nh_clinical_wardboard as({wardboard}); """.format(last_discharge_users=last_discharge_users, last_transfer_users=last_transfer_users, wardboard=wardboard, wb_transfer_ranked=wb_transfer_ranked))