[IMP] website_forum,*: populate with forums, posts and comments
* website_slides_forum Populate a database with different quantities of forums, posts and comments.The number of replies per question and comments per post (question or reply) are defined by weights. We use SQL queries in the '_populate' method to update certain fields automatically managed by the ORM, as well as the 'last_activity_date' of posts (questions). The aim is to give posts, replies and comments varied and consistent create_dates, as well as a wide variety of authors (create_uid). Task-3349374 Task-3463533 closes odoo/odoo#132042 Signed-off-by: Florian Charlier (flch) <flch@odoo.com>
This commit is contained in:
@@ -2,3 +2,4 @@
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import populate
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import forum_forum
|
||||
from . import forum_post
|
||||
from . import mail_message
|
||||
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
from odoo.tools import populate
|
||||
|
||||
class Forum(models.Model):
|
||||
_inherit = 'forum.forum'
|
||||
_populate_sizes = {'small': 1, 'medium': 3, 'large': 10}
|
||||
|
||||
def _populate_factories(self):
|
||||
return [
|
||||
('name', populate.constant('Forum_{counter}')),
|
||||
('description', populate.constant('This is forum number {counter}'))
|
||||
]
|
||||
@@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
from odoo import fields, models
|
||||
from odoo.tools import populate
|
||||
|
||||
QA_WEIGHTS = {0: 25, 1: 35, 2: 20, 3: 10, 4: 4, 5: 3, 6: 2, 7: 1}
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class Post(models.Model):
|
||||
_inherit = 'forum.post'
|
||||
# Include an additional average of 2 post answers for each given size
|
||||
# e.g.: 100 posts as populated_model_records = ~300 actual forum.posts records
|
||||
_populate_sizes = {'small': 100, 'medium': 1000, 'large': 90000}
|
||||
_populate_dependencies = ['forum.forum', 'res.users']
|
||||
|
||||
def _populate_factories(self):
|
||||
forum_ids = self.env.registry.populated_models['forum.forum']
|
||||
hours = [_ for _ in range(1, 49)]
|
||||
random = populate.Random('forum_posts')
|
||||
|
||||
def create_answers(values=None, **kwargs):
|
||||
"""Create random number of answers
|
||||
We set the `last_activity_date` to convert it to `create_date` in `_populate`
|
||||
as the ORM prevents setting these.
|
||||
"""
|
||||
return [
|
||||
fields.Command.create({
|
||||
'name': f"reply to {values['name']}",
|
||||
'forum_id': values['forum_id'],
|
||||
'content': 'Answer content',
|
||||
'last_activity_date': fields.Datetime.add(values['last_activity_date'], hours=random.choice(hours)),
|
||||
})
|
||||
for _ in range(random.choices(*zip(*QA_WEIGHTS.items()))[0])
|
||||
]
|
||||
|
||||
def get_last_activity_date(iterator, *args):
|
||||
days = [_ for _ in range(3, 93)]
|
||||
now = fields.Datetime.now()
|
||||
|
||||
for values in iterator:
|
||||
values.update(last_activity_date=fields.Datetime.subtract(now, days=random.choice(days)))
|
||||
yield values
|
||||
|
||||
return [
|
||||
('forum_id', populate.randomize(forum_ids)),
|
||||
('name', populate.constant('post_{counter}')),
|
||||
('last_activity_date', get_last_activity_date), # Must be before call to 'create_answers'
|
||||
('child_ids', populate.compute(create_answers)),
|
||||
]
|
||||
|
||||
def _populate(self, size):
|
||||
records = super()._populate(size)
|
||||
user_ids = self.env.registry.populated_models['res.users']
|
||||
|
||||
# Overwrite auto-fields: use last_activity_date to update create date
|
||||
_logger.info('forum.post: update create date and uid')
|
||||
question_ids = tuple(records.ids)
|
||||
query = """
|
||||
SELECT setseed(0.5);
|
||||
UPDATE forum_post
|
||||
SET create_date = last_activity_date,
|
||||
create_uid = floor(random() * (%(max_value)s - %(min_value)s + 1) + %(min_value)s)
|
||||
WHERE id in %(question_ids)s or parent_id in %(question_ids)s
|
||||
"""
|
||||
self.env.cr.execute(query, {'question_ids': question_ids, 'min_value': user_ids[0], 'max_value': user_ids[-1]})
|
||||
|
||||
_logger.info('forum.post: update last_activity_date of questions with answers')
|
||||
query = """
|
||||
WITH latest_answer AS(
|
||||
SELECT parent_id, max(last_activity_date) as answer_date
|
||||
FROM forum_post
|
||||
WHERE parent_id in %(question_ids)s
|
||||
GROUP BY parent_id
|
||||
)
|
||||
UPDATE forum_post fp
|
||||
SET last_activity_date = latest_answer.answer_date
|
||||
FROM latest_answer
|
||||
WHERE fp.id = latest_answer.parent_id
|
||||
"""
|
||||
self.env.cr.execute(query, {'question_ids': question_ids})
|
||||
|
||||
return records
|
||||
@@ -0,0 +1,106 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
from odoo import fields, models
|
||||
from odoo.tools import populate
|
||||
|
||||
CP_WEIGHTS = {1: 35, 2: 30, 3: 25, 4: 10}
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class Message(models.Model):
|
||||
_inherit = 'mail.message'
|
||||
_populate_dependencies = ['forum.post']
|
||||
_populate_sizes = {'small': 100, 'medium': 1000, 'large': 90000}
|
||||
|
||||
def _populate_factories(self):
|
||||
comment_subtype = self.env.ref('mail.mt_comment')
|
||||
random = populate.Random('comments_on_forum_posts')
|
||||
|
||||
def get_author_id(iterator, *args):
|
||||
user_ids = self.env.registry.populated_models['res.users']
|
||||
author_ids = self.env['res.users'].search_read([('id', 'in', user_ids)], ['partner_id'])
|
||||
|
||||
for values in iterator:
|
||||
author_id = random.choice(author_ids)
|
||||
values.update(author_id=author_id['partner_id'][0])
|
||||
yield values
|
||||
|
||||
def get_res_id_and_date(iterator, *args):
|
||||
"""Randomly assign messages to some populated posts and answers.
|
||||
This makes sure these questions and answers get one to four comments.
|
||||
Also define a date that is more recent than the post/answer's create_date
|
||||
"""
|
||||
question_ids = self.env.registry.populated_models['forum.post']
|
||||
posts = self.env['forum.post'].search_read(['|', ('id', 'in', question_ids), ('parent_id', 'in', question_ids)], ['id', 'create_date'])
|
||||
sample_size = int(len(question_ids) * 0.7)
|
||||
res_ids = random.sample(posts, sample_size)
|
||||
nb_comments = 0
|
||||
idx = 0
|
||||
post_to_comment_id = None
|
||||
hours = [_ for _ in range(1, 24)]
|
||||
|
||||
for values in iterator:
|
||||
if nb_comments == 0:
|
||||
nb_comments = random.choices(*zip(*CP_WEIGHTS.items()))[0]
|
||||
idx += 1
|
||||
post_to_comment_id = res_ids[idx]['id']
|
||||
nb_comments -= 1
|
||||
values.update({
|
||||
'date': fields.Datetime.add(res_ids[idx]['create_date'], hours=random.choice(hours)),
|
||||
'res_id': post_to_comment_id
|
||||
})
|
||||
yield values
|
||||
|
||||
return [
|
||||
('author_id', get_author_id),
|
||||
('body', populate.constant('message_body_{counter}')),
|
||||
('message_type', populate.constant('comment')),
|
||||
('model', populate.constant('forum.post')),
|
||||
('_res_id_and_date', get_res_id_and_date),
|
||||
('subtype_id', populate.constant(comment_subtype.id)),
|
||||
]
|
||||
|
||||
def _populate(self, size):
|
||||
records = super()._populate(size)
|
||||
|
||||
_logger.info('mail.message: update comments create date and uid')
|
||||
_logger.info('forum.post: update last_activity_date for posts with comments and/or commented answers')
|
||||
query = """
|
||||
WITH comment_author AS(
|
||||
SELECT mm.id, mm.author_id, ru.id as user_id, ru.partner_id
|
||||
FROM mail_message mm
|
||||
JOIN res_users ru
|
||||
ON mm.author_id = ru.partner_id
|
||||
WHERE mm.id in %(comment_ids)s
|
||||
),
|
||||
updated_comments as (
|
||||
UPDATE mail_message mm
|
||||
SET create_date = date,
|
||||
create_uid = ca.user_id
|
||||
FROM comment_author ca
|
||||
WHERE mm.id = ca.id
|
||||
RETURNING res_id as post_id, create_date as comment_date
|
||||
),
|
||||
max_comment_dates AS (
|
||||
SELECT post_id, max(comment_date) as last_comment_date
|
||||
FROM updated_comments
|
||||
GROUP BY post_id
|
||||
),
|
||||
updated_posts AS (
|
||||
UPDATE forum_post fp
|
||||
SET last_activity_date = CASE --on questions, answer could be more recent
|
||||
WHEN fp.parent_id IS NOT NULL THEN greatest(last_activity_date, last_comment_date)
|
||||
ELSE last_comment_date END
|
||||
FROM max_comment_dates
|
||||
WHERE max_comment_dates.post_id = fp.id
|
||||
RETURNING fp.id as post_id, fp.last_activity_date as last_activity_date, fp.parent_id as parent_id
|
||||
)
|
||||
UPDATE forum_post fp
|
||||
SET last_activity_date = greatest(fp.last_activity_date, up.last_activity_date)
|
||||
FROM updated_posts up
|
||||
WHERE up.parent_id = fp.id
|
||||
"""
|
||||
self.env.cr.execute(query, {'comment_ids': tuple(records.ids)})
|
||||
|
||||
return records
|
||||
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import populate
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import forum_forum
|
||||
@@ -0,0 +1,30 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from itertools import zip_longest
|
||||
|
||||
from odoo import models
|
||||
from odoo.addons.website_slides.populate.slide_channel import SlideChannel
|
||||
|
||||
|
||||
class SlidesForum(models.Model):
|
||||
_inherit = 'forum.forum'
|
||||
|
||||
@property
|
||||
def _populate_sizes(self):
|
||||
return {size: count + SlideChannel._populate_sizes[size] for size, count in super()._populate_sizes.items()}
|
||||
|
||||
@property
|
||||
def _populate_dependencies(self):
|
||||
return super()._populate_dependencies + ['slide.channel']
|
||||
|
||||
def _populate_factories(self):
|
||||
def link_course(iterator, *args, **kwargs):
|
||||
courses = self.env['slide.channel'].browse(self.env.registry.populated_models['slide.channel'])
|
||||
for values, course in zip_longest(iterator, courses):
|
||||
if course:
|
||||
values.update(slide_channel_ids=course, name=f"{course.name}'s Forum")
|
||||
yield values
|
||||
|
||||
return super()._populate_factories() + [
|
||||
('_name_and_course', link_course),
|
||||
]
|
||||
Reference in New Issue
Block a user