<commit_before># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import datetime
import dateutil
import logging
import time
from collections import defaultdict

from odoo import api, fields, models, SUPERUSER_ID, tools,  _
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.modules.registry import Registry
from odoo.osv import expression
from odoo.tools import pycompat
from odoo.tools.safe_eval import safe_eval

_logger = logging.getLogger(__name__)

MODULE_UNINSTALL_FLAG = '_force_unlink'


# base environment for doing a safe_eval
SAFE_EVAL_BASE = {
    'datetime': datetime,
    'dateutil': dateutil,
    'time': time,
}

def make_compute(text, deps):
    """ Return a compute function from its code body and dependencies. """
    func = lambda self: safe_eval(text, SAFE_EVAL_BASE, {'self': self}, mode="exec")
    deps = [arg.strip() for arg in deps.split(",")] if deps else []
    return api.depends(*deps)(func)


# generic INSERT and UPDATE queries
INSERT_QUERY = "INSERT INTO {table} ({cols}) VALUES ({vals}) RETURNING id"
UPDATE_QUERY = "UPDATE {table} SET {assignment} WHERE {condition} RETURNING id"

def query_insert(cr, table, values):
    query = INSERT_QUERY.format(
        table=table,
        cols=",".join(values),
        vals=",".join("%({0})s".format(v) for v in values),
    )
    cr.execute(query, values)

def query_update(cr, table, values, selectors):
    setters = set(values) - set(selectors)
    query = UPDATE_QUERY.format(
        table=table,
        assignment=",".join("{0}=%({0})s".format(s) for s in setters),
        condition=" AND ".join("{0}=%({0})s".format(s) for s in selectors),
    )
    cr.execute(query, values)


#
# IMPORTANT: this must be the first model declared in the module
#
class Base(models.AbstractModel):
    """ The base model, which is implicitly inherited by all models. """
    _name = 'base'


class Unknown(models.AbstractModel):
    """
    Abstract model used as a substitute for relational fields with an unknown
    comodel.
    """
    _name = '_unknown'


class IrModel(models.Model):
    _name = 'ir.model'
    _description = "Models"
    _order = 'model'

    def _default_field_id(self):
        if self.env.context.get('install_mode'):
            return []                   # no default field when importing
        return [(0, 0, {'name': 'x_name', 'field_description': 'Name', 'ttype': 'char'})]

    name = fields.Char(string='Model Description', translate=True, required=True)
    model = fields.Char(default='x_', required=True, index=True)
    info = fields.Text(string='Information')
    field_id = fields.One2many('ir.model.fields', 'model_id', string='Fields', required=True, copy=True,
                               default=_default_field_id)
    inherited_model_ids = fields.Many2many('ir.model', compute='_inherited_models', string="Inherited models",
                                           help="The list of models that extends the current model.")
    state = fields.Selection([('manual', 'Custom Object'), ('base', 'Base Object')], string='Type', default='manual', readonly=True)
    access_ids = fields.One2many('ir.model.access', 'model_id', string='Access')
    transient = fields.Boolean(string="Transient Model")
    modules = fields.Char(compute='_in_modules', string='In Apps', help='List of modules in which the object is defined or inherited')
    view_ids = fields.One2many('ir.ui.view', compute='_view_ids', string='Views')
    count = fields.Integer(compute='_compute_count', string="Count (incl. archived)",
                           help="Total number of records in this model")

    @api.depends()
    def _inherited_models(self):
        for model in self:
            parent_names = list(self.env[model.model]._inherits)
            if parent_names:
                model.inherited_model_ids = self.search([('model', 'in', parent_names)])

    @api.depends()
    def _in_modules(self):
        installed_modules = self.env['ir.module.module'].search([('state', '=', 'installed')])
        installed_names = set(installed_modules.mapped('name'))
        xml_ids = models.Model._get_external_ids(self)
        for model in self:
            module_names = set(xml_id.split('.')[0] for xml_id in xml_ids[model.id])
            model.modules = ", ".join(sorted(installed_names & module_names))

    @api.depends()
    def _view_ids(self):
        for model in self:
            model.view_ids = self.env['ir.ui.view'].search([('model', '=', model.model)])

    @api.depends()
    def _compute_count(self):
        cr = self.env.cr
        for model in self:
            records = self.env[model.model]
            if not records._abstract:
                cr.execute('SELECT COUNT(*) FROM "%s"' % records._table)
                model.count = cr.fetchone()[0]

    @api.constrains('model')
    def _check_model_name(self):
        for model in self:
            if model.state == 'manual':
                if not model.model.startswith('x_'):
                    raise ValidationError(_("The model name must start with 'x_'."))
            if not models.check_object_name(model.model):
                raise ValidationError(_("The model name can only contain lowercase characters, digits, underscores and dots."))

    _sql_constraints = [
        ('obj_name_uniq', 'unique (model)', 'Each model must be unique!'),
    ]

    def _get(self, name):
        """ Return the (sudoed) `ir.model` record with the given name.
        The result may be an empty recordset if the model is not found.
        """
        model_id = self._get_id(name) if name else False
        return self.sudo().browse(model_id)

    @tools.ormcache('name')
    def _get_id(self, name):
        self.env.cr.execute("SELECT id FROM ir_model WHERE model=%s", (name,))
        result = self.env.cr.fetchone()
        return result and result[0]

    # overridden to allow searching both on model name (field 'model') and model
    # description (field 'name')
    @api.model
    def _name_search(self, name='', args=None, operator='ilike', limit=100):
        if args is None:
            args = []
        domain = args + ['|', ('model', operator, name), ('name', operator, name)]
        return super(IrModel, self).search(domain, limit=limit).name_get()

    def _drop_table(self):
        for model in self:
            table = self.env[model.model]._table
            kind = tools.table_kind(self._cr, table)
            if kind == 'v':
                self._cr.execute('DROP VIEW "%s"' % table)
            elif kind == 'r':
                self._cr.execute('DROP TABLE "%s" CASCADE' % table)
        return True

    @api.multi
    def unlink(self):
        # Prevent manual deletion of module tables
        if not self._context.get(MODULE_UNINSTALL_FLAG):
            for model in self:
                if model.state != 'manual':
                    raise UserError(_("Model '%s' contains module data and cannot be removed!") % model.name)
                # prevent screwing up fields that depend on these models' fields
                model.field_id._prepare_update()

        self._drop_table()
        res = super(IrModel, self).unlink()

        # Reload registry for normal unlink only. For module uninstall, the
        # reload is done independently in odoo.modules.loading.
        if not self._context.get(MODULE_UNINSTALL_FLAG):
            # setup models; this automatically removes model from registry
            self.pool.setup_models(self._cr)

        return res

    @api.multi
    def write(self, vals):
        if '__last_update' in self._context:
            self = self.with_context({k: v for k, v in self._context.items() if k != '__last_update'})
        if 'model' in vals and any(rec.model != vals['model'] for rec in self):
            raise UserError(_('Field "Model" cannot be modified on models.'))
        if 'state' in vals and any(rec.state != vals['state'] for rec in self):
            raise UserError(_('Field "Type" cannot be modified on models.'))<commit_msg>[FIX] ir_model: properly delete m2m whose comodel is being deleted<commit_after># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import datetime
import dateutil
import logging
import time
from collections import defaultdict

from odoo import api, fields, models, SUPERUSER_ID, tools,  _
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.modules.registry import Registry
from odoo.osv import expression
from odoo.tools import pycompat
from odoo.tools.safe_eval import safe_eval

_logger = logging.getLogger(__name__)

MODULE_UNINSTALL_FLAG = '_force_unlink'


# base environment for doing a safe_eval
SAFE_EVAL_BASE = {
    'datetime': datetime,
    'dateutil': dateutil,
    'time': time,
}

def make_compute(text, deps):
    """ Return a compute function from its code body and dependencies. """
    func = lambda self: safe_eval(text, SAFE_EVAL_BASE, {'self': self}, mode="exec")
    deps = [arg.strip() for arg in deps.split(",")] if deps else []
    return api.depends(*deps)(func)


# generic INSERT and UPDATE queries
INSERT_QUERY = "INSERT INTO {table} ({cols}) VALUES ({vals}) RETURNING id"
UPDATE_QUERY = "UPDATE {table} SET {assignment} WHERE {condition} RETURNING id"

def query_insert(cr, table, values):
    query = INSERT_QUERY.format(
        table=table,
        cols=",".join(values),
        vals=",".join("%({0})s".format(v) for v in values),
    )
    cr.execute(query, values)

def query_update(cr, table, values, selectors):
    setters = set(values) - set(selectors)
    query = UPDATE_QUERY.format(
        table=table,
        assignment=",".join("{0}=%({0})s".format(s) for s in setters),
        condition=" AND ".join("{0}=%({0})s".format(s) for s in selectors),
    )
    cr.execute(query, values)


#
# IMPORTANT: this must be the first model declared in the module
#
class Base(models.AbstractModel):
    """ The base model, which is implicitly inherited by all models. """
    _name = 'base'


class Unknown(models.AbstractModel):
    """
    Abstract model used as a substitute for relational fields with an unknown
    comodel.
    """
    _name = '_unknown'


class IrModel(models.Model):
    _name = 'ir.model'
    _description = "Models"
    _order = 'model'

    def _default_field_id(self):
        if self.env.context.get('install_mode'):
            return []                   # no default field when importing
        return [(0, 0, {'name': 'x_name', 'field_description': 'Name', 'ttype': 'char'})]

    name = fields.Char(string='Model Description', translate=True, required=True)
    model = fields.Char(default='x_', required=True, index=True)
    info = fields.Text(string='Information')
    field_id = fields.One2many('ir.model.fields', 'model_id', string='Fields', required=True, copy=True,
                               default=_default_field_id)
    inherited_model_ids = fields.Many2many('ir.model', compute='_inherited_models', string="Inherited models",
                                           help="The list of models that extends the current model.")
    state = fields.Selection([('manual', 'Custom Object'), ('base', 'Base Object')], string='Type', default='manual', readonly=True)
    access_ids = fields.One2many('ir.model.access', 'model_id', string='Access')
    transient = fields.Boolean(string="Transient Model")
    modules = fields.Char(compute='_in_modules', string='In Apps', help='List of modules in which the object is defined or inherited')
    view_ids = fields.One2many('ir.ui.view', compute='_view_ids', string='Views')
    count = fields.Integer(compute='_compute_count', string="Count (incl. archived)",
                           help="Total number of records in this model")

    @api.depends()
    def _inherited_models(self):
        for model in self:
            parent_names = list(self.env[model.model]._inherits)
            if parent_names:
                model.inherited_model_ids = self.search([('model', 'in', parent_names)])

    @api.depends()
    def _in_modules(self):
        installed_modules = self.env['ir.module.module'].search([('state', '=', 'installed')])
        installed_names = set(installed_modules.mapped('name'))
        xml_ids = models.Model._get_external_ids(self)
        for model in self:
            module_names = set(xml_id.split('.')[0] for xml_id in xml_ids[model.id])
            model.modules = ", ".join(sorted(installed_names & module_names))

    @api.depends()
    def _view_ids(self):
        for model in self:
            model.view_ids = self.env['ir.ui.view'].search([('model', '=', model.model)])

    @api.depends()
    def _compute_count(self):
        cr = self.env.cr
        for model in self:
            records = self.env[model.model]
            if not records._abstract:
                cr.execute('SELECT COUNT(*) FROM "%s"' % records._table)
                model.count = cr.fetchone()[0]

    @api.constrains('model')
    def _check_model_name(self):
        for model in self:
            if model.state == 'manual':
                if not model.model.startswith('x_'):
                    raise ValidationError(_("The model name must start with 'x_'."))
            if not models.check_object_name(model.model):
                raise ValidationError(_("The model name can only contain lowercase characters, digits, underscores and dots."))

    _sql_constraints = [
        ('obj_name_uniq', 'unique (model)', 'Each model must be unique!'),
    ]

    def _get(self, name):
        """ Return the (sudoed) `ir.model` record with the given name.
        The result may be an empty recordset if the model is not found.
        """
        model_id = self._get_id(name) if name else False
        return self.sudo().browse(model_id)

    @tools.ormcache('name')
    def _get_id(self, name):
        self.env.cr.execute("SELECT id FROM ir_model WHERE model=%s", (name,))
        result = self.env.cr.fetchone()
        return result and result[0]

    # overridden to allow searching both on model name (field 'model') and model
    # description (field 'name')
    @api.model
    def _name_search(self, name='', args=None, operator='ilike', limit=100):
        if args is None:
            args = []
        domain = args + ['|', ('model', operator, name), ('name', operator, name)]
        return super(IrModel, self).search(domain, limit=limit).name_get()

    def _drop_table(self):
        for model in self:
            table = self.env[model.model]._table
            kind = tools.table_kind(self._cr, table)
            if kind == 'v':
                self._cr.execute('DROP VIEW "%s"' % table)
            elif kind == 'r':
                self._cr.execute('DROP TABLE "%s" CASCADE' % table)
        return True

    @api.multi
    def unlink(self):
        # Prevent manual deletion of module tables
        if not self._context.get(MODULE_UNINSTALL_FLAG):
            for model in self:
                if model.state != 'manual':
                    raise UserError(_("Model '%s' contains module data and cannot be removed!") % model.name)
                # prevent screwing up fields that depend on these models' fields
                model.field_id._prepare_update()

        # delete fields whose comodel is being removed
        self.env['ir.model.fields'].search([('relation', 'in', self.mapped('model'))]).unlink()

        self._drop_table()
        res = super(IrModel, self).unlink()

        # Reload registry for normal unlink only. For module uninstall, the
        # reload is done independently in odoo.modules.loading.
        if not self._context.get(MODULE_UNINSTALL_FLAG):
            # setup models; this automatically removes model from registry
            self.pool.setup_models(self._cr)

        return res

    @api.multi
    def write(self, vals):
        if '__last_update' in self._context:
            self = self.with_context({k: v for k, v in self._context.items() if k != '__last_update'})
        if 'model' in vals and any(rec.model != vals['model'] for rec in self):
            raise UserError(_('Field "Model" cannot be modified on models.'))
        if 'state' in vals and any(rec.state != vals['state'] for rec in self):
            raise UserError(_('Field "Type" cannot be modified on models.'))
{'fraction_non_alphanumeric': 0.1265063926102649, 'fraction_numerical': 0.002263412246895455, 'mean_word_length': 2.823199251637044, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6864486', 'n_tokens_mistral': 4897, 'n_tokens_neox': 4726, 'n_words': 1388}