class ResultsReportVersionDetailSample(metaclass=PoolMeta): __name__ = 'lims.results_report.version.detail.sample' trend_charts = fields.Function(fields.Text('Trend Charts'), 'get_trend_charts') attachments = fields.Function(fields.Text('Attachments'), 'get_attachments') def get_trend_charts(self, name): pool = Pool() OpenTrendChart = pool.get('lims.trend.chart.open', type='wizard') ResultReport = pool.get('lims.result_report', type='report') if not self.version_detail.trend_charts: return '' charts = [] for tc in self.version_detail.trend_charts: session_id, _, _ = OpenTrendChart.create() open_chart = OpenTrendChart(session_id) open_chart.start.chart = tc.chart open_chart.start.notebook = self.notebook open_chart.transition_compute() plot = tc.chart.get_plot(session_id) charts.append(plot) div_row = '<div style="clear:both;">' charts_x_row = int(self.version_detail.charts_x_row) or 1 if charts_x_row == 1: div_col = '<div style="float:left; width:100%;">' elif charts_x_row == 2: div_col = '<div style="float:left; width:50%;">' end_div = '</div>' content = '<div>' count = 0 for chart in charts: if count == 0: content += div_row content += div_col content += ('<img src="' + ResultReport.get_image(chart) + '" alt="" style="width:100%;">') content += end_div count += 1 if count == charts_x_row: content += end_div count = 0 if count != 0: content += end_div content += end_div return content def get_trend_charts_odt(self): pool = Pool() OpenTrendChart = pool.get('lims.trend.chart.open', type='wizard') if not self.version_detail.trend_charts: return [] charts = [] for tc in self.version_detail.trend_charts: session_id, _, _ = OpenTrendChart.create() open_chart = OpenTrendChart(session_id) open_chart.start.chart = tc.chart open_chart.start.notebook = self.notebook open_chart.transition_compute() plot = tc.chart.get_plot(session_id) charts.append(plot) return charts def _get_resource(self, obj): return '%s,%s' % (obj.__name__, obj.id) def get_attachments(self, name): pool = Pool() Attachment = pool.get('ir.attachment') ResultReport = pool.get('lims.result_report', type='report') resources = [] resources.append(self._get_resource(self)) resources.append(self._get_resource(self.notebook)) resources.append(self._get_resource(self.notebook.fraction)) resources.append(self._get_resource(self.notebook.fraction.sample)) resources.append( self._get_resource(self.notebook.fraction.sample.entry)) for line in self.notebook_lines: resources.append(self._get_resource(line)) resources.append(self._get_resource(line.notebook_line)) attachments = Attachment.search([ ('resource', 'in', resources), ]) div_row = '<div>' div_col = '<div style="float:left; width:50%;">' end_div = '</div>' content = '<div>' count = 0 extensions = ['png', 'jpg'] for attachment in attachments: if not any(x in attachment.name.lower() for x in extensions): continue if count == 0: content += div_row content += div_col if attachment.title: content += '<p style="font-size: 6pt;font-family: arial,\ helvetica, sans-serif;">%s</p>' % (attachment.title, ) content += ('<img src="' + ResultReport.get_image(attachment.data) + '" alt="" style="width:100%;">') content += end_div count += 1 if count == 2: content += end_div count = 0 if count != 0: content += end_div content += end_div return content
class notacreditoExito(ModelView): 'notacredito Exito' __name__ = 'sigcoop_wizard_ventas.notacredito.exito' resumen = fields.Text('Resumen', readonly=True)
class Address(DeactivableMixin, sequence_ordered(), ModelSQL, ModelView): "Address" __name__ = 'party.address' party = fields.Many2One('party.party', 'Party', required=True, ondelete='CASCADE', select=True, states={ 'readonly': Eval('id', 0) > 0, }, depends=['id']) party_name = fields.Char( "Party Name", help="If filled, replace the name of the party for address formatting") name = fields.Char("Building Name") street = fields.Text("Street") postal_code = fields.Char("Postal Code") city = fields.Char("City") country = fields.Many2One('country.country', "Country") subdivision_types = fields.Function( fields.MultiSelection('get_subdivision_types', "Subdivision Types"), 'on_change_with_subdivision_types') subdivision = fields.Many2One( "country.subdivision", 'Subdivision', domain=[ ('country', '=', Eval('country', -1)), If(Eval('subdivision_types', []), ('type', 'in', Eval('subdivision_types', [])), ()), ], depends=['country', 'subdivision_types']) full_address = fields.Function(fields.Text('Full Address'), 'get_full_address') @classmethod def __setup__(cls): super(Address, cls).__setup__() cls._order.insert(0, ('party', 'ASC')) cls.__rpc__.update( autocomplete_postal_code=RPC(instantiate=0, cache=dict(days=1)), autocomplete_city=RPC(instantiate=0, cache=dict(days=1)), ) @classmethod def __register__(cls, module_name): cursor = Transaction().connection.cursor() sql_table = cls.__table__() table = cls.__table_handler__(module_name) # Migration from 5.8: rename zip to postal code table.column_rename('zip', 'postal_code') super(Address, cls).__register__(module_name) table = cls.__table_handler__(module_name) # Migration from 4.0: remove streetbis if table.column_exist('streetbis'): value = Concat(Coalesce(sql_table.street, ''), Concat('\n', Coalesce(sql_table.streetbis, ''))) cursor.execute(*sql_table.update([sql_table.street], [value])) table.drop_column('streetbis') _autocomplete_limit = 100 @fields.depends('country', 'subdivision') def _autocomplete_domain(self): domain = [] if self.country: domain.append(('country', '=', self.country.id)) if self.subdivision: domain.append([ 'OR', ('subdivision', 'child_of', [self.subdivision.id], 'parent'), ('subdivision', '=', None), ]) return domain def _autocomplete_search(self, domain, name): pool = Pool() PostalCode = pool.get('country.postal_code') if domain: records = PostalCode.search(domain, limit=self._autocomplete_limit) if len(records) < self._autocomplete_limit: return sorted({getattr(z, name) for z in records}) return [] @fields.depends('city', methods=['_autocomplete_domain']) def autocomplete_postal_code(self): domain = self._autocomplete_domain() if self.city: domain.append(('city', 'ilike', '%%%s%%' % self.city)) return self._autocomplete_search(domain, 'postal_code') @fields.depends('postal_code', methods=['_autocomplete_domain']) def autocomplete_city(self): domain = self._autocomplete_domain() if self.postal_code: domain.append(('postal_code', 'ilike', '%s%%' % self.postal_code)) return self._autocomplete_search(domain, 'city') def get_full_address(self, name): pool = Pool() AddressFormat = pool.get('party.address.format') full_address = Template(AddressFormat.get_format(self)).substitute( **self._get_address_substitutions()) return '\n'.join( filter(None, (x.strip() for x in full_address.splitlines()))) def _get_address_substitutions(self): context = Transaction().context subdivision_code = '' if getattr(self, 'subdivision', None): subdivision_code = self.subdivision.code or '' if '-' in subdivision_code: subdivision_code = subdivision_code.split('-', 1)[1] substitutions = { 'party_name': '', 'attn': '', 'name': getattr(self, 'name', None) or '', 'street': getattr(self, 'street', None) or '', 'postal_code': getattr(self, 'postal_code', None) or '', 'city': getattr(self, 'city', None) or '', 'subdivision': (self.subdivision.name if getattr( self, 'subdivision', None) else ''), 'subdivision_code': subdivision_code, 'country': (self.country.name if getattr(self, 'country', None) else ''), 'country_code': (self.country.code or '' if getattr( self, 'country', None) else ''), } # Keep zip for backward compatibility substitutions['zip'] = substitutions['postal_code'] if context.get('address_from_country') == getattr(self, 'country', ''): substitutions['country'] = '' if context.get('address_with_party', False): substitutions['party_name'] = self.party_full_name if context.get('address_attention_party', False): substitutions['attn'] = ( context['address_attention_party'].full_name) for key, value in list(substitutions.items()): substitutions[key.upper()] = value.upper() return substitutions @property def party_full_name(self): name = '' if self.party_name: name = self.party_name elif self.party: name = self.party.full_name return name def get_rec_name(self, name): party = self.party_full_name if self.street: street = self.street.splitlines()[0] else: street = None if self.country: country = self.country.code else: country = None return ', '.join( filter(None, [ party, self.name, street, self.postal_code, self.city, country ])) @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, ('party', ) + tuple(clause[1:]), ('name', ) + tuple(clause[1:]), ('street', ) + tuple(clause[1:]), ('postal_code', ) + tuple(clause[1:]), ('city', ) + tuple(clause[1:]), ('country', ) + tuple(clause[1:]), ] @classmethod def write(cls, *args): actions = iter(args) for addresses, values in zip(actions, actions): if 'party' in values: for address in addresses: if address.party.id != values['party']: raise AccessError( gettext('party.msg_address_change_party', address=address.rec_name)) super(Address, cls).write(*args) @fields.depends('subdivision', 'country') def on_change_country(self): if (self.subdivision and self.subdivision.country != self.country): self.subdivision = None @classmethod def get_subdivision_types(cls): pool = Pool() Subdivision = pool.get('country.subdivision') return Subdivision.fields_get(['type'])['type']['selection'] @fields.depends('country') def on_change_with_subdivision_types(self, name=None): pool = Pool() Types = pool.get('party.address.subdivision_type') return Types.get_types(self.country)
class Newborn(ModelSQL, ModelView): 'Newborn Information' __name__ = 'gnuhealth.newborn' name = fields.Char('Newborn ID') patient = fields.Many2One('gnuhealth.patient', 'Baby', required=True, help="Patient associated to this newborn") mother = fields.Many2One('gnuhealth.patient', 'Mother') newborn_name = fields.Char('Name at Birth') birth_date = fields.DateTime('DoB', required=True, help="Date and Time of birth") photo = fields.Binary('Picture') newborn_sex = fields.Function( fields.Selection([ ('m', 'Male'), ('f', 'Female'), ], 'Sex'), 'get_newborn_sex') # 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" \ " sex") cephalic_perimeter = fields.Integer( 'CP', help="Cephalic Perimeter in centimeters (cm)") length = fields.Integer('Length', help="Length in centimeters (cm)") weight = fields.Integer('Weight', help="Weight in grams (g)") apgar1 = fields.Integer('APGAR 1st minute') apgar5 = fields.Integer('APGAR 5th minute') apgar_scores = fields.One2Many('gnuhealth.neonatal.apgar', 'name', 'APGAR scores') meconium = fields.Boolean('Meconium') congenital_diseases = fields.One2Many('gnuhealth.patient.disease', 'newborn_id', 'Congenital diseases') reanimation_stimulation = fields.Boolean('Stimulation') reanimation_aspiration = fields.Boolean('Aspiration') reanimation_intubation = fields.Boolean('Intubation') reanimation_mask = fields.Boolean('Mask') reanimation_oxygen = fields.Boolean('Oxygen') test_vdrl = fields.Boolean('VDRL') test_toxo = fields.Boolean('Toxoplasmosis') test_chagas = fields.Boolean('Chagas') test_billirubin = fields.Boolean('Billirubin') test_audition = fields.Boolean('Audition') test_metabolic = fields.Boolean( 'Metabolic ("heel stick screening")', help="Test for Fenilketonuria, Congenital Hypothyroidism, " "Quistic Fibrosis, Galactosemia") neonatal_ortolani = fields.Boolean('Positive Ortolani') neonatal_barlow = fields.Boolean('Positive Barlow') neonatal_hernia = fields.Boolean('Hernia') neonatal_ambiguous_genitalia = fields.Boolean('Ambiguous Genitalia') neonatal_erbs_palsy = fields.Boolean('Erbs Palsy') neonatal_hematoma = fields.Boolean('Hematomas') neonatal_talipes_equinovarus = fields.Boolean('Talipes Equinovarus') neonatal_polydactyly = fields.Boolean('Polydactyly') neonatal_syndactyly = fields.Boolean('Syndactyly') neonatal_moro_reflex = fields.Boolean('Moro Reflex') neonatal_grasp_reflex = fields.Boolean('Grasp Reflex') neonatal_stepping_reflex = fields.Boolean('Stepping Reflex') neonatal_babinski_reflex = fields.Boolean('Babinski Reflex') neonatal_blink_reflex = fields.Boolean('Blink Reflex') neonatal_sucking_reflex = fields.Boolean('Sucking Reflex') neonatal_swimming_reflex = fields.Boolean('Swimming Reflex') neonatal_tonic_neck_reflex = fields.Boolean('Tonic Neck Reflex') neonatal_rooting_reflex = fields.Boolean('Rooting Reflex') neonatal_palmar_crease = fields.Boolean('Transversal Palmar Crease') medication = fields.One2Many('gnuhealth.patient.medication', 'newborn_id', 'Medication') responsible = fields.Many2One('gnuhealth.healthprofessional', 'Doctor in charge', help="Signed by the health professional") dismissed = fields.DateTime('Discharged') 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') notes = fields.Text('Notes') @classmethod def __setup__(cls): super(Newborn, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'unique(name)', 'The Newborn ID must be unique !'), ] def get_newborn_sex(self, name): if self.patient: return self.patient.sex
class Message(Workflow, ModelSQL, ModelView): 'SEPA Message' __name__ = 'account.payment.sepa.message' _states = { 'readonly': Eval('state') != 'draft', } _depends = ['state'] message = fields.Text('Message', states=_states, depends=_depends) filename = fields.Function(fields.Char('Filename'), 'get_filename') type = fields.Selection([ ('in', 'IN'), ('out', 'OUT'), ], 'Type', required=True, states=_states, depends=_depends) company = fields.Many2One('company.company', 'Company', required=True, select=True, domain=[ ('id', If(Eval('context', {}).contains('company'), '=', '!='), Eval('context', {}).get('company', -1)), ], states={ 'readonly': Eval('state') != 'draft', }, depends=['state']) origin = fields.Reference('Origin', selection='get_origin', select=True, states=_states, depends=_depends) state = fields.Selection([ ('draft', 'Draft'), ('waiting', 'Waiting'), ('done', 'Done'), ('canceled', 'Canceled'), ], 'State', readonly=True, select=True) @classmethod def __setup__(cls): super(Message, cls).__setup__() cls._transitions |= { ('draft', 'waiting'), ('waiting', 'done'), ('waiting', 'draft'), ('draft', 'canceled'), ('waiting', 'canceled'), } cls._buttons.update({ 'cancel': { 'invisible': ~Eval('state').in_(['draft', 'waiting']), }, 'draft': { 'invisible': Eval('state') != 'waiting', }, 'wait': { 'invisible': Eval('state') != 'draft', }, 'do': { 'invisible': Eval('state') != 'waiting', }, }) @classmethod def __register__(cls, module_name): TableHandler = backend.get('TableHandler') cursor = Transaction().connection.cursor() pool = Pool() Group = pool.get('account.payment.group') super(Message, cls).__register__(module_name) # Migration from 3.2 if TableHandler.table_exist(Group._table): group_table = TableHandler(Group, module_name) if group_table.column_exist('sepa_message'): group = Group.__table__() table = cls.__table__() cursor.execute(*group.select( group.id, group.sepa_message, group.company)) for group_id, message, company_id in cursor.fetchall(): cursor.execute(*table.insert( [table.message, table.type, table.company, table.origin, table.state], [[message, 'out', company_id, 'account.payment.group,%s' % group_id, 'done']])) group_table.drop_column('sepa_message') @staticmethod def default_type(): return 'in' @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_state(): return 'draft' def get_filename(self, name): pool = Pool() Group = pool.get('account.payment.group') if isinstance(self.origin, Group): return self.origin.rec_name + '.xml' @staticmethod def _get_origin(): 'Return list of Model names for origin Reference' return ['account.payment.group'] @classmethod def get_origin(cls): IrModel = Pool().get('ir.model') models = cls._get_origin() models = IrModel.search([ ('model', 'in', models), ]) return [(None, '')] + [(m.model, m.name) for m in models] @classmethod @ModelView.button @Workflow.transition('draft') def draft(cls, messages): pass @classmethod @ModelView.button @Workflow.transition('waiting') def wait(cls, messages): pass @classmethod @ModelView.button @Workflow.transition('done') def do(cls, messages): for message in messages: if message.type == 'in': message.parse() else: message.send() @classmethod @ModelView.button @Workflow.transition('canceled') def cancel(cls, messages): pass @staticmethod def _get_handlers(): pool = Pool() Payment = pool.get('account.payment') return { 'urn:iso:std:iso:20022:tech:xsd:camt.054.001.01': lambda f: CAMT054(f, Payment), 'urn:iso:std:iso:20022:tech:xsd:camt.054.001.02': lambda f: CAMT054(f, Payment), 'urn:iso:std:iso:20022:tech:xsd:camt.054.001.03': lambda f: CAMT054(f, Payment), 'urn:iso:std:iso:20022:tech:xsd:camt.054.001.04': lambda f: CAMT054(f, Payment), } @staticmethod def get_namespace(message): f = BytesIO(message) for _, element in etree.iterparse(f, events=('start',)): tag = etree.QName(element) if tag.localname == 'Document': return tag.namespace def parse(self): message = self.message.encode('utf-8') f = BytesIO(message) namespace = self.get_namespace(message) handlers = self._get_handlers() if namespace not in handlers: raise # TODO UserError handlers[namespace](f) def send(self): pass
class AddressFormat(DeactivableMixin, MatchMixin, ModelSQL, ModelView): "Address Format" __name__ = 'party.address.format' country = fields.Many2One('country.country', "Country") language = fields.Many2One('ir.lang', "Language") format_ = fields.Text("Format", required=True, help="Available variables (also in upper case):\n" "- ${party_name}\n" "- ${name}\n" "- ${attn}\n" "- ${street}\n" "- ${zip}\n" "- ${city}\n" "- ${subdivision}\n" "- ${subdivision_code}\n" "- ${country}\n" "- ${country_code}") _get_format_cache = Cache('party.address.format.get_format') @classmethod def __setup__(cls): super(AddressFormat, cls).__setup__() cls._order.insert(0, ('country', 'ASC')) cls._order.insert(1, ('language', 'ASC')) @classmethod def default_format_(cls): return """${party_name} ${name} ${street} ${zip} ${city} ${subdivision} ${COUNTRY}""" @staticmethod def order_language(tables): table, _ = tables[None] return [Case((table.language == Null, 1), else_=0), table.language] @classmethod def create(cls, *args, **kwargs): records = super(AddressFormat, cls).create(*args, **kwargs) cls._get_format_cache.clear() return records @classmethod def write(cls, *args, **kwargs): super(AddressFormat, cls).write(*args, **kwargs) cls._get_format_cache.clear() @classmethod def delete(cls, *args, **kwargs): super(AddressFormat, cls).delete(*args, **kwargs) cls._get_format_cache.clear() @classmethod def validate(cls, formats): super(AddressFormat, cls).validate(formats) for format_ in formats: format_.check_format() def check_format(self): pool = Pool() Address = pool.get('party.address') address = Address() try: Template(self.format_).substitute( **address._get_address_substitutions()) except Exception as exception: raise InvalidFormat( gettext('party.invalid_format', format=self.format_, exception=exception)) from exception @classmethod def get_format(cls, address, pattern=None): pool = Pool() Language = pool.get('ir.lang') if pattern is None: pattern = {} else: pattern = pattern.copy() pattern.setdefault('country', address.country.id if address.country else None) languages = Language.search([ ('code', '=', Transaction().language), ], limit=1) if languages: language, = languages else: language = None pattern.setdefault('language', language.id if language else None) key = tuple(sorted(pattern.items())) format_ = cls._get_format_cache.get(key) if format_ is not None: return format_ for record in cls.search([]): if record.match(pattern): format_ = record.format_ break else: format_ = cls.default_format_() cls._get_format_cache.set(key, format_) return format_
class TransportRequest(ModelSQL, ModelView): 'Transport Request Registration' __name__ = 'policoop.transport_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') request_date = fields.DateTime('Date', required=True, help="Date and time of the call for help") return_date = fields.DateTime('Return_date', required=True, help="Date and time of return") 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") 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'), ('urbanzone', 'Urban Zone'), ('ruralzone', 'Rural zone'), ], 'Origin', help="Place of occurrance",sort=False) event_type = fields.Selection([ (None, ''), ('event1', 'Zonal'), ('event2', 'Urbano'), ], 'Event type') service_type = fields.Selection([ (None, ''), ('event1', 'Alta'), ('event2', 'Internación'), ], 'Tipo de Servicio') escort = fields.Text("Acompañante", help="Persona que acompaña al afectado en la ambulancia / Descripción o relación") wait = fields.Selection([ (None, ''), ('event1', 'Si'), ('event2', 'No'), ], '¿Con espera?', help="¿La ambulancia se queda esperando en el lugar?") ambulances = fields.One2Many( 'policoop.ambulance.transport', '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) lines = fields.One2Many( 'policoop.transport.line', 'inventory', 'Lines') @staticmethod def default_request_date(): return datetime.now() @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('policoop.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.transport_request_code_sequence.id) return super(TransportRequest, cls).create(vlist) @classmethod def __setup__(cls): super(TransportRequest, 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 Iss(ModelSQL, ModelView): 'Injury Surveillance System Registration' __name__ = 'gnuhealth.iss' name = fields.Many2One('gnuhealth.patient.evaluation', 'Evaluation', required=True, help='Related Patient Evaluation') injury_date = fields.Date('Injury Date', help="Usually the same as the Evaluation") registration_date = fields.Date('Registration Date') code = fields.Char('Code', help='Injury Code', required=True) operational_sector = fields.Many2One( 'gnuhealth.operational_sector', 'O. Sector', help="Operational Sector in which happened the injury") latitude = fields.Numeric('Latidude', digits=(3, 14)) longitude = fields.Numeric('Longitude', digits=(4, 14)) urladdr = fields.Char( 'OSM Map', help="Maps the Accident / Injury location on Open Street Map") healthcenter = fields.Many2One('gnuhealth.institution', 'Institution') patient = fields.Function(fields.Char('Patient'), 'get_patient', searcher='search_patient') patient_sex = fields.Function(fields.Char('Gender'), 'get_patient_sex') patient_age = fields.Function(fields.Char('Age'), 'get_patient_age') complaint = fields.Function(fields.Char('Chief Complaint'), 'get_patient_complaint') injury_type = fields.Selection([ (None, ''), ('accidental', 'Accidental / Unintentional'), ('violence', 'Violence'), ('attempt_suicide', 'Suicide Attempt'), ('motor_vehicle', 'Motor Vehicle'), ], 'Injury Type', required=True, sort=False) mva_mode = fields.Selection( [ (None, ''), ('pedestrian', 'Pedestrian'), ('bicycle', 'Bicycle'), ('motorbike', 'Motorbike'), ('car', 'Car'), ('van', 'Van / Pickup / Jeep'), ('truck', 'Truck / Heavy vehicle'), ('bus', 'Bus'), ('train', 'Train'), ('taxi', 'Taxi'), ('boat', 'Boat / Ship'), ('aircraft', 'Aircraft'), ('other', 'Other'), ('unknown', 'Unknown'), ], 'Mode', help="Motor Vehicle Accident Mode", sort=False, states={'required': Equal(Eval('injury_type'), 'motor_vehicle')}) mva_position = fields.Selection( [ (None, ''), ('driver', 'Driver'), ('passenger', 'Passenger'), ('outside', 'Outside / on the back'), ('bystander', 'Bystander'), ('unspecified_vehicle', 'Unspecified vehicle'), ('unknown', 'Unknown'), ], 'User Position', help="Motor Vehicle Accident user position", sort=False, states={'required': Equal(Eval('injury_type'), 'motor_vehicle')}) mva_counterpart = fields.Selection( [ (None, ''), ('pedestrian', 'Pedestrian'), ('bicycle', 'Bicycle'), ('motorbike', 'Motorbike'), ('car', 'Car'), ('van', 'Van / Pickup / Jeep'), ('truck', 'Truck / Heavy vehicle'), ('bus', 'Bus'), ('train', 'Train'), ('taxi', 'Taxi'), ('boat', 'Boat / Ship'), ('aircraft', 'Aircraft'), ('other', 'Other'), ('unknown', 'Unknown'), ], 'Counterpart', help="Motor Vehicle Accident Counterpart", sort=False, states={'required': Equal(Eval('injury_type'), 'motor_vehicle')}) safety_gear = fields.Selection( [ (None, ''), ('yes', 'Yes'), ('no', 'No'), ('unknown', 'Unknown'), ], 'Safety Gear', help="Use of Safety Gear - Helmet, safety belt...", sort=False, states={'required': Equal(Eval('injury_type'), 'motor_vehicle')}) alcohol = fields.Selection( [ (None, ''), ('yes', 'Yes'), ('no', 'No'), ('suspected', 'Suspected'), ('unknown', 'Unknown'), ], 'Alcohol', required=True, help="Is there evidence of alcohol use by the injured person" " in the 6 hours before the accident ?", sort=False) drugs = fields.Selection( [ (None, ''), ('yes', 'Yes'), ('no', 'No'), ('suspected', 'Suspected'), ('unknown', 'Unknown'), ], 'Other Drugs', required=True, help="Is there evidence of drug use by the injured person" " in the 6 hours before the accident ?", sort=False) injury_details = fields.Text('Details') # Add victim-perpretator relationship for violence-related injuries victim_perpetrator = fields.Selection( [ (None, ''), ('parent', 'Parent'), ('spouse', 'Wife / Husband'), ('girlboyfriend', 'Girl / Boyfriend'), ('relative', 'Other relative'), ('acquaintance', 'Acquaintance / Friend'), ('official', 'Official / Legal'), ('stranger', 'Stranger'), ('other', 'other'), ], 'Relationship', help="Victim - Perpetrator relationship", sort=False, states={'required': Equal(Eval('injury_type'), 'violence')}) violence_circumstances = fields.Selection( [ (None, ''), ('fight', 'Fight'), ('robbery', 'Robbery'), ('drug', 'Drug Related'), ('sexual', 'Sexual Assault'), ('gang', 'Gang Activity'), ('other_crime', 'Committing a crime (other)'), ('other', 'Other'), ('unknown', 'Unknown'), ], 'Context', help="Precipitating Factor", sort=False, states={'required': Equal(Eval('injury_type'), 'violence')}) injury_method = fields.Selection( [ (None, ''), ('blunt', 'Blunt object'), ('push', 'Push/bodily force'), ('sharp', 'Sharp objects'), ('gun', 'Gun shot'), ('sexual', 'Sexual Assault'), ('choking', 'Choking/strangulation'), ('other', 'Other'), ('unknown', 'Unknown'), ], 'Method', help="Method of Injury", sort=False, states={'required': Equal(Eval('injury_type'), 'violence')}) # Place of occurrance . Not used in motor vehicle accidents place_occurrance = fields.Selection( [ (None, ''), ('home', 'Home'), ('street', 'Street'), ('institution', 'Institution'), ('school', 'School'), ('commerce', 'Commercial Area'), ('publicbuilding', 'Public Building'), ('recreational', 'Recreational Area'), ('transportation', 'Public transportation'), ('sports', 'Sports event'), ('unknown', 'Unknown'), ], 'Place', help="Place of occurrance", sort=False, states={'required': Not(Equal(Eval('injury_type'), 'motor_vehicle'))}) disposition = fields.Selection([ (None, ''), ('treated_sent', 'Treated and Sent Home'), ('admitted', 'Admitted to Ward'), ('observation', 'Admitted to Observation'), ('died', 'Died'), ('daa', 'Discharge Against Advise'), ('transferred', 'Transferred'), ('doa', 'Dead on Arrival'), ], 'Disposition', help="Place of occurrance", sort=False, required=True) def get_patient(self, name): return self.name.patient.rec_name def get_patient_sex(self, name): return self.name.patient.name.gender def get_patient_age(self, name): return self.name.patient.name.age def get_patient_complaint(self, name): return self.name.chief_complaint @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 search_patient(cls, name, clause): res = [] value = clause[2] res.append(('name.patient', clause[1], value)) return res @classmethod def __setup__(cls): super(Iss, cls).__setup__() t = cls.__table__() cls._sql_constraints = [ ('code_uniq', Unique(t, t.code), 'This ISS registration Code already exists'), ] @classmethod def view_attributes(cls): return [ ('//group[@id="motor_vehicle_accident"]', 'states', { 'invisible': Not(Equal(Eval('injury_type'), 'motor_vehicle')), }), ('//group[@id="violent_injury"]', 'states', { 'invisible': Not(Equal(Eval('injury_type'), 'violence')), }), ('//group[@id="iss_place"]', 'states', { 'invisible': Equal(Eval('injury_type'), 'motor_vehicle'), }), ]
class QuotationLine(ModelSQL, ModelView): "Purchase Request For Quotation Line" __name__ = 'purchase.request.quotation.line' supplier = fields.Function(fields.Many2One('party.party', 'Supplier'), 'get_supplier') supply_date = fields.Date('Supply Date', help="When it should be delivered.") product = fields.Function(fields.Many2One('product.product', 'Product'), 'get_product', searcher='search_product') description = fields.Text('Description', states={'required': ~Eval('product')}) quantity = fields.Float("Quantity", digits='unit', required=True) unit = fields.Many2One( 'product.uom', 'Unit', ondelete='RESTRICT', states={ 'required': Bool(Eval('product')), }, domain=[ If(Bool(Eval('product_uom_category')), ('category', '=', Eval('product_uom_category')), ('category', '!=', -1)), ]) product_uom_category = fields.Function( fields.Many2One('product.uom.category', 'Product Uom Category'), 'on_change_with_product_uom_category') unit_price = Monetary("Unit Price", currency='currency', digits=price_digits) currency = fields.Many2One('currency.currency', 'Currency', states={ 'required': Bool(Eval('unit_price')), }) request = fields.Many2One( 'purchase.request', 'Request', ondelete='CASCADE', select=True, required=True, domain=[ If( Eval('quotation_state') == 'draft', ('state', 'in', ['draft', 'quotation', 'received']), (), ), ], states={'readonly': Eval('quotation_state') != 'draft'}, help="The request which this line belongs to.") quotation = fields.Many2One('purchase.request.quotation', 'Quotation', ondelete='CASCADE', required=True, domain=[ ('supplier', '=', Eval('supplier')), ]) quotation_state = fields.Function(fields.Selection('get_quotation_state', 'Quotation State'), 'on_change_with_quotation_state', searcher='search_quotation_state') @classmethod def __setup__(cls): super().__setup__() cls.__access__.add('quotation') @staticmethod def order_quotation_state(tables): pool = Pool() Quotation = pool.get('purchase.request.quotation') quotation_line, _ = tables[None] quotation = Quotation.__table__() tables['purchase.request.quotation'] = { None: (quotation, quotation_line.quotation == quotation.id), } return [ Case((quotation.state == 'received', 0), else_=1), quotation.state ] def get_supplier(self, name): if self.quotation and self.quotation.supplier: return self.quotation.supplier.id @fields.depends('request', '_parent_request.product', '_parent_request.description', '_parent_request.quantity', '_parent_request.uom', '_parent_request.company', '_parent_request.supply_date') def on_change_request(self): if self.request: self.product = self.request.product self.description = self.request.description self.quantity = self.request.quantity self.unit = self.request.uom if self.request.company: self.currency = self.request.company.currency self.supply_date = self.request.supply_date or datetime.date.max @fields.depends('product') def on_change_with_product_uom_category(self, name=None): if self.product: return self.product.default_uom_category.id @classmethod def get_quotation_state(cls): pool = Pool() Quotation = pool.get('purchase.request.quotation') return (Quotation.fields_get(['state'])['state']['selection']) @fields.depends('quotation', '_parent_quotation.state') def on_change_with_quotation_state(self, name=None): pool = Pool() Quotation = pool.get('purchase.request.quotation') if self.quotation: return self.quotation.state return Quotation.default_state() @classmethod def search_quotation_state(cls, name, clause): return [('quotation.state', ) + tuple(clause[1:])] def get_rec_name(self, name): return '%s - %s' % (self.quotation.rec_name, self.supplier.rec_name) @classmethod def search_rec_name(cls, name, clause): names = clause[2].split(' - ', 1) res = [('quotation', clause[1], names[0])] if len(names) != 1 and names[1]: res.append(('supplier', clause[1], names[1])) return res @classmethod def delete(cls, quotationlines): pool = Pool() Request = pool.get('purchase.request') requests = [l.request for l in quotationlines] super(QuotationLine, cls).delete(quotationlines) Request.update_state(requests) def get_product(self, name): if self.request and self.request.product: return self.request.product.id @classmethod def search_product(cls, name, clause): return [('request.' + clause[0], ) + tuple(clause[1:])]
class CrmCaseRule(ModelSQL, ModelView): _name = "ekd.crm.case.rule" _description = "Case Rule" name = fields.Char('Rule Name',size=64, required=True) active = fields.Boolean('Active') sequence = fields.Integer('Sequence') trg_state_from = fields.Selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'Case State') trg_state_to = fields.Selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'Button Pressed') trg_date_type = fields.Selection([ ('none','None'), ('create','Creation Date'), ('action_last','Last Action Date'), ('deadline','Deadline'), ('date','Date'), ], 'Trigger Date') trg_date_range = fields.Integer('Delay after trigger date') trg_date_range_type = fields.Selection([('minutes', 'Minutes'),('hour','Hours'),('day','Days'),('month','Months')], 'Delay type') trg_section = fields.Many2One('ekd.crm.case.section', 'Section') trg_categ = fields.Many2One('ekd.crm.case.category', 'Category', domain=[('section','=',Eval('trg_section'))]) trg_user = fields.Many2One('res.user', 'Responsible') trg_party = fields.Many2One('party.party', 'party') trg_party_categ = fields.Many2One('party.category', 'party Category') trg_priority_from = fields.Selection([('','')] + AVAILABLE_PRIORITIES, 'Minimum Priority') trg_priority_to = fields.Selection([('','')] + AVAILABLE_PRIORITIES, 'Maximim Priority') trg_max_history = fields.Integer('Maximum Communication History') act_method = fields.Char('Call Object Method', size=64) act_state = fields.Selection([('','')]+AVAILABLE_STATES, 'Set state to') act_section = fields.Many2One('ekd.crm.case.section', 'Set section to') act_user = fields.Many2One('res.user', 'Set responsible to') act_priority = fields.Selection([('','')] + AVAILABLE_PRIORITIES, 'Set priority to') act_email_cc = fields.Char('Add watchers (Cc)', size=250, help="These people will receive a copy of the futur communication between party and users by email") act_remind_party = fields.Boolean('Remind party', help="Check this if you want the rule to send a reminder by email to the party.") act_remind_user = fields.Boolean('Remind responsible', help="Check this if you want the rule to send a reminder by email to the user.") act_remind_attach = fields.Boolean('Remind with attachment', help="Check this if you want that all documents attached to the case be attached to the reminder email sent.") act_mail_to_user = fields.Boolean('Mail to responsible'), act_mail_to_party = fields.Boolean('Mail to party'), act_mail_to_watchers = fields.Boolean('Mail to watchers (Cc)'), act_mail_to_email = fields.Char('Mail to these emails', size=128), act_mail_body = fields.Text('Mail body') def __init__(self): super(CrmCaseRule, self).__init__() self._constraints += [ ('_check_mail', 'Error: The mail is not well formated'), ] #self._constraints += [ # ('_check_recursion', 'Error ! You cannot create recursive sections.') #] #self._sql_constraints += [ # ('code_uniq', 'unique(code)', 'The code of the section must be unique !') #] #self._error_messages.update({ # 'recursive_accounts': 'You can not create recursive accounts!', #}) self._order.insert(0, ('sequence', 'ASC')) def default_active(self): return True def default_trg_date_type(self): return 'none' def default_trg_date_range_type(self): return 'day' def default_act_mail_to_user(self): return 0 def default_act_remind_party(self): return 0 def default_act_remind_user(self): return 0 def default_act_mail_to_party(self): return 0 def default_act_mail_to_watchers(self): return 0 def _check(self, ids=False): ''' Function called by the scheduler to process cases for date actions Only works on not done and cancelled cases ''' cr.execute('select * from ekd_crm_case \ where (date_action_last<%s or date_action_last is null) \ and (date_action_next<=%s or date_action_next is null) \ and state not in (\'cancel\',\'done\')', (time.strftime("%Y-%m-%d %H:%M:%S"), time.strftime('%Y-%m-%d %H:%M:%S'))) ids2 = map(lambda x: x[0], cr.fetchall() or []) case_obj = self.pool.get('ekd.crm.case') cases = case_obj.browse( ids2) return case_obj._action( cases, False, context=context) def _check_mail(self, ids): caseobj = self.pool.get('ekd.crm.case') emptycase = orm.browse_null() for rule in self.browse( ids): if rule.act_mail_body: try: caseobj.format_mail(emptycase, rule.act_mail_body) except (ValueError, KeyError, TypeError): return False return True
class CrmCase(ModelSQL, ModelView): _name = "ekd.crm.case" _description = "Case" id = fields.Integer('ID', readonly=True) name = fields.Char('Description',size=64,required=True) priority = fields.Selection(AVAILABLE_PRIORITIES, 'Priority') active = fields.Boolean('Active') description = fields.Text('Your action') section = fields.Many2One('ekd.crm.case.section', 'Section', required=True, select=True) category = fields.Many2One('ekd.crm.case.category', 'Category', on_change=['category'], required=True, domain=[('section','=',Eval('section'))]) planned_revenue = fields.Float('Planned Revenue') planned_cost = fields.Float('Planned Costs') probability = fields.Float('Probability (%)') email_from = fields.Char('party Email', size=128) email_cc = fields.Char('Watchers Emails', size=252) email_last = fields.Function(fields.Text('Latest E-Mail'), '_email_last') party = fields.Many2One('party.party', 'Party') party_address = fields.Many2One('party.address', 'Party Contact', domain=[('party','=',Eval('party'))]) som = fields.Many2One('ekd.party.som', 'State of Mind') date = fields.DateTime('Date') create_date = fields.DateTime('Created' ,readonly=True) date_deadline = fields.DateTime('Deadline') date_closed = fields.DateTime('Closed', readonly=True) channel = fields.Many2One('ekd.party.channel', 'Channel') user = fields.Many2One('res.user', 'Responsible') history_line = fields.One2Many('ekd.crm.case.history', 'case', 'Communication', readonly=1) log_ids = fields.One2Many('ekd.crm.case.log', 'case', 'Logs History', readonly=1) state = fields.Selection(AVAILABLE_STATES, 'State', readonly=True) ref = fields.Reference('Reference', '_links_get') ref2 = fields.Reference('Reference 2', '_links_get') date_action_last = fields.DateTime('Last Action', readonly=1) date_action_next = fields.DateTime('Next Action', readonly=1) def __init__(self): super(CrmCase, self).__init__() self._rpc.update({ 'button_case_log': True, 'button_add_reply': True, 'button_case_log_reply': True, 'button_case_close': True, 'button_case_open': True, 'button_case_pending': True, 'button_case_escalate': True, 'button_case_reset': True, 'button_case_cancel': True, 'button_remind_party': True, 'button_remind_user': True, }) self._order.insert(0, ('priority', 'ASC')) self._order.insert(1, ('date_deadline', 'DESC')) self._order.insert(2, ('date', 'DESC')) self._order.insert(3, ('id', 'DESC')) def _email_last(self, ids, name, arg): res = {} for case in self.browse(ids): if case.history_line: res[case.id] = case.history_line[0].description else: res[case.id] = False return res def copy(self, id, default=None): if not default: default = {} default.update( {'state':'draft', 'id':False, 'history_line':[],'log_ids':[]}) return super(crm_case, self).copy( id, default) def default_active(self): return True # def default_user(self): # return Transaction().user # def default_party(self): # user = self.pool.get('res.user').browse(Transaction().user) # if not user.address: # return False # return user.address.party.id # def default_party_address(self): # return self.pool.get('res.user').browse(Transaction().user).address.id # def default_email_from(self): # context = Transaction().context # if not context.get('portal',False): # return False # user = self.pool.get('res.user').browse(Transaction().user) # if not user.address: # return False # raise Exception(Transaction().user) # return user.address.email def default_state(self): return 'draft' def default_priority(self): return AVAILABLE_PRIORITIES[2][0], def default_date(self): return datetime.datetime.now() def _links_get(self): request_link_obj = self.pool.get('res.request.link') ids = request_link_obj.search([]) request_links = request_link_obj.browse(ids) if request_links: return [(x.model, x.name) for x in request_links] def unlink(self, ids): for case in self.browse( ids): if (not case.section.allow_unlink) and (case.state <> 'draft'): self.raise_user_error('Warning !', 'You can not delete this case. You should better cancel it.') return super(crm_case, self).unlink( ids)
class Product(DeactivableMixin, ModelSQL, ModelView, CompanyMultiValueMixin): "Product Variant" __name__ = "product.product" _order_name = 'rec_name' template = fields.Many2One('product.template', 'Product Template', required=True, ondelete='CASCADE', select=True, states=STATES, depends=DEPENDS) code = fields.Char("Code", size=None, select=True, states=STATES, depends=DEPENDS) cost_price = fields.MultiValue( fields.Numeric("Cost Price", required=True, digits=price_digits, states=STATES, depends=DEPENDS)) cost_prices = fields.One2Many('product.cost_price', 'product', "Cost Prices") description = fields.Text("Description", translate=True, states=STATES, depends=DEPENDS) list_price_uom = fields.Function( fields.Numeric('List Price', digits=price_digits), 'get_price_uom') cost_price_uom = fields.Function( fields.Numeric('Cost Price', digits=price_digits), 'get_price_uom') @classmethod def __setup__(cls): pool = Pool() Template = pool.get('product.template') if not hasattr(cls, '_no_template_field'): cls._no_template_field = set() cls._no_template_field.update(['products']) super(Product, cls).__setup__() for attr in dir(Template): tfield = getattr(Template, attr) if not isinstance(tfield, fields.Field): continue if attr in cls._no_template_field: continue field = getattr(cls, attr, None) if not field or isinstance(field, TemplateFunction): setattr(cls, attr, TemplateFunction(copy.deepcopy(tfield))) order_method = getattr(cls, 'order_%s' % attr, None) if (not order_method and not isinstance( tfield, (fields.Function, fields.One2Many, fields.Many2Many))): order_method = TemplateFunction.order(attr) setattr(cls, 'order_%s' % attr, order_method) if isinstance(tfield, fields.One2Many): getattr(cls, attr).setter = '_set_template_function' @classmethod def _set_template_function(cls, products, name, value): # Prevent NotImplementedError for One2Many pass @fields.depends('template') def on_change_template(self): for name, field in self._fields.items(): if isinstance(field, TemplateFunction): if self.template: value = getattr(self.template, name, None) else: value = None setattr(self, name, value) def get_template(self, name): value = getattr(self.template, name) if isinstance(value, Model): return value.id elif (isinstance(value, (list, tuple)) and value and isinstance(value[0], Model)): return [r.id for r in value] else: return value @classmethod def multivalue_model(cls, field): pool = Pool() if field == 'cost_price': return pool.get('product.cost_price') return super(Product, cls).multivalue_model(field) @classmethod def default_cost_price(cls, **pattern): return Decimal(0) @classmethod def search_template(cls, name, clause): return [('template.' + clause[0], ) + tuple(clause[1:])] @classmethod def order_rec_name(cls, tables): pool = Pool() Template = pool.get('product.template') product, _ = tables[None] if 'template' not in tables: template = Template.__table__() tables['template'] = { None: (template, product.template == template.id), } else: template = tables['template'] return [product.code] + Template.name.convert_order( 'name', tables['template'], Template) def get_rec_name(self, name): if self.code: return '[' + self.code + '] ' + self.name else: return self.name @classmethod def search_rec_name(cls, name, clause): if clause[1].startswith('!') or clause[1].startswith('not '): bool_op = 'AND' else: bool_op = 'OR' code_value = clause[2] if clause[1].endswith('like'): code_value = lstrip_wildcard(clause[2]) return [ bool_op, ('code', clause[1], code_value) + tuple(clause[3:]), ('template.name', ) + tuple(clause[1:]), ] @staticmethod def get_price_uom(products, name): Uom = Pool().get('product.uom') res = {} field = name[:-4] if Transaction().context.get('uom'): to_uom = Uom(Transaction().context['uom']) else: to_uom = None for product in products: price = getattr(product, field) if to_uom and product.default_uom.category == to_uom.category: res[product.id] = Uom.compute_price(product.default_uom, price, to_uom) else: res[product.id] = price return res @classmethod def search_global(cls, text): for id_, rec_name, icon in super(Product, cls).search_global(text): icon = icon or 'tryton-product' yield id_, rec_name, icon
class ImportCSVColumn(ModelSQL, ModelView): 'Import CSV Column' __name__ = 'import.csv.column' profile_csv = fields.Many2One('import.csv', 'Import CSV', required=True, ondelete='CASCADE') column = fields.Char( 'Columns', required=False, states={'invisible': Bool(Eval('constant'))}, help=('The position of the CSV columns separated by commas ' 'to get the content of this field. First one is 0.')) constant = fields.Char( 'Constant', states={'invisible': Bool(Eval('column'))}, help='A constant value to set in this field.', ) field = fields.Many2One('ir.model.field', 'Field', domain=[('model', '=', Eval('_parent_profile_csv', {}).get('model'))], select=True, required=True) field_type = fields.Function(fields.Char('Field Type'), 'on_change_with_field_type') submodel = fields.Function(fields.Many2One('ir.model', 'Submodel'), 'on_change_with_submodel') subfield = fields.Many2One( 'ir.model.field', 'Subfield', domain=[('model', '=', Eval('submodel'))], states={ 'invisible': ~Eval('field_type').in_(['one2many', 'many2many']), 'required': Eval('field_type').in_(['one2many', 'many2many']), }, depends=['field_type', 'submodel'], select=True) ttype = fields.Function(fields.Char('Field Type'), 'on_change_with_ttype') date_format = fields.Char( 'Date Format', states={ 'invisible': Not( In(Eval('ttype'), [ 'datetime', 'date', 'timestamp', 'time', 'many2one', 'one2many', 'many2many' ])), 'required': In(Eval('ttype'), ['datetime', 'date', 'timestamp', 'time']), }, help=( 'Set the CSV format of the DateTime, Date or Timestamp data.\n\n' '%d: Day.\t\t\t\t%H: Hours.\n' '%m: Month.\t\t\t%M: Minutes.\n' '%Y: Year.\t\t\t\t%S: Seconds.\n\n' 'Use commas to separate more than one field.\n\n' 'Eg:\n\n%d/%m/%Y,%H:%M:%S\n\n' 'Will match date and time from two fields like \'13/01/2015\' and ' '\'17:01:56\'\n\n' 'To see more information visit: ' 'https://docs.python.org/2/library/datetime.html' '?highlight=datetime#strftime-and-strptime-behavior')) add_to_domain = fields.Boolean( 'Add to Domain', help='If checked, adds this field to domain for searching records in ' 'order to avoid duplicates.') selection = fields.Text( 'Selection', states={ 'invisible': Eval('ttype') != 'selection', }, depends=['ttype'], help='A couple of key and value separated by ":" per line') @classmethod def __setup__(cls): super(ImportCSVColumn, cls).__setup__() cls._order = [ ('column', 'ASC'), ('id', 'DESC'), ] cls._error_messages.update({ 'columns_must_be_integers': ('Columns on field \'%s\' must be integers separated by ' 'commas.'), 'numeric_format_error': ('Error importing numeric.\n' 'Possible causes:\n\n' '\t- The format of the number is wrong.\n' '\t- The csv file has a header and you does not checked ' 'the box on the wizard.\n\n' 'Field: \'%s\'\n' 'Value: \'%s\'\n'), 'datetime_format_error': ('Error importing DateTime, Date or ' 'Time.\n' 'Possible causes:\n\n' '\t- The format of the date is wrong.\n' '\t- The csv file has a header and you does not checked ' 'the box on the wizard.\n\n' 'Field: \'%s\'\n' 'Value: \'%s\'\n' 'Format: \'%s\''), 'char_encoding_error': ('Error importing Char.\n' 'Possible causes:\n\n' '\t- The character encoding of the file is wrong.\n' 'Field: \'%s\'\n'), 'integer_too_big_error': ('Error importing integer.\n' 'Field \'%s\' has a very big number:\n' 'Value: \'%s\'\n' 'Value must be between -2147483648 and 2147483647'), 'integer_format_error': ('Error importing integer.\n' 'Possible causes:\n\n' '\t- The format of the number is wrong.\n' '\t- The csv file has a header and you does not checked ' 'the box on the wizard.\n\n' 'Field: \'%s\'\n' 'Value: \'%s\'\n'), 'boolean_format_error': ('Error importing boolean.\n' 'Possible causes:\n\n' '\t- The format of the boolean is wrong.\n' '\t- The csv file has a header and you does not checked ' 'the box on the wizard.\n\n' 'Field: \'%s\'\n' 'Value: \'%s\'\n'), 'column_and_constant_null_error': ('The "Columns" and ' '"Constant" fields of line %s can not be empty at the ' 'ame time. Please fill at least one of them.'), }) @fields.depends('field') def on_change_with_field_type(self, name=None): if self.field: return self.field.ttype return '' @fields.depends('field', '_parent_profile_csv.model') def on_change_with_submodel(self, name=None): Model = Pool().get('ir.model') if getattr(self, 'profile_csv'): profile_model = self.profile_csv.model elif 'model' in Transaction().context: profile_model = Model(Transaction().context.get('model')) else: return None ProfileModel = Pool().get(profile_model.model) if (self.field and self.field.ttype in ['many2one', 'one2many', 'many2many']): field = ProfileModel._fields[self.field.name] relation = field.get_target().__name__ models = Model.search([('model', '=', relation)]) return models[0].id if models else None return None @classmethod def validate(cls, records): super(ImportCSVColumn, cls).validate(records) cls.check_sources(records) cls.check_columns(records) @classmethod def check_sources(cls, columns): for column in columns: if not column.column and not column.constant: cls.raise_user_error('column_and_constant_null_error', error_args=(column.field.name, )) @classmethod def check_columns(cls, columns): for column in columns: cells = column.column if not cells: continue cells = cells.split(',') for cell in cells: try: int(cell) except ValueError: cls.raise_user_error('columns_must_be_integers', error_args=(column.field.name, )) def field_required(self): field = Pool().get(self.field.model.model) return (field._fields[self.field.name].required or field._fields[self.field.name].states.get( 'required', False)) @fields.depends('field') def on_change_with_ttype(self, name=None): if self.field: return self.field.ttype @property def digits(self): digits = 4 if self.field.ttype in ('float', 'numeric'): Model = Pool().get(self.field.model.model) digits = Model._fields.get(self.field.name).digits[1] if isinstance(digits, PYSON): digits = PYSONDecoder().decode(PYSONEncoder().encode(digits)) return digits def get_numeric(self, values): for value in values: quantize = Decimal(10)**-Decimal(self.digits) thousands_separator = self.profile_csv.thousands_separator decimal_separator = self.profile_csv.decimal_separator if thousands_separator != 'none': value = value.replace(thousands_separator, '') if decimal_separator == ',': value = value.replace(decimal_separator, '.') try: value = Decimal(value).quantize(quantize) except: self.raise_user_error('numeric_format_error', error_args=(self.field.name, value)) return value def get_char(self, values): result = '' for value in values: character_encoding = self.profile_csv.character_encoding # Python3 strings can not be decoded if hasattr(value, 'decode'): try: value = value.decode(character_encoding) except: self.raise_user_error('char_encoding_error', error_args=(self.field.name)) if result: result += ', ' + value else: result = value return result def get_text(self, values): return self.get_char(values) def get_integer(self, values): for value in values: try: value = int(value) except: self.raise_user_error('integer_format_error', error_args=(self.field.name, value)) if value < -2147483648 or value > 2147483647: self.raise_user_error('integer_too_big_error', error_args=(self.field.name, value)) return value def get_datetime(self, values): for value in values: date_format = self.date_format try: value = datetime.strptime(value, date_format) except ValueError: self.raise_user_error('datetime_format_error', error_args=(self.field.name, value, date_format)) return value def get_date(self, values): value = self.get_datetime(values) return date(value.year, value.month, value.day) def get_time(self, values): for value in values: date_format = self.date_format try: value = time.strptime(value, date_format) except ValueError: self.raise_user_error('datetime_format_error', error_args=(self.field.name, value, date_format)) return value def get_timestamp(self, values): return self.get_datetime(values) def get_boolean(self, values): for value in values: try: value = bool(value) except: self.raise_user_error('boolean_format_error', error_args=(self.field.name, value)) return value def get_selection(self, values): value = self.get_char([values[0]]) map_values = self.selection or u'' if map_values: for pair in map_values.splitlines(): if pair: key, map_value = pair.split(':') if key == value: value = map_value.strip() break return value def get_many2one(self, values): Model = Pool().get(self.field.relation) records = Model.search([ ('name', '=', values[0]), ]) if records: return records[0] else: return def get_one2many(self, values): return values[0] def get_many2many(self, values): # TODO pass def get_value(self, values): if values and values[0]: return getattr(self, 'get_%s' % self.ttype)(values) elif self.constant: return getattr(self, 'get_%s' % self.ttype)([self.constant])
class DocumentType(ModelSQL, ModelView): """ Elastic Search Document Type Definition This will in future be used for the mapping too. """ __name__ = "elasticsearch.document.type" name = fields.Char('Name', required=True) model = fields.Many2One('ir.model', 'Model', required=True, select=True) trigger = fields.Many2One('ir.trigger', 'Trigger', required=False, ondelete='RESTRICT') mapping = fields.Text('Mapping', required=True) @staticmethod def default_mapping(): return '{}' @classmethod def __setup__(cls): super(DocumentType, cls).__setup__() # TODO: add a unique constraint on model cls._buttons.update({ 'update_mapping': {}, 'reindex_all_records': {}, 'get_default_mapping': {}, }) cls._error_messages.update({ 'wrong_mapping': 'Mapping does not seem to be valid JSON', }) @classmethod def create(cls, document_types): "Create records and make appropriate triggers" # So that we don't modify the original data passed document_types = [dt.copy() for dt in document_types] for document_type in document_types: document_type['trigger'] = cls._trigger_create( document_type['name'], document_type['model']).id return super(DocumentType, cls).create(document_types) @classmethod def write(cls, document_types, values): "Update records and add/remove triggers appropriately" Trigger = Pool().get('ir.trigger') if 'trigger' in values: raise UserError("Updating Trigger manually is not allowed!") triggers_to_delete = [] for document_type in document_types: triggers_to_delete.append(document_type.trigger) values_new = values.copy() # so that we don't change the original values passed to us trigger = cls._trigger_create( values_new.get('name', document_type.name), values_new.get('model', document_type.model.id)) values_new['trigger'] = trigger.id super(DocumentType, cls).write([document_type], values_new) Trigger.delete(triggers_to_delete) @classmethod def delete(cls, document_types): "Delete records and remove associated triggers" Trigger = Pool().get('ir.trigger') triggers_to_delete = [dt.trigger for dt in document_types] super(DocumentType, cls).delete(document_types) Trigger.delete(triggers_to_delete) @classmethod def _trigger_create(cls, name, model): """Create trigger for model :param name: Name of the DocumentType used as Trigger name :param model: Model id """ Trigger = Pool().get('ir.trigger') Model = Pool().get('ir.model') index_model = Model(model) action_model, = Model.search([ ('model', '=', cls.__name__), ]) return Trigger.create([{ 'name': "elasticsearch_%s" % name, 'model': index_model.id, 'on_create': True, 'on_write': True, 'on_delete': True, 'action_model': action_model.id, 'condition': 'True', 'action_function': '_trigger_handler', }])[0] @classmethod def _trigger_handler(cls, records, trigger): "Handler called by trigger" return IndexBacklog.create_from_records(records) @classmethod def validate(cls, document_types): "Validate the records" super(DocumentType, cls).validate(document_types) for document_type in document_types: document_type.check_mapping() def check_mapping(self): """ Check if it is possible to at least load the JSON as a check for its validity """ try: json.loads(self.mapping) except: self.raise_user_error('wrong_mapping') @classmethod @ModelView.button def reindex_all_records(cls, document_types): """ Reindex all of the records in this model :param document_types: Document Types """ IndexBacklog = Pool().get('elasticsearch.index_backlog') for document_type in document_types: Model = Pool().get(document_type.model.model) records = Model.search([]) # Performance speedups index_backlog_create = IndexBacklog.create model_name = Model.__name__ vlist = [] for record in records: vlist.append({ 'record_model': model_name, 'record_id': record.id, }) index_backlog_create(vlist) @classmethod @ModelView.button def get_default_mapping(cls, document_types): """ Tries to get the default mapping from the model object """ for document_type in document_types: Model = Pool().get(document_type.model.model) if hasattr(Model, 'es_mapping'): cls.write( [document_type], {'mapping': json.dumps(Model.es_mapping(), indent=4)}) else: cls.raise_user_error("Model %s has no mapping specified" % Model.__name__) @classmethod @ModelView.button def update_mapping(cls, document_types): """ Update the mapping on the server side """ config = Pool().get('elasticsearch.configuration')(1) conn = config.get_es_connection() for document_type in document_types: conn.indices.put_mapping( config.make_type_name(document_type.model.model), # Type json.loads(document_type.mapping), # Mapping [config.index_name], # Index )
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 OpenStatementDone(ModelView): 'Open Statement' __name__ = 'open.statement.done' result = fields.Text('Result', readonly=True)
class ResultsReportVersionDetailSample(metaclass=PoolMeta): __name__ = 'lims.results_report.version.detail.sample' plant = fields.Function(fields.Many2One('lims.plant', 'Plant'), 'get_notebook_field') equipment = fields.Function(fields.Many2One('lims.equipment', 'Equipment'), 'get_notebook_field') equipment_template = fields.Function( fields.Many2One('lims.equipment.template', 'Equipment Template'), 'get_notebook_field') equipment_model = fields.Function(fields.Char('Equipment Model'), 'get_notebook_field') equipment_serial_number = fields.Function( fields.Char('Equipment Serial Number'), 'get_notebook_field') equipment_name = fields.Function(fields.Char('Equipment Name'), 'get_notebook_field') component = fields.Function(fields.Many2One('lims.component', 'Component'), 'get_notebook_field') comercial_product = fields.Function( fields.Many2One('lims.comercial.product', 'Comercial Product'), 'get_notebook_field') precedent1 = fields.Many2One( 'lims.notebook', 'Precedent 1', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent2 = fields.Many2One( 'lims.notebook', 'Precedent 2', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent3 = fields.Many2One( 'lims.notebook', 'Precedent 3', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent4 = fields.Many2One( 'lims.notebook', 'Precedent 4', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent5 = fields.Many2One( 'lims.notebook', 'Precedent 5', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent6 = fields.Many2One( 'lims.notebook', 'Precedent 6', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent7 = fields.Many2One( 'lims.notebook', 'Precedent 7', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent8 = fields.Many2One( 'lims.notebook', 'Precedent 8', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) free_precedents = fields.Boolean('Free precedents') precedent1_diagnosis = fields.Function( fields.Text('Diagnosis Precedent 1'), 'on_change_with_precedent1_diagnosis') precedent2_diagnosis = fields.Function( fields.Text('Diagnosis Precedent 2'), 'on_change_with_precedent2_diagnosis') precedent3_diagnosis = fields.Function( fields.Text('Diagnosis Precedent 3'), 'on_change_with_precedent3_diagnosis') precedent1_diagnosis_states = fields.Function( fields.Dict('lims.diagnosis.state', 'Diagnosis States Precedent 1'), 'get_precedent_diagnosis') precedent2_diagnosis_states = fields.Function( fields.Dict('lims.diagnosis.state', 'Diagnosis States Precedent 2'), 'get_precedent_diagnosis') precedent3_diagnosis_states = fields.Function( fields.Dict('lims.diagnosis.state', 'Diagnosis States Precedent 3'), 'get_precedent_diagnosis') @staticmethod def default_free_precedents(): return False @classmethod def view_attributes(cls): missing_diagnosis = True if 'diagnosis' not in cls._fields else False return super().view_attributes() + [ ('//group[@id="diagnosis"]', 'states', { 'invisible': missing_diagnosis, }), ] @fields.depends('precedent1') def on_change_with_precedent1_diagnosis(self, name=None): if self.precedent1: result = self.get_precedent_diagnosis((self, ), ('precedent1_diagnosis', )) return result['precedent1_diagnosis'][self.id] return None @fields.depends('precedent2') def on_change_with_precedent2_diagnosis(self, name=None): if self.precedent2: result = self.get_precedent_diagnosis((self, ), ('precedent2_diagnosis', )) return result['precedent2_diagnosis'][self.id] return None @fields.depends('precedent3') def on_change_with_precedent3_diagnosis(self, name=None): if self.precedent3: result = self.get_precedent_diagnosis((self, ), ('precedent3_diagnosis', )) return result['precedent3_diagnosis'][self.id] return None @classmethod def get_precedent_diagnosis(cls, samples, names): result = {} missing_diagnosis = True if 'diagnosis' not in cls._fields else False if missing_diagnosis: for name in names: result[name] = {} for s in samples: result[name][s.id] = None else: for name in names: result[name] = {} if 'precedent1' in name: for s in samples: result[name][s.id] = cls._get_precedent_diagnosis( s.precedent1, name) elif 'precedent2' in name: for s in samples: result[name][s.id] = cls._get_precedent_diagnosis( s.precedent2, name) else: # name == 'precedent3_diagnosis': for s in samples: result[name][s.id] = cls._get_precedent_diagnosis( s.precedent3, name) return result @classmethod def _get_precedent_diagnosis(cls, precedent, name): if not precedent: return None precedent_sample = cls.search([ ('notebook', '=', precedent), ]) if not precedent_sample: return None return (precedent_sample[0].diagnosis_states if 'states' in name else precedent_sample[0].diagnosis) @classmethod def _get_fields_from_sample(cls, sample, only_accepted=True): sample_default = super()._get_fields_from_sample(sample, only_accepted) sample_default['precedent1'] = (sample.precedent1 and sample.precedent1 or None) sample_default['precedent2'] = (sample.precedent2 and sample.precedent2 or None) sample_default['precedent3'] = (sample.precedent3 and sample.precedent3 or None) sample_default['precedent4'] = (sample.precedent4 and sample.precedent4 or None) sample_default['precedent5'] = (sample.precedent5 and sample.precedent5 or None) sample_default['precedent6'] = (sample.precedent6 and sample.precedent6 or None) sample_default['precedent7'] = (sample.precedent7 and sample.precedent7 or None) sample_default['precedent8'] = (sample.precedent8 and sample.precedent8 or None) return sample_default @classmethod def create(cls, vlist): samples = super().create(vlist) for sample in samples: if not sample.precedent1: precedents = cls.get_default_precedents(sample) if not precedents: continue for i in range(0, min(3, len(precedents))): setattr(sample, 'precedent%s' % str(i + 1), precedents[i]) sample.save() cls.update_precedent_lines(sample) return samples @classmethod def write(cls, *args): super().write(*args) update_precedent_lines = False if not update_precedent_lines: return actions = iter(args) for samples, vals in zip(actions, actions): change_precedents = False for field in [ 'precedent1', 'precedent2', 'precedent3', 'precedent4', 'precedent5', 'precedent6', 'precedent7', 'precedent8' ]: if field in vals: change_precedents = True if change_precedents: for sample in samples: cls.update_precedent_lines(sample) @staticmethod def get_default_precedents(sample): pool = Pool() Notebook = pool.get('lims.notebook') if not sample.component: return [] precedents = Notebook.search([ ('id', '!=', sample.notebook.id), ('component', '=', sample.component), ('fraction.sample.state', '!=', 'annulled'), ('invoice_party', '=', sample.notebook.invoice_party), ], order=[ ('fraction.sample.number', 'DESC'), ], limit=3) return precedents @classmethod def update_precedent_lines(cls, sample): pool = Pool() ResultsLine = pool.get('lims.results_report.version.detail.line') NotebookLine = pool.get('lims.notebook.line') precedent_lines = ResultsLine.search([ ('detail_sample', '=', sample.id), ('notebook_line', '=', None), ]) if precedent_lines: ResultsLine.delete(precedent_lines) result_lines = ResultsLine.search([ ('detail_sample', '=', sample.id), ]) analysis = [rl.notebook_line.analysis.id for rl in result_lines] lines_to_create = [] for precedent in [ sample.precedent1, sample.precedent2, sample.precedent3 ]: if not precedent: continue precedent_lines = NotebookLine.search([ ('notebook', '=', precedent), ('analysis', 'not in', analysis), ('accepted', '=', True), ]) for line in precedent_lines: lines_to_create.append({ 'detail_sample': sample.id, 'precedent_analysis': line.analysis.id, }) analysis.append(line.analysis.id) if lines_to_create: ResultsLine.create(lines_to_create)
class CloseStatementDone(ModelView): 'Close Statement' __name__ = 'close.statement.done' result = fields.Text('Result', readonly=True)
class ContactMechanism(DeactivableMixin, sequence_ordered(), ModelSQL, ModelView): "Contact Mechanism" __name__ = 'party.contact_mechanism' _rec_name = 'value' type = fields.Selection(_TYPES, 'Type', required=True, states=STATES, sort=False, depends=DEPENDS) value = fields.Char( 'Value', select=True, states=STATES, depends=DEPENDS # Add all function fields to ensure to always fill them via on_change + ['email', 'website', 'skype', 'sip', 'other_value', 'value_compact']) value_compact = fields.Char('Value Compact', readonly=True) name = fields.Char("Name", states=STATES, depends=DEPENDS) comment = fields.Text('Comment', states=STATES, depends=DEPENDS) party = fields.Many2One('party.party', 'Party', required=True, ondelete='CASCADE', states=STATES, select=True, depends=DEPENDS) email = fields.Function(fields.Char('E-Mail', states={ 'invisible': Eval('type') != 'email', 'required': Eval('type') == 'email', 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') website = fields.Function(fields.Char('Website', states={ 'invisible': Eval('type') != 'website', 'required': Eval('type') == 'website', 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') skype = fields.Function(fields.Char('Skype', states={ 'invisible': Eval('type') != 'skype', 'required': Eval('type') == 'skype', 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') sip = fields.Function(fields.Char('SIP', states={ 'invisible': Eval('type') != 'sip', 'required': Eval('type') == 'sip', 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') other_value = fields.Function( fields.Char('Value', states={ 'invisible': Eval('type').in_(['email', 'website', 'skype', 'sip']), 'required': ~Eval('type').in_(['email', 'website']), 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') url = fields.Function( fields.Char('URL', states={ 'invisible': ~Eval('url'), }), 'on_change_with_url') @classmethod def __setup__(cls): super(ContactMechanism, cls).__setup__() cls._order.insert(0, ('party', 'ASC')) @staticmethod def default_type(): return 'phone' @classmethod def get_value(cls, mechanisms, names): return dict((name, dict((m.id, m.value) for m in mechanisms)) for name in names) @fields.depends('type', 'value') def on_change_with_url(self, name=None, value=None): if value is None: value = self.value if self.type == 'email': return 'mailto:%s' % value elif self.type == 'website': return value elif self.type == 'skype': return 'callto:%s' % value elif self.type == 'sip': return 'sip:%s' % value elif self.type == 'phone': return 'tel:%s' % value elif self.type == 'fax': return 'fax:%s' % value return None @fields.depends('party', '_parent_party.addreses') def _phone_country_codes(self): if self.party: for address in self.party.addresses: if address.country: yield address.country.code @fields.depends(methods=['_phone_country_codes']) def _parse_phonenumber(self, value): for country_code in chain(self._phone_country_codes(), [None]): try: # Country code is ignored if value has an international prefix return phonenumbers.parse(value, country_code) except NumberParseException: pass return None @fields.depends(methods=['_parse_phonenumber']) def format_value(self, value=None, type_=None): if phonenumbers and type_ in _PHONE_TYPES: phonenumber = self._parse_phonenumber(value) if phonenumber: value = phonenumbers.format_number( phonenumber, PhoneNumberFormat.INTERNATIONAL) return value @fields.depends(methods=['_parse_phonenumber']) def format_value_compact(self, value=None, type_=None): if phonenumbers and type_ in _PHONE_TYPES: phonenumber = self._parse_phonenumber(value) if phonenumber: value = phonenumbers.format_number(phonenumber, PhoneNumberFormat.E164) return value @classmethod def set_value(cls, mechanisms, name, value): # Setting value is done by on_changes pass @fields.depends( methods=['on_change_with_url', 'format_value', 'format_value_compact']) def _change_value(self, value, type_): self.value = self.format_value(value=value, type_=type_) self.value_compact = self.format_value_compact(value=value, type_=type_) self.website = value self.email = value self.skype = value self.sip = value self.other_value = value self.url = self.on_change_with_url(value=value) @fields.depends('value', 'type', methods=['_change_value']) def on_change_value(self): return self._change_value(self.value, self.type) @fields.depends('website', 'type', methods=['_change_value']) def on_change_website(self): return self._change_value(self.website, self.type) @fields.depends('email', 'type', methods=['_change_value']) def on_change_email(self): return self._change_value(self.email, self.type) @fields.depends('skype', 'type', methods=['_change_value']) def on_change_skype(self): return self._change_value(self.skype, self.type) @fields.depends('sip', 'type', methods=['_change_value']) def on_change_sip(self): return self._change_value(self.sip, self.type) @fields.depends('other_value', 'type', methods=['_change_value']) def on_change_other_value(self): return self._change_value(self.other_value, self.type) @classmethod def search_rec_name(cls, name, clause): return [ 'OR', ('value', ) + tuple(clause[1:]), ('value_compact', ) + tuple(clause[1:]), ] @classmethod def _format_values(cls, mechanisms): for mechanism in mechanisms: value = mechanism.format_value(value=mechanism.value, type_=mechanism.type) if value != mechanism.value: mechanism.value = value value_compact = mechanism.format_value_compact( value=mechanism.value, type_=mechanism.type) if value_compact != mechanism.value_compact: mechanism.value_compact = value_compact cls.save(mechanisms) @classmethod def create(cls, vlist): mechanisms = super(ContactMechanism, cls).create(vlist) cls._format_values(mechanisms) return mechanisms @classmethod def write(cls, *args): actions = iter(args) all_mechanisms = [] for mechanisms, values in zip(actions, actions): all_mechanisms.extend(mechanisms) if 'party' in values: for mechanism in mechanisms: if mechanism.party.id != values['party']: raise AccessError( gettext('party' '.msg_contact_mechanism_change_party') % { 'contact': mechanism.rec_name, }) super(ContactMechanism, cls).write(*args) cls._format_values(all_mechanisms) @classmethod def validate(cls, mechanisms): super(ContactMechanism, cls).validate(mechanisms) for mechanism in mechanisms: mechanism.check_valid_phonenumber() def check_valid_phonenumber(self): if not phonenumbers or self.type not in _PHONE_TYPES: return phonenumber = self._parse_phonenumber(self.value) if not (phonenumber and phonenumbers.is_valid_number(phonenumber)): raise InvalidPhoneNumber( gettext('party.msg_invalid_phone_number', phone=self.value, party=self.party.rec_name)) @classmethod def usages(cls, _fields=None): "Returns the selection list of usage" usages = [(None, "")] if _fields: for name, desc in cls.fields_get(_fields).items(): usages.append((name, desc['string'])) return usages
class View(ModelSQL, ModelView): "View" __name__ = 'ir.ui.view' _rec_name = 'model' model = fields.Char('Model', select=True, states={ 'required': Eval('type').in_([None, 'tree', 'form', 'graph']), }) priority = fields.Integer('Priority', required=True, select=True) type = fields.Selection([ (None, ''), ('tree', 'Tree'), ('form', 'Form'), ('graph', 'Graph'), ('calendar', 'Calendar'), ('board', 'Board'), ], 'View Type', select=True, domain=[ If(Bool(Eval('inherit')), ('type', '=', None), ('type', '!=', None)), ], depends=['inherit']) data = fields.Text('Data') name = fields.Char('Name', states={ 'invisible': ~(Eval('module') & Eval('name')), }, depends=['module'], readonly=True) arch = fields.Function(fields.Text('View Architecture', states={ 'readonly': Bool(Eval('name')), }, depends=['name']), 'get_arch', setter='set_arch') inherit = fields.Many2One('ir.ui.view', 'Inherited View', select=True, ondelete='CASCADE') field_childs = fields.Char('Children Field', states={ 'invisible': Eval('type') != 'tree', }, depends=['type']) module = fields.Char('Module', states={ 'invisible': ~Eval('module'), }, readonly=True) domain = fields.Char('Domain', states={ 'invisible': ~Eval('inherit'), }, depends=['inherit']) _get_rng_cache = Cache('ir_ui_view.get_rng') @classmethod def __setup__(cls): super(View, cls).__setup__() cls._error_messages.update({ 'invalid_xml': 'Invalid XML for view "%s".', }) cls._order.insert(0, ('priority', 'ASC')) cls._buttons.update({ 'show': { 'readonly': Eval('type') != 'form', 'depends': ['type'], }, }) @staticmethod def default_priority(): return 16 @staticmethod def default_module(): return Transaction().context.get('module') or '' @classmethod @ModelView.button_action('ir.act_view_show') def show(cls, views): pass @classmethod def get_rng(cls, type_): key = (cls.__name__, type_) rng = cls._get_rng_cache.get(key) if rng is None: rng_name = os.path.join(os.path.dirname(__file__), type_ + '.rng') with open(rng_name, 'rb') as fp: rng = etree.fromstring(fp.read()) cls._get_rng_cache.set(key, rng) return rng @property def rng_type(self): if self.inherit: return self.inherit.rng_type return self.type @classmethod def validate(cls, views): super(View, cls).validate(views) cls.check_xml(views) @classmethod def check_xml(cls, views): "Check XML" for view in views: if not view.arch: continue xml = view.arch.strip() if not xml: continue tree = etree.fromstring(xml) if hasattr(etree, 'RelaxNG'): validator = etree.RelaxNG(etree=cls.get_rng(view.rng_type)) if not validator.validate(tree): error_log = '\n'.join(map(str, validator.error_log.filter_from_errors())) logger.error('Invalid XML view %s:\n%s\n%s', view.rec_name, error_log, xml) cls.raise_user_error( 'invalid_xml', (view.rec_name,), error_log) root_element = tree.getroottree().getroot() # validate pyson attributes validates = { 'states': fields.states_validate, } def encode(element): for attr in ('states', 'domain', 'spell'): if not element.get(attr): continue try: value = PYSONDecoder().decode(element.get(attr)) validates.get(attr, lambda a: True)(value) except Exception as e: error_log = '%s: <%s %s="%s"/>' % ( e, element.get('id') or element.get('name'), attr, element.get(attr)) logger.error( 'Invalid XML view %s:\n%s\n%s', view.rec_name, error_log, xml) cls.raise_user_error( 'invalid_xml', (view.rec_name,), error_log) for child in element: encode(child) encode(root_element) def get_arch(self, name): value = None if self.name and self.module: path = os.path.join(self.module, 'view', self.name + '.xml') try: with file_open(path, subdir='modules', mode='r', encoding='utf-8') as fp: value = fp.read() except IOError: pass if not value: value = self.data return value @classmethod def set_arch(cls, views, name, value): cls.write(views, {'data': value}) @classmethod def delete(cls, views): super(View, cls).delete(views) # Restart the cache ModelView._fields_view_get_cache.clear() @classmethod def create(cls, vlist): views = super(View, cls).create(vlist) # Restart the cache ModelView._fields_view_get_cache.clear() return views @classmethod def write(cls, views, values, *args): super(View, cls).write(views, values, *args) # Restart the cache ModelView._fields_view_get_cache.clear()
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', 'Healh 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', on_change_with=[ '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' ]) @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 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 ViewTreeState(ModelSQL, ModelView): 'View Tree State' __name__ = 'ir.ui.view_tree_state' _rec_name = 'model' model = fields.Char('Model', required=True) domain = fields.Char('Domain', required=True) user = fields.Many2One('res.user', 'User', required=True, ondelete='CASCADE') child_name = fields.Char('Child Name') nodes = fields.Text('Expanded Nodes') selected_nodes = fields.Text('Selected Nodes') @classmethod def __setup__(cls): super(ViewTreeState, cls).__setup__() cls.__rpc__.update({ 'set': RPC(readonly=False, check_access=False), 'get': RPC(check_access=False), }) @classmethod def __register__(cls, module_name): super(ViewTreeState, cls).__register__(module_name) table = cls.__table_handler__(module_name) table.index_action(['model', 'domain', 'user', 'child_name'], 'add') @staticmethod def default_nodes(): return '[]' @staticmethod def default_selected_nodes(): return '[]' @classmethod def set(cls, model, domain, child_name, nodes, selected_nodes): # Normalize the json domain domain = json.dumps(json.loads(domain), separators=(',', ':')) current_user = Transaction().user records = cls.search([ ('user', '=', current_user), ('model', '=', model), ('domain', '=', domain), ('child_name', '=', child_name), ]) cls.delete(records) cls.create([{ 'user': current_user, 'model': model, 'domain': domain, 'child_name': child_name, 'nodes': nodes, 'selected_nodes': selected_nodes, }]) @classmethod def get(cls, model, domain, child_name): # Normalize the json domain domain = json.dumps(json.loads(domain), separators=(',', ':')) current_user = Transaction().user try: expanded_info, = cls.search([ ('user', '=', current_user), ('model', '=', model), ('domain', '=', domain), ('child_name', '=', child_name), ], limit=1) except ValueError: return (cls.default_nodes(), cls.default_selected_nodes()) state = cls(expanded_info) return (state.nodes or cls.default_nodes(), state.selected_nodes or cls.default_selected_nodes())
class Payment: __metaclass__ = PoolMeta __name__ = 'account.payment' sepa_mandate = fields.Many2One('account.payment.sepa.mandate', 'Mandate', ondelete='RESTRICT', domain=[ ('party', '=', Eval('party', -1)), ('company', '=', Eval('company', -1)), ], depends=['party', 'company']) sepa_mandate_sequence_type = fields.Char('Mandate Sequence Type', readonly=True) sepa_return_reason_code = fields.Char('Return Reason Code', readonly=True, states={ 'invisible': (~Eval('sepa_return_reason_code') & (Eval('state') != 'failed')), }, depends=['state']) sepa_return_reason_information = fields.Text('Return Reason Information', readonly=True, states={ 'invisible': (~Eval('sepa_return_reason_information') & (Eval('state') != 'failed')), }, depends=['state']) sepa_end_to_end_id = fields.Function(fields.Char('SEPA End To End ID'), 'get_sepa_end_to_end_id', searcher='search_end_to_end_id') sepa_instruction_id = fields.Function(fields.Char('SEPA Instruction ID'), 'get_sepa_instruction_id', searcher='search_sepa_instruction_id') @classmethod def copy(cls, payments, default=None): if default is None: default = {} default.setdefault('sepa_mandate_sequence_type', None) return super(Payment, cls).copy(payments, default=default) @classmethod def get_sepa_mandates(cls, payments): mandates = [] for payment in payments: if payment.sepa_mandate: if payment.sepa_mandate.is_valid: mandate = payment.sepa_mandate else: mandate = None else: for mandate in payment.party.sepa_mandates: if mandate.is_valid: break else: mandate = None mandates.append(mandate) return mandates def get_sepa_end_to_end_id(self, name): return str(self.id) @classmethod def search_end_to_end_id(cls, name, domain): table = cls.__table__() _, operator, value = domain cast = cls.sepa_end_to_end_id._field.sql_type().base Operator = fields.SQL_OPERATORS[operator] query = table.select(table.id, where=Operator(table.id.cast(cast), value)) return [('id', 'in', query)] get_sepa_instruction_id = get_sepa_end_to_end_id search_sepa_instruction_id = search_end_to_end_id @property def sepa_remittance_information(self): if self.description: return self.description elif self.line and self.line.origin: return self.line.origin.rec_name @property def sepa_bank_account_number(self): if self.kind == 'receivable': if self.sepa_mandate: return self.sepa_mandate.account_number else: for account in self.party.bank_accounts: for number in account.numbers: if number.type == 'iban': return number @property def rejected(self): return (self.state == 'failed' and self.sepa_return_reason_code and self.sepa_return_reason_information == '/RTYP/RJCT') def create_clearing_move(self, date=None): if not date: date = Transaction().context.get('date_value') return super(Payment, self).create_clearing_move(date=date)
class DictSchemaMixin(object): _rec_name = 'string' name = fields.Char('Name', required=True) string = fields.Char('String', translate=True, required=True) type_ = fields.Selection([ ('boolean', 'Boolean'), ('integer', 'Integer'), ('char', 'Char'), ('float', 'Float'), ('numeric', 'Numeric'), ('date', 'Date'), ('datetime', 'DateTime'), ('selection', 'Selection'), ], 'Type', required=True) digits = fields.Integer('Digits', states={ 'invisible': ~Eval('type_').in_(['float', 'numeric']), }, depends=['type_']) selection = fields.Text( 'Selection', states={ 'invisible': Eval('type_') != 'selection', }, translate=True, depends=['type_'], help='A couple of key and label separated by ":" per line') selection_sorted = fields.Boolean( 'Selection Sorted', states={ 'invisible': Eval('type_') != 'selection', }, depends=['type_'], help='If the selection must be sorted on label') selection_json = fields.Function( fields.Char('Selection JSON', states={ 'invisible': Eval('type_') != 'selection', }, depends=['type_']), 'get_selection_json') @classmethod def __setup__(cls): super(DictSchemaMixin, cls).__setup__() cls.__rpc__.update({ 'get_keys': RPC(instantiate=0), }) @staticmethod def default_digits(): return 2 @staticmethod def default_selection_sorted(): return True def get_selection_json(self, name): db_selection = self.selection or '' selection = [[w.strip() for w in v.split(':', 1)] for v in db_selection.splitlines() if v] return json.dumps(selection, separators=(',', ':')) @classmethod def get_keys(cls, records): pool = Pool() Config = pool.get('ir.configuration') keys = [] for record in records: new_key = { 'id': record.id, 'name': record.name, 'string': record.string, 'type_': record.type_, } if record.type_ == 'selection': with Transaction().set_context(language=Config.get_language()): english_key = cls(record.id) selection = OrderedDict( json.loads(english_key.selection_json)) selection.update(dict(json.loads(record.selection_json))) new_key['selection'] = selection.items() new_key['sorted'] = record.selection_sorted elif record.type_ in ('float', 'numeric'): new_key['digits'] = (16, record.digits) keys.append(new_key) return keys
class ContactMechanism(sequence_ordered(), ModelSQL, ModelView): "Contact Mechanism" __name__ = 'party.contact_mechanism' _rec_name = 'value' type = fields.Selection(_TYPES, 'Type', required=True, states=STATES, sort=False, depends=DEPENDS) value = fields.Char( 'Value', select=True, states=STATES, depends=DEPENDS # Add all function fields to ensure to always fill them via on_change + ['email', 'website', 'skype', 'sip', 'other_value', 'value_compact']) value_compact = fields.Char('Value Compact', readonly=True) comment = fields.Text('Comment', states=STATES, depends=DEPENDS) party = fields.Many2One('party.party', 'Party', required=True, ondelete='CASCADE', states=STATES, select=True, depends=DEPENDS) active = fields.Boolean( 'Active', select=True, help="Uncheck to exclude the contact mechanism from future use.") email = fields.Function(fields.Char('E-Mail', states={ 'invisible': Eval('type') != 'email', 'required': Eval('type') == 'email', 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') website = fields.Function(fields.Char('Website', states={ 'invisible': Eval('type') != 'website', 'required': Eval('type') == 'website', 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') skype = fields.Function(fields.Char('Skype', states={ 'invisible': Eval('type') != 'skype', 'required': Eval('type') == 'skype', 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') sip = fields.Function(fields.Char('SIP', states={ 'invisible': Eval('type') != 'sip', 'required': Eval('type') == 'sip', 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') other_value = fields.Function( fields.Char('Value', states={ 'invisible': Eval('type').in_(['email', 'website', 'skype', 'sip']), 'required': ~Eval('type').in_(['email', 'website']), 'readonly': ~Eval('active', True), }, depends=['value', 'type', 'active']), 'get_value', setter='set_value') url = fields.Function( fields.Char( 'URL', states={ 'invisible': (~Eval('type').in_( ['email', 'website', 'skype', 'sip', 'fax', 'phone']) | ~Eval('url')), }, depends=['type']), 'get_url') @classmethod def __setup__(cls): super(ContactMechanism, cls).__setup__() cls._order.insert(0, ('party', 'ASC')) cls._error_messages.update({ 'write_party': ('You can not modify the party of contact ' 'mechanism "%s".'), 'invalid_phonenumber': ('The phone number "%(phone)s" of ' 'party "%(party)s" is not valid .'), }) @classmethod def __register__(cls, module_name): TableHandler = backend.get('TableHandler') table = TableHandler(cls, module_name) super(ContactMechanism, cls).__register__(module_name) # Migration from 2.4: drop required on sequence table.not_null_action('sequence', action='remove') @staticmethod def default_type(): return 'phone' @staticmethod def default_active(): return True @classmethod def get_value(cls, mechanisms, names): return dict((name, dict((m.id, m.value) for m in mechanisms)) for name in names) def get_url(self, name=None, value=None): if value is None: value = self.value if self.type == 'email': return 'mailto:%s' % value elif self.type == 'website': return value elif self.type == 'skype': return 'callto:%s' % value elif self.type == 'sip': return 'sip:%s' % value elif self.type == 'phone': return 'tel:%s' % value elif self.type == 'fax': return 'fax:%s' % value return None @classmethod def format_value(cls, value=None, type_=None): if phonenumbers and type_ in _PHONE_TYPES: try: phonenumber = phonenumbers.parse(value) except NumberParseException: pass else: value = phonenumbers.format_number( phonenumber, PhoneNumberFormat.INTERNATIONAL) return value @classmethod def format_value_compact(cls, value=None, type_=None): if phonenumbers and type_ in _PHONE_TYPES: try: phonenumber = phonenumbers.parse(value) except NumberParseException: pass else: value = phonenumbers.format_number(phonenumber, PhoneNumberFormat.E164) return value @classmethod def set_value(cls, mechanisms, name, value): # Setting value is done by on_changes pass def _change_value(self, value, type_): self.value = self.format_value(value=value, type_=type_) self.value_compact = self.format_value_compact(value=value, type_=type_) self.website = value self.email = value self.skype = value self.sip = value self.other_value = value self.url = self.get_url(value=value) @fields.depends('value', 'type') def on_change_type(self): self.url = self.get_url(value=self.value) @fields.depends('value', 'type') def on_change_value(self): return self._change_value(self.value, self.type) @fields.depends('website', 'type') def on_change_website(self): return self._change_value(self.website, self.type) @fields.depends('email', 'type') def on_change_email(self): return self._change_value(self.email, self.type) @fields.depends('skype', 'type') def on_change_skype(self): return self._change_value(self.skype, self.type) @fields.depends('sip', 'type') def on_change_sip(self): return self._change_value(self.sip, self.type) @fields.depends('other_value', 'type') def on_change_other_value(self): return self._change_value(self.other_value, self.type) @classmethod def search_rec_name(cls, name, clause): return [ 'OR', ('value', ) + tuple(clause[1:]), ('value_compact', ) + tuple(clause[1:]), ] @classmethod def _format_values(cls, mechanisms): for mechanism in mechanisms: value = mechanism.format_value(value=mechanism.value, type_=mechanism.type) if value != mechanism.value: mechanism.value = value value_compact = mechanism.format_value_compact( value=mechanism.value, type_=mechanism.type) if value_compact != mechanism.value_compact: mechanism.value_compact = value_compact cls.save(mechanisms) @classmethod def create(cls, vlist): mechanisms = super(ContactMechanism, cls).create(vlist) cls._format_values(mechanisms) return mechanisms @classmethod def write(cls, *args): actions = iter(args) all_mechanisms = [] for mechanisms, values in zip(actions, actions): all_mechanisms.extend(mechanisms) if 'party' in values: for mechanism in mechanisms: if mechanism.party.id != values['party']: cls.raise_user_error('write_party', (mechanism.rec_name, )) super(ContactMechanism, cls).write(*args) cls._format_values(all_mechanisms) @classmethod def validate(cls, mechanisms): super(ContactMechanism, cls).validate(mechanisms) for mechanism in mechanisms: mechanism.check_valid_phonenumber() def check_valid_phonenumber(self): if not phonenumbers or self.type not in _PHONE_TYPES: return try: phonenumber = phonenumbers.parse(self.value) except NumberParseException: phonenumber = None if not (phonenumber and phonenumbers.is_valid_number(phonenumber)): self.raise_user_error('invalid_phonenumber', { 'phone': self.value, 'party': self.party.rec_name })
class HrContract(ModelSQL, ModelView): """Employee Contract""" __name__ = 'hr.contract' salary_code = fields.Char('Salary Code', required=True) employee = fields.Many2One('company.employee', 'Employee', required=True) name = fields.Char('Salary detail Reference', required=True) center = fields.Many2One('gnuhealth.institution', 'Center') department = fields.Many2One('company.department', 'Department', required=True) designation = fields.Many2One('employee.designation', 'Designation', required=True) date_start = fields.Date('Start Date', required=True) date_end = fields.Date('End Date') notes = fields.Text('Notes') is_active = fields.Boolean('Active') basic = fields.Float('Basic Pay', required=True) approve_date = fields.Date('Date of Approval') approve_by = fields.Many2One('res.user', 'Approved By') @classmethod def validate(cls, records): super(HrContract, cls).validate(records) for record in records: record.valid_date() contracts = cls.search([('id', '!=', record.id), ('employee', '=', record.employee), ('date_start', '<=', record.date_start), ('date_end', '>=', record.date_start)]) if contracts: cls.raise_user_error('Contract for this user already\ exists for the given period') def valid_date(self): if self.date_end and self.date_end < self.date_start: self.raise_user_error('Not a valid date') @fields.depends('date_end') def on_change_with_is_active(self): present_date = datetime.date.today() if self.date_end and (present_date > self.date_end): return False return True @classmethod def set_approvedby(cls, hrcontract): ''' Fill the approved by and approve date field ''' approve_date = datetime.datetime.now().date() pool = Pool() User = pool.get('res.user') user = User(Transaction().user) for contract in hrcontract: contract.approve_date = approve_date contract.approve_by = user.id cls.save(hrcontract) @fields.depends('employee') def on_change_employee(self): if self.employee: self.salary_code = self.employee.salary_code self.designation = self.employee.designation self.department = self.employee.department self.center = self.employee.center @staticmethod def default_employee(): pool = Pool() User = pool.get('res.user') user = User(Transaction().user) employee = user.employee return employee.id if employee else None @classmethod def default_date_start(cls): start_date = datetime.date.today().replace(day=1) return start_date
class Asset(ModelSQL, ModelView): """ Fleet Management Asset """ _name = "fleet.asset" _rec_name = 'code' _description = "Fleet Management Asset" code = fields.Char('Code', required=True, select=1) meter_unit = fields.Many2One("product.uom", "Meter Unit", required=True, domain=[('category.name', '=', 'Length')]) status = fields.Selection([ ('Active', 'Active'), ('Out of Service', 'Out of Service'), ], "Status", readonly=True, select=1) average_fuel_efficiency = fields.Function( fields.Numeric("Average Fuel Efficiency", loading="lazy"), 'get_avg_efficiency') # Specifications year = fields.Integer("Year") make = fields.Char("Make") model = fields.Char("Model", select=1) serial_number = fields.Char("Serial Number") license_plate = fields.Char("Plate Number", select=1) # Purchase vendor = fields.Many2One("party.party", "Vendor") purchase_date = fields.Date("Purchase Date") purchase_meter = fields.BigInteger("Purchase Meter") warranty_expiration_date = fields.Date("Warranty Expiration Date") warranty_expiration_meter = fields.BigInteger("Warranty Expiration Meter") # Status in_service_date = fields.Date("In Service Date") out_of_service_date = fields.Date("Out of Service Date") # Comments comment = fields.Text('Comment') def default_year(self): """Get the current year """ date_obj = Pool().get('ir.date') return date_obj.today().year def get_avg_efficiency(self, ids, name): """ Get average efficiency of the fuel """ purchase_line_obj = Pool().get('purchase.line') res = {} for asset in self.browse(ids): sum_quantity = 0 purchase_line_ids = purchase_line_obj.search( [('asset', '=', asset.id)], order=[('id', 'DESC')], limit=100) for purchase_line in purchase_line_obj.browse(purchase_line_ids): sum_quantity += Decimal(str(purchase_line.quantity)) if purchase_line_ids: # get the last purchase line for particular asset. last_line = purchase_line_obj.browse(purchase_line_ids[-1]) # get the first purchase line from last 100 records # for particular asset. first_line = purchase_line_obj.browse(purchase_line_ids[0]) if len(purchase_line_ids) == 1: avg_efficiency = (first_line.meter_reading - \ 0)/Decimal(str(purchase_line.quantity)) else: avg_efficiency = (first_line.meter_reading - \ last_line.meter_reading) / (sum_quantity) else: avg_efficiency = 0 res[asset.id] = avg_efficiency return res
class Ambulance (ModelSQL, ModelView): 'Ambulance' __name__ = 'gnuhealth.ambulance' vehicle_identifier = fields.Char('ID', required=True, help="Ambulance license number or other type of ID") vehicle_brand = fields.Char('Brand', help="Ambulance maker") vehicle_model = fields.Char('Model', help="Ambulance model") vehicle_odometer = fields.Integer('Odometer', help="Current odometer reading") vehicle_type = fields.Selection([ (None, ''), ('van', 'Van'), ('car', 'Car'), ('boat', 'Boat'), ('helicopter', 'Helicopter'), ('airplane', 'Airplane'), ('motorcycle', 'Motorcycle'), ('bicycle', 'Bicycle'), ('drone', 'Drone'), ], 'Type', required=True, help="Type of vehicle",sort=False) vehicle_function = fields.Selection([ (None, ''), ('patient_transport', 'Type A - Patient Transport'), ('emergency', 'Type B - Emergency'), ('icu', 'Type C - Mobile ICU'), ], 'Function',sort=False, required=True, help="Vehicle main functionality") vehicle_station = fields.Many2One( 'gnuhealth.institution', 'Station', help="Station / Base of the vehicle") state = fields.Selection([ (None, ''), ('available', 'Available / At Station'), ('dispatched', 'Dispatched'), ('en_route', 'En Route'), ('on_location', 'On Location'), ('to_hospital', 'To Hospital'), ('at_hospital', 'At Hospital'), ('returning', 'Returning'), ('out_of_service', 'Out of service'), ], 'Status',sort=False, readonly=True, help="Vehicle status") vehicle_remarks = fields.Text('Remarks') active = fields.Boolean('Active', select=True) @staticmethod def default_active(): return True @staticmethod def default_state(): return 'available' @classmethod def __setup__(cls): super(Ambulance, cls).__setup__() t = cls.__table__() cls._sql_constraints = [ ('vehicle_uniq', Unique(t,t.vehicle_identifier), 'This vehicle ID already exists'), ] def get_rec_name(self, name): return (self.vehicle_identifier + ' : ' + self.vehicle_type)
class AddressFormat(DeactivableMixin, MatchMixin, ModelSQL, ModelView): "Address Format" __name__ = 'party.address.format' country_code = fields.Char("Country Code", size=2) language_code = fields.Char("Language Code", size=2) format_ = fields.Text("Format", required=True, help="Available variables (also in upper case):\n" "- ${party_name}\n" "- ${name}\n" "- ${attn}\n" "- ${street}\n" "- ${postal_code}\n" "- ${city}\n" "- ${subdivision}\n" "- ${subdivision_code}\n" "- ${country}\n" "- ${country_code}") _get_format_cache = Cache('party.address.format.get_format') @classmethod def __setup__(cls): super(AddressFormat, cls).__setup__() cls._order.insert(0, ('country_code', 'ASC NULLS LAST')) cls._order.insert(1, ('language_code', 'ASC NULLS LAST')) @classmethod def __register__(cls, module_name): pool = Pool() Country = pool.get('country.country') Language = pool.get('ir.lang') country = Country.__table__() language = Language.__table__() table = cls.__table__() cursor = Transaction().connection.cursor() super().__register__(module_name) table_h = cls.__table_handler__() # Migration from 5.2: replace country by country_code if table_h.column_exist('country'): query = table.update([table.country_code], country.select( country.code, where=country.id == table.country)) cursor.execute(*query) table_h.drop_column('country') # Migration from 5.2: replace language by language_code if table_h.column_exist('language'): query = table.update([table.language_code], language.select( Substring(language.code, 0, 2), where=language.id == table.language)) cursor.execute(*query) table_h.drop_column('language') @classmethod def default_format_(cls): return """${party_name} ${name} ${street} ${postal_code} ${city} ${subdivision} ${COUNTRY}""" @classmethod def create(cls, *args, **kwargs): records = super(AddressFormat, cls).create(*args, **kwargs) cls._get_format_cache.clear() return records @classmethod def write(cls, *args, **kwargs): super(AddressFormat, cls).write(*args, **kwargs) cls._get_format_cache.clear() @classmethod def delete(cls, *args, **kwargs): super(AddressFormat, cls).delete(*args, **kwargs) cls._get_format_cache.clear() @classmethod def validate(cls, formats): super(AddressFormat, cls).validate(formats) for format_ in formats: format_.check_format() def check_format(self): pool = Pool() Address = pool.get('party.address') address = Address() try: Template(self.format_).substitute( **address._get_address_substitutions()) except Exception as exception: raise InvalidFormat( gettext('party.invalid_format', format=self.format_, exception=exception)) from exception @classmethod def get_format(cls, address, pattern=None): if pattern is None: pattern = {} else: pattern = pattern.copy() pattern.setdefault('country_code', address.country.code if address.country else None) pattern.setdefault('language_code', Transaction().language[:2]) key = tuple(sorted(pattern.items())) format_ = cls._get_format_cache.get(key) if format_ is not None: return format_ for record in cls.search([]): if record.match(pattern): format_ = record.format_ break else: format_ = cls.default_format_() cls._get_format_cache.set(key, format_) return format_
class ResultsReportVersionDetail(metaclass=PoolMeta): __name__ = 'lims.results_report.version.detail' template = fields.Many2One('lims.report.template', 'Report Template', domain=[ ('report_name', '=', 'lims.result_report'), ('type', 'in', [None, 'base']), ], states={'readonly': Eval('state') != 'draft'}, depends=['state']) template_type = fields.Function( fields.Selection([ (None, ''), ('base', 'HTML'), ('header', 'HTML - Header'), ('footer', 'HTML - Footer'), ], 'Report Template Type'), 'get_template_type') sections = fields.One2Many('lims.results_report.version.detail.section', 'version_detail', 'Sections') previous_sections = fields.Function(fields.One2Many( 'lims.results_report.version.detail.section', 'version_detail', 'Previous Sections', domain=[('position', '=', 'previous')]), 'get_previous_sections', setter='set_previous_sections') following_sections = fields.Function(fields.One2Many( 'lims.results_report.version.detail.section', 'version_detail', 'Following Sections', domain=[('position', '=', 'following')]), 'get_following_sections', setter='set_following_sections') trend_charts = fields.One2Many( 'lims.results_report.version.detail.trend.chart', 'version_detail', 'Trend Charts') charts_x_row = fields.Selection([ ('1', '1'), ('2', '2'), ], 'Charts per Row') comments_plain = fields.Function(fields.Text('Comments', translate=True), 'get_comments_plain', setter='set_comments_plain') @classmethod def __setup__(cls): super().__setup__() if 'invisible' in cls.resultrange_origin.states: del cls.resultrange_origin.states['invisible'] if 'required' in cls.resultrange_origin.states: del cls.resultrange_origin.states['required'] @classmethod def view_attributes(cls): return super().view_attributes() + [ ('//page[@id="comments"]', 'states', { 'invisible': Not(Bool(Eval('template_type'))), }), ('//page[@id="comments_plain"]', 'states', { 'invisible': Eval('template_type') == 'base', }), ] @staticmethod def default_charts_x_row(): return '1' def get_template_type(self, name): return self.template and self.template.type or None @fields.depends('template', '_parent_template.trend_charts', '_parent_template.sections', 'sections', 'resultrange_origin') def on_change_template(self): if (self.template and self.template.resultrange_origin and not self.resultrange_origin): self.resultrange_origin = self.template.resultrange_origin.id if self.template and self.template.trend_charts: self.trend_charts = [{ 'chart': c.chart.id, 'order': c.order, } for c in self.template.trend_charts] self.charts_x_row = self.template.charts_x_row if self.template and self.template.sections: sections = {} for s in self.sections + self.template.sections: sections[s.name] = { 'name': s.name, 'data': s.data, 'data_id': s.data_id, 'position': s.position, 'order': s.order, } self.sections = sections.values() def get_previous_sections(self, name): return [s.id for s in self.sections if s.position == 'previous'] @classmethod def set_previous_sections(cls, sections, name, value): if not value: return cls.write(sections, {'sections': value}) def get_following_sections(self, name): return [s.id for s in self.sections if s.position == 'following'] @classmethod def set_following_sections(cls, sections, name, value): if not value: return cls.write(sections, {'sections': value}) @classmethod def _get_fields_from_samples(cls, samples, generate_report_form=None): pool = Pool() Notebook = pool.get('lims.notebook') detail_default = super()._get_fields_from_samples( samples, generate_report_form) result_template = None if generate_report_form and generate_report_form.template: result_template = generate_report_form.template resultrange_origin = None for sample in samples: nb = Notebook(sample['notebook']) if not result_template: result_template = cls._get_result_template_from_sample(nb) if not resultrange_origin: resultrange_origin = cls._get_resultrange_from_sample(nb) if result_template: detail_default['template'] = result_template.id if not resultrange_origin: resultrange_origin = result_template.resultrange_origin if result_template.trend_charts: detail_default['trend_charts'] = [('create', [{ 'chart': c.chart.id, 'order': c.order, } for c in result_template.trend_charts])] detail_default['charts_x_row'] = (result_template.charts_x_row) if result_template.sections: detail_default['sections'] = [('create', [{ 'name': s.name, 'data': s.data, 'data_id': s.data_id, 'position': s.position, 'order': s.order, } for s in result_template.sections])] if resultrange_origin: detail_default['resultrange_origin'] = resultrange_origin.id return detail_default @classmethod def _get_result_template_from_sample(cls, notebook): pool = Pool() Service = pool.get('lims.service') Laboratory = pool.get('lims.laboratory') Configuration = pool.get('lims.configuration') result_template = notebook.fraction.sample.result_template if not result_template: ok = True services = Service.search([ ('fraction', '=', notebook.fraction), ('analysis.type', '=', 'group'), ('annulled', '=', False), ]) for service in services: if service.analysis.result_template: if not result_template: result_template = service.analysis.result_template elif result_template != service.analysis.result_template: ok = False elif result_template: ok = False if not ok: result_template = None if not result_template: laboratory_id = Transaction().context.get( 'samples_pending_reporting_laboratory', None) if laboratory_id: laboratory = Laboratory(laboratory_id) result_template = laboratory.result_template if not result_template: config_ = Configuration(1) result_template = config_.result_template return result_template @classmethod def _get_resultrange_from_sample(cls, notebook): return notebook.fraction.sample.resultrange_origin @classmethod def _get_fields_not_overwrite(cls): fields = super()._get_fields_not_overwrite() fields.extend([ 'template', 'trend_charts', 'charts_x_row', 'sections', 'resultrange_origin' ]) return fields @classmethod def _get_fields_from_detail(cls, detail): detail_default = super()._get_fields_from_detail(detail) if detail.template: detail_default['template'] = detail.template.id if detail.trend_charts: detail_default['trend_charts'] = [('create', [{ 'chart': c.chart.id, 'order': c.order, } for c in detail.trend_charts])] detail_default['charts_x_row'] = detail.charts_x_row if detail.sections: detail_default['sections'] = [('create', [{ 'name': s.name, 'data': s.data, 'data_id': s.data_id, 'position': s.position, 'order': s.order, } for s in detail.sections])] return detail_default def get_comments_plain(self, name): return self.comments @classmethod def set_comments_plain(cls, records, name, value): cls.write(records, {'comments': value})