def _gc_file_store(self): """ Perform the garbage collection of the filestore. """ if self._storage() != 'file': return # Continue in a new transaction. The LOCK statement below must be the # first one in the current transaction, otherwise the database snapshot # used by it may not contain the most recent changes made to the table # ir_attachment! Indeed, if concurrent transactions create attachments, # the LOCK statement will wait until those concurrent transactions end. # But this transaction will not see the new attachements if it has done # other requests before the LOCK (like the method _storage() above). cr = self._cr cr.commit() # prevent all concurrent updates on ir_attachment while collecting, # but only attempt to grab the lock for a little bit, otherwise it'd # start blocking other transactions. (will be retried later anyway) cr.execute("SET LOCAL lock_timeout TO '10s'") cr.execute("LOCK ir_attachment IN SHARE MODE") # retrieve the file names from the checklist checklist = {} for dirpath, _, filenames in os.walk(self._full_path('checklist')): dirname = os.path.basename(dirpath) for filename in filenames: fname = "%s/%s" % (dirname, filename) checklist[fname] = os.path.join(dirpath, filename) # Clean up the checklist. The checklist is split in chunks and files are garbage-collected # for each chunk. removed = 0 for names in cr.split_for_in_conditions(checklist): # determine which files to keep among the checklist cr.execute( "SELECT store_fname FROM ir_attachment WHERE store_fname IN %s", [names]) whitelist = set(row[0] for row in cr.fetchall()) # remove garbage files, and clean up checklist for fname in names: filepath = checklist[fname] if fname not in whitelist: try: os.unlink(self._full_path(fname)) _logger.debug("_file_gc unlinked %s", self._full_path(fname)) removed += 1 except (OSError, IOError): _logger.info("_file_gc could not unlink %s", self._full_path(fname), exc_info=True) with tools.ignore(OSError): os.unlink(filepath) # commit to release the lock cr.commit() _logger.info("filestore gc %d checked, %d removed", len(checklist), removed)
def _value_unpickle(self): for record in self: value = record.value if record.key == 'default' and value: # default values are pickled on the fly with tools.ignore(Exception): value = str(pickle.loads(value)) record.value_unpickle = value
def transitive_dependencies(field, seen=[]): if field in seen: return for seq1 in dependencies[field]: yield seq1 exceptions = (Exception,) if field.base_field.manual else () with ignore(*exceptions): for seq2 in transitive_dependencies(seq1[-1], seen + [field]): yield concat(seq1[:-1], seq2)
def _mark_for_gc(self, fname): """ Add ``fname`` in a checklist for the filestore garbage collection. """ # we use a spooldir: add an empty file in the subdirectory 'checklist' full_path = os.path.join(self._full_path('checklist'), fname) if not os.path.exists(full_path): dirname = os.path.dirname(full_path) if not os.path.isdir(dirname): with tools.ignore(OSError): os.makedirs(dirname) open(full_path, 'ab').close()
def get_needaction_data(self): """ Return for each menu entry in ``self``: - whether it uses the needaction mechanism (needaction_enabled) - the needaction counter of the related action, taking into account the action domain """ menu_ids = set() for menu in self: menu_ids.add(menu.id) ctx = {} if menu.action and menu.action.type in ( 'ir.actions.act_window', 'ir.actions.client') and menu.action.context: with tools.ignore(Exception): # use magical UnquoteEvalContext to ignore undefined client-side variables such as `active_id` eval_ctx = tools.UnquoteEvalContext(self._context) ctx = eval(menu.action.context, locals_dict=eval_ctx, nocopy=True) or {} menu_refs = ctx.get('needaction_menu_ref') if menu_refs: if not isinstance(menu_refs, list): menu_refs = [menu_refs] for menu_ref in menu_refs: record = self.env.ref(menu_ref, False) if record and record._name == 'ir.ui.menu': menu_ids.add(record.id) res = {} for menu in self.browse(menu_ids): res[menu.id] = { 'needaction_enabled': False, 'needaction_counter': False, } if menu.action and menu.action.type in ( 'ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: if menu.action.res_model in self.env: model = self.env[menu.action.res_model] if model._needaction: if menu.action.type == 'ir.actions.act_window': eval_context = self.env[ 'ir.actions.act_window']._get_eval_context() dom = eval(menu.action.domain or '[]', eval_context) else: dom = eval(menu.action.params_store or '{}', { 'uid': self._uid }).get('domain') res[menu.id]['needaction_enabled'] = model._needaction res[menu.id][ 'needaction_counter'] = model._needaction_count( dom) return res
def _validate_numerical_box(self, answer): try: floatanswer = float(answer) except ValueError: return {self.id: _('This is not a number')} if self.validation_required: # Answer is not in the right range with tools.ignore(Exception): if not (self.validation_min_float_value <= floatanswer <= self.validation_max_float_value): return {self.id: self.validation_error_msg} return {}
def _file_gc(self): """ Perform the garbage collection of the filestore. """ if self._storage() != 'file': return # Continue in a new transaction. The LOCK statement below must be the # first one in the current transaction, otherwise the database snapshot # used by it may not contain the most recent changes made to the table # ir_attachment! Indeed, if concurrent transactions create attachments, # the LOCK statement will wait until those concurrent transactions end. # But this transaction will not see the new attachements if it has done # other requests before the LOCK (like the method _storage() above). cr = self._cr cr.commit() # prevent all concurrent updates on ir_attachment while collecting! cr.execute("LOCK ir_attachment IN SHARE MODE") # retrieve the file names from the checklist checklist = {} for dirpath, _, filenames in os.walk(self._full_path('checklist')): dirname = os.path.basename(dirpath) for filename in filenames: fname = "%s/%s" % (dirname, filename) checklist[fname] = os.path.join(dirpath, filename) # determine which files to keep among the checklist whitelist = set() for names in cr.split_for_in_conditions(checklist): cr.execute("SELECT store_fname FROM ir_attachment WHERE store_fname IN %s", [names]) whitelist.update(row[0] for row in cr.fetchall()) # remove garbage files, and clean up checklist removed = 0 for fname, filepath in checklist.items(): if fname not in whitelist: try: os.unlink(self._full_path(fname)) removed += 1 except (OSError, IOError): _logger.info("_file_gc could not unlink %s", self._full_path(fname), exc_info=True) with tools.ignore(OSError): os.unlink(filepath) # commit to release the lock cr.commit() _logger.info("filestore gc %d checked, %d removed", len(checklist), removed)
def get_needaction_data(self): """ Return for each menu entry in ``self``: - whether it uses the needaction mechanism (needaction_enabled) - the needaction counter of the related action, taking into account the action domain """ menu_ids = set() for menu in self: menu_ids.add(menu.id) ctx = {} if ( menu.action and menu.action.type in ("ir.actions.act_window", "ir.actions.client") and menu.action.context ): with tools.ignore(Exception): # use magical UnquoteEvalContext to ignore undefined client-side variables such as `active_id` eval_ctx = tools.UnquoteEvalContext(self._context) ctx = safe_eval(menu.action.context, locals_dict=eval_ctx, nocopy=True) or {} menu_refs = ctx.get("needaction_menu_ref") if menu_refs: if not isinstance(menu_refs, list): menu_refs = [menu_refs] for menu_ref in menu_refs: record = self.env.ref(menu_ref, False) if record and record._name == "ir.ui.menu": menu_ids.add(record.id) res = {} for menu in self.browse(menu_ids): res[menu.id] = {"needaction_enabled": False, "needaction_counter": False} if ( menu.action and menu.action.type in ("ir.actions.act_window", "ir.actions.client") and menu.action.res_model ): if menu.action.res_model in self.env: model = self.env[menu.action.res_model] if model._needaction: if menu.action.type == "ir.actions.act_window": eval_context = self.env["ir.actions.act_window"]._get_eval_context() dom = safe_eval(menu.action.domain or "[]", eval_context) else: dom = safe_eval(menu.action.params_store or "{}", {"uid": self._uid}).get("domain") res[menu.id]["needaction_enabled"] = model._needaction res[menu.id]["needaction_counter"] = model._needaction_count(dom) return res
def clean_store(dbname, env): tables = defaultdict(set) for model_name in env.registry.models: model = env[model_name] if not model._abstract: for name, field in model._fields.items(): if field.type == 'file': tables[model._table].add(name) checklist = set() filestore = get_store_path(dbname) path = os.path.join(filestore, 'checklist') for root, dirs, files in os.walk(path): for file in files: checkpath = os.path.join(root, file) relpath = os.path.relpath(checkpath, path) checklist.add(os.path.join(filestore, relpath)) env.cr.commit() whitelist = set() for table, fields in tables.items(): select_fields = list(fields) env.cr.execute("LOCK %s IN SHARE MODE" % table) select_query = "SELECT {0}".format(', '.join(select_fields)) where_query = "WHERE {0} IN %(paths)s".format(select_fields[0]) if len(select_fields) > 1: for field in select_fields[:1]: where_query += "OR {0} IN %s".format(field) sql_query = "{0} FROM {1} {2};".format(select_query, table, where_query) for paths in env.cr.split_for_in_conditions(checklist): env.cr.execute(sql_query, {'paths': paths}) for row in env.cr.fetchall(): for column in row: whitelist.add(column) remove = checklist - whitelist for file in remove: try: os.unlink(file) except (OSError, IOError): _logger.warn("Deleting file from %s failed!", file, exc_info=True) with tools.ignore(OSError): shutil.rmtree(path) env.cr.commit() _logger.info("Cleaned files [ %d checked | %d removed ]", len(checklist), len(remove))
def field_triggers(self): # determine field dependencies dependencies = {} for Model in self.models.values(): if Model._abstract: continue for field in Model._fields.values(): # dependencies of custom fields may not exist; ignore that case exceptions = (Exception, ) if field.base_field.manual else () with ignore(*exceptions): dependencies[field] = set(field.resolve_depends(self)) # determine transitive dependencies def transitive_dependencies(field, seen=[]): if field in seen: return for seq1 in dependencies[field]: yield seq1 exceptions = (Exception, ) if field.base_field.manual else () with ignore(*exceptions): for seq2 in transitive_dependencies( seq1[-1], seen + [field]): yield concat(seq1[:-1], seq2) def concat(seq1, seq2): if seq1 and seq2: f1, f2 = seq1[-1], seq2[0] if f1.type == 'one2many' and f2.type == 'many2one' and \ f1.model_name == f2.comodel_name and f1.inverse_name == f2.name: return concat(seq1[:-1], seq2[1:]) return seq1 + seq2 # determine triggers based on transitive dependencies triggers = {} for field in dependencies: for path in transitive_dependencies(field): if path: tree = triggers for label in reversed(path): tree = tree.setdefault(label, {}) tree.setdefault(None, set()).add(field) return triggers
def get_needaction_data(self): """ Return for each menu entry in ``self``: - whether it uses the needaction mechanism (needaction_enabled) - the needaction counter of the related action, taking into account the action domain """ menu_ids = set() for menu in self: menu_ids.add(menu.id) ctx = {} if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.context: with tools.ignore(Exception): # use magical UnquoteEvalContext to ignore undefined client-side variables such as `active_id` eval_ctx = tools.UnquoteEvalContext(self._context) ctx = eval(menu.action.context, locals_dict=eval_ctx, nocopy=True) or {} menu_refs = ctx.get('needaction_menu_ref') if menu_refs: if not isinstance(menu_refs, list): menu_refs = [menu_refs] for menu_ref in menu_refs: record = self.env.ref(menu_ref, False) if record and record._name == 'ir.ui.menu': menu_ids.add(record.id) res = {} for menu in self.browse(menu_ids): res[menu.id] = { 'needaction_enabled': False, 'needaction_counter': False, } if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: if menu.action.res_model in self.env: model = self.env[menu.action.res_model] if model._needaction: if menu.action.type == 'ir.actions.act_window': eval_context = self.env['ir.actions.act_window']._get_eval_context() dom = eval(menu.action.domain or '[]', eval_context) else: dom = eval(menu.action.params_store or '{}', {'uid': self._uid}).get('domain') res[menu.id]['needaction_enabled'] = model._needaction res[menu.id]['needaction_counter'] = model._needaction_count(dom) return res
def _file_gc(self): """ Perform the garbage collection of the filestore. """ if self._storage() != 'file': return # prevent all concurrent updates on ir_attachment while collecting! cr = self._cr cr.execute("LOCK ir_attachment IN SHARE MODE") # retrieve the file names from the checklist checklist = {} for dirpath, _, filenames in os.walk(self._full_path('checklist')): dirname = os.path.basename(dirpath) for filename in filenames: fname = "%s/%s" % (dirname, filename) checklist[fname] = os.path.join(dirpath, filename) # determine which files to keep among the checklist whitelist = set() for names in cr.split_for_in_conditions(checklist): cr.execute( "SELECT store_fname FROM ir_attachment WHERE store_fname IN %s", [names]) whitelist.update(row[0] for row in cr.fetchall()) # remove garbage files, and clean up checklist removed = 0 for fname, filepath in checklist.iteritems(): if fname not in whitelist: try: os.unlink(self._full_path(fname)) removed += 1 except (OSError, IOError): _logger.info("_file_gc could not unlink %s", self._full_path(fname), exc_info=True) with tools.ignore(OSError): os.unlink(filepath) _logger.info("filestore gc %d checked, %d removed", len(checklist), removed)
def validate_numerical_box(self, post, answer_tag): self.ensure_one() errors = {} answer = post[answer_tag].strip() # Empty answer to mandatory question if self.constr_mandatory and not answer: errors.update({answer_tag: self.constr_error_msg}) # Checks if user input is a number if answer: try: floatanswer = float(answer) except ValueError: errors.update({answer_tag: _('This is not a number')}) # Answer validation (if properly defined) if answer and self.validation_required: # Answer is not in the right range with tools.ignore(Exception): floatanswer = float(answer) # check that it is a float has been done hereunder if not (self.validation_min_float_value <= floatanswer <= self.validation_max_float_value): errors.update({answer_tag: self.validation_error_msg}) return errors
def _file_gc(self): """ Perform the garbage collection of the filestore. """ if self._storage() != 'file': return # prevent all concurrent updates on ir_attachment while collecting! cr = self._cr cr.execute("LOCK ir_attachment IN SHARE MODE") # retrieve the file names from the checklist checklist = {} for dirpath, _, filenames in os.walk(self._full_path('checklist')): dirname = os.path.basename(dirpath) for filename in filenames: fname = "%s/%s" % (dirname, filename) checklist[fname] = os.path.join(dirpath, filename) # determine which files to keep among the checklist whitelist = set() for names in cr.split_for_in_conditions(checklist): cr.execute("SELECT store_fname FROM ir_attachment WHERE store_fname IN %s", [names]) whitelist.update(row[0] for row in cr.fetchall()) # remove garbage files, and clean up checklist removed = 0 for fname, filepath in checklist.iteritems(): if fname not in whitelist: try: os.unlink(self._full_path(fname)) removed += 1 except (OSError, IOError): _logger.info("_file_gc could not unlink %s", self._full_path(fname), exc_info=True) with tools.ignore(OSError): os.unlink(filepath) _logger.info("filestore gc %d checked, %d removed", len(checklist), removed)
def validate_star_rate(self, post, answer_tag): self.ensure_one() errors = {} answer = post[answer_tag].strip() # Empty answer to mandatory question if self.constr_mandatory and not answer: errors.update({answer_tag: self.constr_error_msg}) # Checks if user input is a number if answer: try: floatanswer = float(answer) except ValueError: errors.update({answer_tag: "This is not a number"}) return errors # Answer is not in the right range with tools.ignore(Exception): # 0 answer to mandatory question if self.constr_mandatory: if floatanswer == 0: errors.update({answer_tag: self.constr_error_msg}) if not (0 <= floatanswer <= 5): errors.update( {answer_tag: "Answer is not in the right range"}) return errors
def setup_models(self, cr): """ Complete the setup of models. This must be called after loading modules and before using the ORM. """ lazy_property.reset_all(self) env = odoo.api.Environment(cr, SUPERUSER_ID, {}) # add manual models if self._init_modules: env['ir.model']._add_manual_models() # prepare the setup on all models models = list(env.values()) for model in models: model._prepare_setup() # do the actual setup from a clean state self._m2m = defaultdict(list) for model in models: model._setup_base() for model in models: model._setup_fields() # determine field dependencies dependencies = {} for model in models: if model._abstract: continue for field in model._fields.values(): # dependencies of custom fields may not exist; ignore that case exceptions = (Exception,) if field.manual else () with ignore(*exceptions): dependencies[field] = set(field.resolve_depends(model)) # determine transitive dependencies def transitive_dependencies(field, seen=[]): if field in seen: return for seq1 in dependencies[field]: yield seq1 for seq2 in transitive_dependencies(seq1[-1], seen + [field]): yield concat(seq1[:-1], seq2) def concat(seq1, seq2): if seq1 and seq2: f1, f2 = seq1[-1], seq2[0] if f1.type == 'one2many' and f2.type == 'many2one' and \ f1.model_name == f2.comodel_name and f1.inverse_name == f2.name: return concat(seq1[:-1], seq2[1:]) return seq1 + seq2 # determine triggers based on transitive dependencies triggers = {} for field in dependencies: for path in transitive_dependencies(field): if path: tree = triggers for label in reversed(path): tree = tree.setdefault(label, {}) tree.setdefault(None, set()).add(field) self.field_triggers = triggers for model in models: model._setup_complete() self.registry_invalidated = True
_logger.warning("%s: inconsistent 'compute_sudo' for computed fields: %s", self._name, ", ".join(field.name for field in fields)) return computed @lazy_property def field_triggers(self): # determine field dependencies dependencies = {} <<<<<<< HEAD for model in models: if model._abstract: continue for field in model._fields.values(): # dependencies of custom fields may not exist; ignore that case exceptions = (Exception,) if field.base_field.manual else () with ignore(*exceptions): dependencies[field] = set(field.resolve_depends(model)) ======= for Model in self.models.values(): if Model._abstract: continue for field in Model._fields.values(): # dependencies of custom fields may not exist; ignore that case exceptions = (Exception,) if field.base_field.manual else () with ignore(*exceptions): dependencies[field] = set(field.resolve_depends(self)) >>>>>>> f0a66d05e70e432d35dc68c9fb1e1cc6e51b40b8 # determine transitive dependencies def transitive_dependencies(field, seen=[]): if field in seen:
def setup_models(self, cr): """ Complete the setup of models. This must be called after loading modules and before using the ORM. """ env = odoo.api.Environment(cr, SUPERUSER_ID, {}) # Uninstall registry hooks. Because of the condition, this only happens # on a fully loaded registry, and not on a registry being loaded. if self.ready: for model in env.values(): model._unregister_hook() lazy_property.reset_all(self) if env.all.tocompute: _logger.error( "Remaining fields to compute before setting up registry: %s", env.all.tocompute, stack_info=True, ) # add manual models if self._init_modules: env['ir.model']._add_manual_models() # prepare the setup on all models models = list(env.values()) for model in models: model._prepare_setup() # do the actual setup from a clean state self._m2m = defaultdict(list) for model in models: model._setup_base() for model in models: model._setup_fields() # determine field dependencies dependencies = {} for model in models: if model._abstract: continue for field in model._fields.values(): # dependencies of custom fields may not exist; ignore that case exceptions = (Exception, ) if field.base_field.manual else () with ignore(*exceptions): dependencies[field] = set(field.resolve_depends(model)) # determine transitive dependencies def transitive_dependencies(field, seen=[]): if field in seen: return for seq1 in dependencies.get(field, ()): yield seq1 for seq2 in transitive_dependencies(seq1[-1], seen + [field]): yield concat(seq1[:-1], seq2) def concat(seq1, seq2): if seq1 and seq2: f1, f2 = seq1[-1], seq2[0] if f1.type == 'one2many' and f2.type == 'many2one' and \ f1.model_name == f2.comodel_name and f1.inverse_name == f2.name: return concat(seq1[:-1], seq2[1:]) return seq1 + seq2 # determine triggers based on transitive dependencies triggers = {} for field in dependencies: for path in transitive_dependencies(field): if path: tree = triggers for label in reversed(path): tree = tree.setdefault(label, {}) tree.setdefault(None, set()).add(field) self.field_triggers = triggers for model in models: model._setup_complete() self.registry_invalidated = True # Reinstall registry hooks. Because of the condition, this only happens # on a fully loaded registry, and not on a registry being loaded. if self.ready: for model in env.values(): model._register_hook() env['base'].flush()