Refactor: Move SEO content files to root of module
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class OtkSeoTemplate(models.Model):
|
||||
_name = 'otk.seo.template'
|
||||
_description = 'SEO Content Template'
|
||||
_order = 'sequence, name'
|
||||
|
||||
name = fields.Char('Template Name', required=True, translate=True,
|
||||
help='A descriptive name for this template (e.g., "How-To Guide", "Product Review").')
|
||||
sequence = fields.Integer('Sequence', default=10,
|
||||
help='Order in which templates appear in selection lists. Lower numbers appear first.')
|
||||
active = fields.Boolean('Active', default=True,
|
||||
help='If unchecked, this template will not be available for selection.')
|
||||
|
||||
content_type = fields.Selection([
|
||||
('blog_post', 'Blog Post'),
|
||||
('product_desc', 'Product Description'),
|
||||
('category_desc', 'Category Description'),
|
||||
('landing_page', 'Landing Page Content'),
|
||||
], string='Content Type', required=True, default='blog_post',
|
||||
help='The type of content this template is designed for. Templates only appear for matching content types.')
|
||||
|
||||
description = fields.Text('Description', translate=True,
|
||||
help='Explanation of when and how to use this template. Shown to users when selecting a template.')
|
||||
|
||||
# === Prompt Configuration ===
|
||||
system_prompt = fields.Text('System Prompt',
|
||||
help='Background instructions for the AI defining its role, expertise, and constraints. This shapes the overall writing style and approach.')
|
||||
|
||||
content_prompt_template = fields.Text('Content Prompt Template',
|
||||
help='The main prompt template sent to the AI. Use placeholders: {keywords}, {topic}, {product_name}, {tone}, {word_count}, {language}. These will be replaced with actual values.')
|
||||
|
||||
title_prompt_template = fields.Text('Title Prompt Template',
|
||||
help='Template for generating the content title. Use placeholders to customize based on keywords or topic.')
|
||||
|
||||
meta_prompt_template = fields.Text('Meta Description Prompt Template',
|
||||
help='Template for generating the SEO meta description. Should produce text between 120-160 characters.')
|
||||
|
||||
# === Defaults ===
|
||||
default_tone = fields.Selection([
|
||||
('professional', 'Professional'),
|
||||
('casual', 'Casual'),
|
||||
('technical', 'Technical'),
|
||||
('persuasive', 'Persuasive'),
|
||||
('conversational', 'Conversational'),
|
||||
], string='Default Tone', default='professional',
|
||||
help='The default writing tone when using this template. Users can override this when generating content.')
|
||||
|
||||
default_word_count = fields.Selection([
|
||||
('short', 'Short (300-500 words)'),
|
||||
('medium', 'Medium (500-1000 words)'),
|
||||
('long', 'Long (1000-2000 words)'),
|
||||
], string='Default Length', default='medium',
|
||||
help='The default target word count when using this template. Users can override this when generating content.')
|
||||
|
||||
# === Image Generation ===
|
||||
include_images = fields.Boolean('Include Image Generation', default=True,
|
||||
help='If enabled, image generation will be included by default when using this template.')
|
||||
default_image_count = fields.Integer('Default Image Count', default=1,
|
||||
help='Default number of images to generate when using this template.')
|
||||
image_prompt_template = fields.Text('Image Prompt Template',
|
||||
help='Template for generating image prompts. Use placeholders: {title}, {keywords}, {section}. The AI will use this to create relevant images.')
|
||||
default_image_style = fields.Selection([
|
||||
('photorealistic', 'Photorealistic'),
|
||||
('illustration', 'Illustration'),
|
||||
('digital_art', 'Digital Art'),
|
||||
('watercolor', 'Watercolor'),
|
||||
('sketch', 'Sketch'),
|
||||
('3d_render', '3D Render'),
|
||||
], string='Default Image Style', default='photorealistic',
|
||||
help='The default visual style for generated images when using this template.')
|
||||
|
||||
# === Formatting Options ===
|
||||
include_toc = fields.Boolean('Include Table of Contents',
|
||||
help='If enabled, the AI will generate a table of contents at the beginning of the content. Best for longer articles.')
|
||||
include_faq = fields.Boolean('Include FAQ Section',
|
||||
help='If enabled, the AI will add a FAQ section at the end. Good for SEO and covering common questions.')
|
||||
include_cta = fields.Boolean('Include Call-to-Action',
|
||||
help='If enabled, the AI will include a call-to-action at the end of the content.')
|
||||
header_structure = fields.Selection([
|
||||
('h2_only', 'H2 Headers Only'),
|
||||
('h2_h3', 'H2 and H3 Headers'),
|
||||
('h2_h3_h4', 'H2, H3, and H4 Headers'),
|
||||
], string='Header Structure', default='h2_h3',
|
||||
help='Heading hierarchy to use in the content. H2/H3 is recommended for most content; simpler structure for short content, deeper nesting for comprehensive guides.')
|
||||
|
||||
# === Reference Library ===
|
||||
reference_ids = fields.Many2many('otk.seo.reference',
|
||||
'otk_seo_template_reference_rel', 'template_id', 'reference_id',
|
||||
string='Default References',
|
||||
domain="['|', ('content_type', '=', content_type), ('content_type', '=', 'all')]",
|
||||
help='References to include by default when using this template. Users can add or remove references when generating content.')
|
||||
|
||||
# === Statistics ===
|
||||
usage_count = fields.Integer('Times Used', compute='_compute_usage_count',
|
||||
help='Number of content items created using this template.')
|
||||
|
||||
def _compute_usage_count(self):
|
||||
if not self.ids:
|
||||
return
|
||||
data = self.env['otk.seo.content']._read_group(
|
||||
[('template_id', 'in', self.ids)],
|
||||
['template_id'],
|
||||
['__count'],
|
||||
)
|
||||
counts = {template.id: count for template, count in data}
|
||||
for record in self:
|
||||
record.usage_count = counts.get(record.id, 0)
|
||||
|
||||
def action_view_contents(self):
|
||||
"""View contents created with this template."""
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'otk.seo.content',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('template_id', '=', self.id)],
|
||||
'name': _('Contents using %s') % self.name,
|
||||
}
|
||||
Reference in New Issue
Block a user