*: base, account, crm, hr, hr_attendance, test_access_rights The various public methods of the ORM can be override in other models, those overrides sometime don't implement the exact same signature as the original method in the ORM. In this work we sanitize all the overrides to ensure a better compatibility. The background objective is to make it possible to call any public method using kwarg: `search(domain=[...])`. * `search`, the first parameter was renamed from `args` to `domain` in0e9adf7but the overrides were not updated. * `invalidate_models` and `invalidate_recordset`, a new `flush=True` parameter was introduced in9c3b9a4but the overrides were not updated. * `update`, there is a clash between the `update` method responsible for writing on a record and `update` in bus responsible to update the user presence. The bus method has been renamed so it doesn't clash with the ORM. This sanitization comes with a new linter that verifies that all overrides of BaseModel public methods share a compatible signature. The linter has been disabled for `create`, `write` and `default_get` as too many overrides don't respect the signature of BaseModel. closes odoo/odoo#106999 Related: odoo/enterprise#34991 Signed-off-by: Julien Castiaux <juc@odoo.com>
73 lines
3.3 KiB
Python
73 lines
3.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
import datetime
|
|
import time
|
|
|
|
from psycopg2 import OperationalError
|
|
|
|
from odoo import api, fields, models
|
|
from odoo import tools
|
|
from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY
|
|
from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
|
|
|
|
UPDATE_PRESENCE_DELAY = 60
|
|
DISCONNECTION_TIMER = UPDATE_PRESENCE_DELAY + 5
|
|
AWAY_TIMER = 1800 # 30 minutes
|
|
|
|
|
|
class BusPresence(models.Model):
|
|
""" User Presence
|
|
Its status is 'online', 'away' or 'offline'. This model should be a one2one, but is not
|
|
attached to res_users to avoid database concurrence errors. Since the 'update_presence' method is executed
|
|
at each poll, if the user have multiple opened tabs, concurrence errors can happend, but are 'muted-logged'.
|
|
"""
|
|
|
|
_name = 'bus.presence'
|
|
_description = 'User Presence'
|
|
_log_access = False
|
|
|
|
user_id = fields.Many2one('res.users', 'Users', ondelete='cascade')
|
|
last_poll = fields.Datetime('Last Poll', default=lambda self: fields.Datetime.now())
|
|
last_presence = fields.Datetime('Last Presence', default=lambda self: fields.Datetime.now())
|
|
status = fields.Selection([('online', 'Online'), ('away', 'Away'), ('offline', 'Offline')], 'IM Status', default='offline')
|
|
|
|
def init(self):
|
|
self.env.cr.execute("CREATE UNIQUE INDEX IF NOT EXISTS bus_presence_user_unique ON %s (user_id) WHERE user_id IS NOT NULL" % self._table)
|
|
|
|
@api.model
|
|
def update_presence(self, inactivity_period, identity_field, identity_value):
|
|
""" Updates the last_poll and last_presence of the current user
|
|
:param inactivity_period: duration in milliseconds
|
|
"""
|
|
# This method is called in method _poll() and cursor is closed right
|
|
# after; see bus/controllers/main.py.
|
|
try:
|
|
# Hide transaction serialization errors, which can be ignored, the presence update is not essential
|
|
# The errors are supposed from presence.write(...) call only
|
|
with tools.mute_logger('odoo.sql_db'):
|
|
self._update_presence(inactivity_period=inactivity_period, identity_field=identity_field, identity_value=identity_value)
|
|
# commit on success
|
|
self.env.cr.commit()
|
|
except OperationalError as e:
|
|
if e.pgcode in PG_CONCURRENCY_ERRORS_TO_RETRY:
|
|
# ignore concurrency error
|
|
return self.env.cr.rollback()
|
|
raise
|
|
|
|
@api.model
|
|
def _update_presence(self, inactivity_period, identity_field, identity_value):
|
|
presence = self.search([(identity_field, '=', identity_value)], limit=1)
|
|
# compute last_presence timestamp
|
|
last_presence = datetime.datetime.now() - datetime.timedelta(milliseconds=inactivity_period)
|
|
values = {
|
|
'last_poll': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
|
}
|
|
# update the presence or a create a new one
|
|
if not presence: # create a new presence for the user
|
|
values[identity_field] = identity_value
|
|
values['last_presence'] = last_presence
|
|
self.create(values)
|
|
else: # update the last_presence if necessary, and write values
|
|
if presence.last_presence < last_presence:
|
|
values['last_presence'] = last_presence
|
|
presence.write(values)
|