Files
seo/otoolkit_seo_content/models/seo_template.py
T

121 lines
6.0 KiB
Python

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,
}