[IMP] project: populate
New populate feature (#48479) now allows to define model generation methods to populate the database with model records for performance testing. This PR aims to implement database population for the `project` App. Furthermore, to reduce population time, a first performance improvement is applied to create `project.task` records in batch. -- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr closes odoo/odoo#50405 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
@@ -5,3 +5,4 @@ from . import controllers
|
||||
from . import models
|
||||
from . import report
|
||||
from . import wizard
|
||||
from . import populate
|
||||
|
||||
@@ -735,7 +735,7 @@ class Task(models.Model):
|
||||
self.company_id = self.project_id.company_id
|
||||
else:
|
||||
self.stage_id = False
|
||||
|
||||
|
||||
@api.onchange('company_id')
|
||||
def _onchange_task_company(self):
|
||||
if self.project_id.company_id != self.company_id:
|
||||
@@ -805,23 +805,32 @@ class Task(models.Model):
|
||||
# CRUD overrides
|
||||
# ------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
# context: no_log, because subtype already handle this
|
||||
context = dict(self.env.context)
|
||||
stage_id = vals.get('stage_id') or context.get('default_stage_id') or False
|
||||
# for default stage
|
||||
if vals.get('project_id') and not context.get('default_project_id'):
|
||||
context['default_project_id'] = vals.get('project_id')
|
||||
# user_id change: update date_assign
|
||||
if vals.get('user_id'):
|
||||
vals['date_assign'] = fields.Datetime.now()
|
||||
# Stage change: Update date_end if folded stage and date_last_stage_update
|
||||
if stage_id:
|
||||
vals.update(self.update_date_end(stage_id))
|
||||
vals['date_last_stage_update'] = fields.Datetime.now()
|
||||
task = super(Task, self.with_context(context)).create(vals)
|
||||
return task
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
default_stage = dict()
|
||||
for vals in vals_list:
|
||||
project_id = vals.get('project_id') or self.env.context.get('default_project_id')
|
||||
if project_id and not "company_id" in vals:
|
||||
vals["company_id"] = self.env["project.project"].browse(
|
||||
project_id
|
||||
).company_id.id or self.env.company.id
|
||||
if project_id and "stage_id" not in vals:
|
||||
# 1) Allows keeping the batch creation of tasks
|
||||
# 2) Ensure the defaults are correct (and computed once by project),
|
||||
# by using default get (instead of _get_default_stage_id or _stage_find),
|
||||
if project_id not in default_stage:
|
||||
default_stage[project_id] = self.with_context(
|
||||
default_project_id=project_id
|
||||
).default_get(['stage_id']).get('stage_id')
|
||||
vals["stage_id"] = default_stage[project_id]
|
||||
# user_id change: update date_assign
|
||||
if vals.get('user_id'):
|
||||
vals['date_assign'] = fields.Datetime.now()
|
||||
# Stage change: Update date_end if folded stage and date_last_stage_update
|
||||
if vals.get('stage_id'):
|
||||
vals.update(self.update_date_end(vals['stage_id']))
|
||||
vals['date_last_stage_update'] = fields.Datetime.now()
|
||||
return super().create(vals_list)
|
||||
|
||||
def write(self, vals):
|
||||
now = fields.Datetime.now()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from . import project
|
||||
@@ -0,0 +1,102 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import logging
|
||||
import collections
|
||||
|
||||
from odoo import models
|
||||
from odoo.tools import populate
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class ProjectStage(models.Model):
|
||||
_inherit = "project.task.type"
|
||||
_populate_sizes = {"small": 10, "medium": 50, "large": 500}
|
||||
|
||||
def _populate_factories(self):
|
||||
return [
|
||||
("name", populate.constant('stage_{counter}')),
|
||||
("sequence", populate.randomize([False] + [i for i in range(1, 101)])),
|
||||
("description", populate.constant('project_stage_description_{counter}')),
|
||||
("active", populate.randomize([True, False], [0.8, 0.2])),
|
||||
("fold", populate.randomize([True, False], [0.9, 0.1]))
|
||||
]
|
||||
|
||||
class ProjectProject(models.Model):
|
||||
_inherit = "project.project"
|
||||
_populate_sizes = {"small": 10, "medium": 50, "large": 1000}
|
||||
_populate_dependencies = ["res.company", "project.task.type"]
|
||||
|
||||
def _populate_factories(self):
|
||||
company_ids = self.env.registry.populated_models["res.company"]
|
||||
stage_ids = self.env.registry.populated_models["project.task.type"]
|
||||
|
||||
def get_company_id(random, **kwargs):
|
||||
return random.choice(company_ids)
|
||||
# user_ids from company.user_ids ?
|
||||
# Also add a partner_ids on res_company ?
|
||||
|
||||
def get_stage_ids(random, **kwargs):
|
||||
return [
|
||||
(6, 0, [
|
||||
random.choice(stage_ids)
|
||||
for i in range(random.choice([j for j in range(1, 10)]))
|
||||
])
|
||||
]
|
||||
|
||||
return [
|
||||
("name", populate.constant('project_{counter}')),
|
||||
("sequence", populate.randomize([False] + [i for i in range(1, 101)])),
|
||||
("active", populate.randomize([True, False], [0.8, 0.2])),
|
||||
("company_id", populate.compute(get_company_id)),
|
||||
("type_ids", populate.compute(get_stage_ids)),
|
||||
('color', populate.randomize([False] + [i for i in range(1, 7)])),
|
||||
# TODO user_id but what about multi-company coherence ??
|
||||
]
|
||||
|
||||
|
||||
class ProjectTask(models.Model):
|
||||
_inherit = "project.task"
|
||||
_populate_sizes = {"small": 500, "medium": 5000, "large": 50000}
|
||||
_populate_dependencies = ["project.project"]
|
||||
|
||||
def _populate_factories(self):
|
||||
project_ids = self.env.registry.populated_models["project.project"]
|
||||
stage_ids = self.env.registry.populated_models["project.task.type"]
|
||||
def get_project_id(random, **kwargs):
|
||||
return random.choice([False, False, False] + project_ids)
|
||||
def get_stage_id(random, **kwargs):
|
||||
return random.choice([False, False] + stage_ids)
|
||||
return [
|
||||
("name", populate.constant('project_task_{counter}')),
|
||||
("sequence", populate.randomize([False] + [i for i in range(1, 101)])),
|
||||
("active", populate.randomize([True, False], [0.8, 0.2])),
|
||||
("color", populate.randomize([False] + [i for i in range(1, 7)])),
|
||||
("kanban_state", populate.randomize(['normal', 'done', 'blocked'])),
|
||||
("project_id", populate.compute(get_project_id)),
|
||||
("stage_id", populate.compute(get_stage_id)),
|
||||
]
|
||||
|
||||
def _populate(self, size):
|
||||
records = super()._populate(size)
|
||||
# set parent_ids
|
||||
self._populate_set_children_tasks(records, size)
|
||||
return records
|
||||
|
||||
def _populate_set_children_tasks(self, tasks, size):
|
||||
_logger.info('Setting parent tasks')
|
||||
rand = populate.Random('project.task+children_generator')
|
||||
parents = self.env["project.task"]
|
||||
for task in tasks:
|
||||
if not rand.getrandbits(4):
|
||||
parents |= task
|
||||
parent_ids = parents.ids
|
||||
tasks -= parents
|
||||
parent_childs = collections.defaultdict(lambda: self.env['project.task'])
|
||||
for count, task in enumerate(tasks):
|
||||
if not rand.getrandbits(4):
|
||||
parent_childs[rand.choice(parent_ids)] |= task
|
||||
|
||||
for count, (parent, childs) in enumerate(parent_childs.items()):
|
||||
if (count + 1) % 100 == 0:
|
||||
_logger.info('Setting parent: %s/%s', count + 1, len(parent_childs))
|
||||
childs.write({'parent_id': parent})
|
||||
Reference in New Issue
Block a user