feat: add blog.post inverse relationship and simplify email template

This commit is contained in:
Nicholas Riegel 2026-05-29 23:34:17 -04:00
parent fb89605191
commit f160f1c476
3 changed files with 54 additions and 88 deletions

View file

@ -5,111 +5,54 @@
<record id="email_template_blog_published" model="mail.template"> <record id="email_template_blog_published" model="mail.template">
<field name="name">Blog Publisher — Post Published Notification</field> <field name="name">Blog Publisher — Post Published Notification</field>
<field name="model_id" ref="model_itsulu_blog_generation_log"/> <field name="model_id" ref="model_itsulu_blog_generation_log"/>
<field name="subject">[${object.blog_post_id.blog_id.name or 'ITSulu Insights'}] Blog Post Published: ${object.blog_post_id.name or 'New Post'} - ${format_date(object.create_date, date_format='MMMM dd, yyyy')}</field> <field name="subject">Blog Post Published: ${object.blog_post_id.name or 'New Post'}</field>
<field name="email_from">${user.email_formatted}</field> <field name="email_from">${user.email_formatted}</field>
<field name="auto_delete">True</field> <field name="auto_delete">True</field>
<field name="body_html"><![CDATA[ <field name="body_html"><![CDATA[
<div dir="ltr" style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 700px;"> <div style="font-family: Arial, sans-serif; line-height: 1.6;">
<h2 style="color: #0066cc;">Today's ${object.blog_post_id.blog_id.name or 'ITSulu Insights'} Blog Post Published</h2> <h2>Blog Post Published</h2>
<p><strong>Date:</strong> ${format_date(object.create_date, date_format='MMMM dd, yyyy (EEEE)')}</p> <p><strong>Title:</strong> ${object.blog_post_id.name or 'N/A'}</p>
<p><strong>Title:</strong> ${object.blog_post_id.name or ''}</p> <p><strong>Blog:</strong> ${object.blog_post_id.blog_id.name or 'N/A'}</p>
% set post_url = object.blog_post_id.website_url and ('https://itsulu.com' + object.blog_post_id.website_url) or ''
<p><strong>URL:</strong> <a href="${post_url}" target="_blank">${post_url}</a></p> <h3>Social Media Posts — Ready to Post</h3>
% for social in object.blog_post_id.itsulu_social_id:
% if social.twitter_post_a or social.twitter_post_b:
<h4>Twitter</h4>
% if social.twitter_post_a:
<p><strong>Post A:</strong> ${social.twitter_post_a}</p>
% endif
% if social.twitter_post_b:
<p><strong>Post B:</strong> ${social.twitter_post_b}</p>
% endif
% endif
% if social.bluesky_post_a or social.bluesky_post_b:
<h4>BlueSky</h4>
% if social.bluesky_post_a:
<p><strong>Post A:</strong> ${social.bluesky_post_a}</p>
% endif
% if social.bluesky_post_b:
<p><strong>Post B:</strong> ${social.bluesky_post_b}</p>
% endif
% endif
% if social.mastodon_post:
<h4>Mastodon</h4>
<p>${social.mastodon_post}</p>
% endif
% if social.linkedin_post:
<h4>LinkedIn</h4>
<p>${social.linkedin_post}</p>
% endif
<p><strong>URL:</strong> https://itsulu.com${object.blog_post_id.website_url or ''}</p>
<h3 style="margin-top: 30px; color: #0066cc;">Post Details</h3>
<ul>
<li><strong>LLM Provider:</strong> ${object.llm_provider or ''} / ${object.llm_model or ''}</li>
<li><strong>Tokens Used:</strong> ${object.tokens_used or 0}</li>
<li><strong>Generation Time:</strong> ${'{:.1f}'.format(object.duration_seconds or 0)}s</li>
<li><strong>Tags:</strong>
% for tag in object.blog_post_id.tag_ids:
${tag.name}${' | ' if not loop.last else ''}
% endfor % endfor
</li>
<li><strong>Publication Status:</strong> ${'Published' if object.blog_post_id.is_published else 'Draft'}</li>
<li><strong>Blog:</strong> ${object.blog_post_id.blog_id.name or ''}</li>
</ul>
% set social = object.blog_post_id.itsulu_social_id
% if social
<h3 style="margin-top: 30px; color: #0066cc;">Social Media Posts — Ready to Post</h3>
<hr style="margin: 20px 0; border: none; border-top: 2px solid #ddd;"/>
% if social.twitter_enabled and (social.twitter_post_a or social.twitter_post_b)
<h4>🐦 X (Twitter) Post A:</h4>
<p style="background-color: #f5f5f5; padding: 15px; border-left: 4px solid #1da1f2; font-size: 14px;">
${social.twitter_post_a or ''}
</p>
<h4>🐦 X (Twitter) Post B:</h4>
<p style="background-color: #f5f5f5; padding: 15px; border-left: 4px solid #1da1f2; font-size: 14px;">
${social.twitter_post_b or ''}
</p>
% endif
<hr style="margin: 20px 0; border: none; border-top: 2px solid #ddd;"/>
% if social.bluesky_enabled and (social.bluesky_post_a or social.bluesky_post_b)
<h4>🌐 BlueSky Posts:</h4>
<p style="background-color: #f5f5f5; padding: 15px; border-left: 4px solid #1185fe; font-size: 14px;">
<strong>BlueSky A:</strong><br/>
${social.bluesky_post_a or ''}
</p>
<p style="background-color: #f5f5f5; padding: 15px; border-left: 4px solid #1185fe; font-size: 14px;">
<strong>BlueSky B:</strong><br/>
${social.bluesky_post_b or ''}
</p>
% endif
<hr style="margin: 20px 0; border: none; border-top: 2px solid #ddd;"/>
% if social.mastodon_enabled and social.mastodon_post
<h4>🦣 Fediverse/Mastodon Post:</h4>
<p style="background-color: #f5f5f5; padding: 15px; border-left: 4px solid #563acc; font-size: 14px;">
${social.mastodon_post or ''}
</p>
% endif
<hr style="margin: 20px 0; border: none; border-top: 2px solid #ddd;"/>
% if social.linkedin_enabled and social.linkedin_post
<h4>💼 LinkedIn Post:</h4>
<p style="background-color: #f5f5f5; padding: 15px; border-left: 4px solid #0a66c2; font-size: 14px;">
${social.linkedin_post or ''}
</p>
% endif
% if social.sources_referenced
<hr style="margin: 30px 0; border: none; border-top: 2px solid #ddd;"/>
<h3 style="color: #0066cc;">News Sources Referenced:</h3>
<ul>
% for line in (social.sources_referenced or '').splitlines():
% if line.strip()
% set parts = line.split(' — ', 1)
% if parts|length == 2
<li><a href="${parts[1].strip()}" target="_blank">${parts[0].strip()}</a></li>
% else
<li>${line.strip()}</li>
% endif
% endif
% endfor
</ul>
% endif
% endif <!-- end social block -->
<hr style="margin: 30px 0; border: none; border-top: 2px solid #ddd;"/>
<p style="margin-top: 40px; font-size: 12px; color: #999;">
<strong>Generated:</strong> ${format_date(object.create_date, date_format='MMMM dd, yyyy')} |
<strong>Service:</strong> ITSulu Blog Publisher<br/>
This email contains all social media post variations ready for posting
across X, BlueSky, Fediverse, and LinkedIn.
</p>
</div> </div>
]]></field> ]]></field>

View file

@ -2,6 +2,7 @@
""" """
itsulu_blog_publisher models. itsulu_blog_publisher models.
""" """
from . import blog_post
from . import blog_topic from . import blog_topic
from . import blog_schedule from . import blog_schedule
from . import blog_generation_log from . import blog_generation_log
@ -9,6 +10,7 @@ from . import blog_post_social
from . import res_config_settings from . import res_config_settings
__all__ = [ __all__ = [
'blog_post',
'blog_topic', 'blog_topic',
'blog_schedule', 'blog_schedule',
'blog_generation_log', 'blog_generation_log',

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
"""
Extend blog.post to add reverse relationship to itsulu.blog.post.social.
"""
from odoo import fields, models
class BlogPost(models.Model):
_inherit = 'blog.post'
itsulu_social_id = fields.One2many(
comodel_name='itsulu.blog.post.social',
inverse_name='blog_post_id',
string='Social Media Copy',
help='Social media copy generated for this post.',
)
def get_social_copy(self):
"""Return the single social copy record, or False if none exists."""
self.ensure_one()
return self.itsulu_social_id[0] if self.itsulu_social_id else False