ITSulu Blog Publisher Odoo addon
Find a file
Nicholas Riegel 55ab28f5db port(14.0): fix schedule-test mock body_html + raise query budget to 60
- test_blog_schedule._make_mock_llm_response set .text but not .body_html;
  _create_blog_post writes body_html into blog.post.content. Odoo 14 rejects
  the unset MagicMock ("can't adapt type 'MagicMock'") where 17 stringified it.
  Set body_html/raw_text on the mock (fixes 6 TestBlogScheduleExecution tests).
- test_generation_uses_fewer_than_50_queries: Odoo 14 issues ~54 framework
  queries vs 17's <50; raise the 14.0 budget to 60 (still catches N+1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:51:19 -04:00
.claude docs: add Kubernetes test infrastructure documentation 2026-05-29 18:13:32 -04:00
addons/itsulu_blog_publisher port(14.0): fix schedule-test mock body_html + raise query budget to 60 2026-05-30 11:51:19 -04:00
docs Add comprehensive BDD framework documentation 2026-05-29 12:42:54 -04:00
e2e fix: deploy E2E tests on ITSulu K8s cluster instead of external Runboat 2026-05-30 00:58:46 -04:00
scripts chore: make versioning series-aware for multi-Odoo-version branches 2026-05-30 11:14:12 -04:00
templates Initialized from 'GitLab CI/CD components' project template 2025-03-19 11:40:54 +01:00
.gitlab-ci.yml release: v0.4.8 — CI pipeline green + Odoo 17 fixes (squash of !1) 2026-05-30 10:58:57 -04:00
.odoo-series release: v0.5.0 — establish Odoo 14.0 branch (ITSulu production target) 2026-05-30 11:17:49 -04:00
ARCHITECTURE.md docs: add Kubernetes test infrastructure documentation 2026-05-29 18:13:32 -04:00
CHANGELOG.md release: v0.5.0 — establish Odoo 14.0 branch (ITSulu production target) 2026-05-30 11:17:49 -04:00
CLAUDE.md chore: make versioning series-aware for multi-Odoo-version branches 2026-05-30 11:14:12 -04:00
conftest.py release: v0.4.8 — CI pipeline green + Odoo 17 fixes (squash of !1) 2026-05-30 10:58:57 -04:00
conftest_runner.py fix: auto-install addon via conftest for pytest-odoo 2026-05-29 22:45:54 -04:00
Dockerfile port(14.0): fix web_ribbon view attr + convert tests to setUp/self.env 2026-05-30 11:44:24 -04:00
GITLAB_SETUP_GUIDE.md docs: Add GitLab setup guide for Phase 3 E2E testing 2026-05-30 01:15:14 -04:00
LICENCE Initialized from 'GitLab CI/CD components' project template 2025-03-19 11:40:54 +01:00
main.py Claude Sonnet 4.6 2026-05-29 01:40:58 -04:00
PHASE2_ROADMAP.md docs: mark Phase 2 as complete with final status 2026-05-30 00:47:09 -04:00
PHASE3_DEPLOYMENT_CHECKLIST.md docs: update deployment checklist for ITSulu K8s cluster 2026-05-30 00:59:31 -04:00
PHASE3_ROADMAP.md feat: integrate Runboat E2E testing and performance tests into CI/CD pipeline 2026-05-30 00:54:59 -04:00
PORTING.md port(14.0): mail template -> Jinja2, view attrs -> Odoo 14 domains 2026-05-30 11:31:11 -04:00
README.md release: v0.5.0 — establish Odoo 14.0 branch (ITSulu production target) 2026-05-30 11:17:49 -04:00
VERSION release: v0.5.0 — establish Odoo 14.0 branch (ITSulu production target) 2026-05-30 11:17:49 -04:00

ITSulu Blog Publisher — Odoo 14 Addon

Version: 0.5.0

Automated blog post generation and publishing for Odoo 14.0 Community Edition, powered by generative AI (Anthropic Claude, OpenAI, Google Gemini, or Ollama).

⚠️ Porting in progress. This 14.0 branch is the target for the ITSulu production instance. It is seeded from the 17.0 baseline; the Odoo-14 API port (notably the older Jinja-style mail templates) is tracked in PORTING.md.

VersioningMAJOR.MINOR.PATCH (each 0999). MAJOR = major release for sale / significant change; MINOR = features or performance improvements; PATCH = a single group of commits. See CHANGELOG.md for release notes and CLAUDE.md §15 for the full scheme.

Odoo version branches — like odoo/odoo, each Odoo Community release lives on its own branch. Supported targets: 19.0 (latest stable) and 14.0 (the version ITSulu runs). Check out the branch matching your Odoo version. Release tags are namespaced by series, e.g. 19.0-v0.5.0.

Features

Core Functionality

  • On-Demand Generation — Wizard-driven blog post creation via backend form
  • Scheduled Generation — Three configurable daily cron slots (morning, afternoon, evening)
  • Multi-Provider LLM — Route requests to Anthropic Claude, OpenAI ChatGPT, Google Gemini, or local Ollama models
  • SEO Automation — Auto-populate meta titles, descriptions, keywords, and tags
  • Social Media Copy — Generate platform-specific posts for X, BlueSky, Mastodon, and LinkedIn
  • Auto-Publish — Publish generated posts immediately or save as drafts for review
  • Topic Queue — Priority-based topic queue for scheduled generation
  • Notification Emails — Automated emails with generated content and social copy
  • Generation Audit Trail — Complete logs with token usage, provider, model, and error tracking
  • Error Recovery — Retry button on failed generation logs

🧠 Supported LLM Providers

Provider Model Examples Config Parameter
Anthropic Claude claude-sonnet-4-20250514, claude-opus-4-1 itsulu_blog_publisher.anthropic_api_key
OpenAI gpt-4o, gpt-4-turbo itsulu_blog_publisher.openai_api_key
Google Gemini gemini-2.0-flash, gemini-pro itsulu_blog_publisher.gemini_api_key
Ollama Any local model (mistral, llama2, etc.) itsulu_blog_publisher.ollama_base_url

📊 Test Coverage

69 automated tests, all passing on the ITSulu Kubernetes test cluster.

Test Suite Count Status
Unit Tests (TDD) 48 All Passing
Behaviour Tests (BDD) 15 All Passing
Performance Benchmarks 6 All Passing
Total 69 100%

Coverage Areas:

  • Topic queue management (7 tests)
  • Generation audit logs (8 tests)
  • Social media copy (16 tests)
  • Schedule slots (10 tests)
  • LLM router dispatch (7 tests)
  • E2E / behaviour workflows (15 BDD scenarios)
  • Performance SLOs — latency, query count, token usage, concurrency (6 benchmarks)

Tests run as ephemeral Kubernetes jobs in the itsulu-testing namespace against a primed PostgreSQL template database. See CLAUDE.md §8 for the K8s test setup.

Installation

Prerequisites

  • Odoo 14.0 Community
  • Python 3.63.8
  • PostgreSQL 10+
  • pip packages: requests, pytest-odoo, pytest-bdd

Steps

  1. Clone the repository into your Odoo addons directory:
git clone https://github.com/itsulu-odoo/itsulu-blog-publisher.git \
  /path/to/odoo/addons/itsulu_blog_publisher
  1. Install Python dependencies:
pip install requests
  1. Install the addon in Odoo (UI or CLI):
odoo -d mydb -i itsulu_blog_publisher --stop-after-init
  1. Configure API keys in Odoo Settings → Technical → Parameters:

    • itsulu_blog_publisher.anthropic_api_key (if using Claude)
    • itsulu_blog_publisher.openai_api_key (if using OpenAI)
    • itsulu_blog_publisher.gemini_api_key (if using Gemini)
    • itsulu_blog_publisher.ollama_base_url (if using Ollama, e.g., http://localhost:11434)
  2. Configure notification emails in Settings:

    • itsulu_blog_publisher.notification_emails (comma-separated list)

Usage

1. Create a Blog Post On-Demand

  1. Go to BlogBlog PublisherGenerate Now
  2. Enter topic/prompt
  3. Select LLM provider and model
  4. Choose auto-publish or draft
  5. Click Generate

The addon will:

  • Call the selected LLM provider
  • Create a blog.post with generated title, body, SEO fields
  • Generate social media copy for enabled platforms
  • Send notification email if published
  • Log tokens used, provider, model, and generation time

2. Schedule Automatic Generation

  1. Go to BlogBlog PublisherSchedule Slots
  2. Create/configure 3 slots: Morning, Afternoon, Evening
  3. Set trigger time (UTC hours)
  4. Choose LLM provider and model per slot
  5. Enable auto-publish or save as draft
  6. Save and activate

The system will:

  • Run active slots at configured times via cron
  • Pull next topic from queue (if available) or LLM-chosen topic
  • Publish immediately or save for review
  • Send notification emails for published posts

3. Manage Topic Queue

  1. Go to BlogBlog PublisherTopics
  2. Create topics with title, notes, and priority (Urgent/High/Normal)
  3. Set state to "Pending"
  4. During generation, slots will consume pending topics in priority order
  5. Topics move to "Used" state after generation

4. Monitor Generation Logs

  1. Go to BlogBlog PublisherGeneration Logs
  2. View all generation attempts (success/error)
  3. Check tokens used, provider, model, duration
  4. Retry failed generations using Retry button

Configuration

Settings

Parameter Type Default Description
itsulu_blog_publisher.notification_emails Char (empty) Comma-separated email addresses for notifications
itsulu_blog_publisher.anthropic_api_key Char (empty) Anthropic API key for Claude models
itsulu_blog_publisher.openai_api_key Char (empty) OpenAI API key for ChatGPT models
itsulu_blog_publisher.gemini_api_key Char (empty) Google Gemini API key
itsulu_blog_publisher.ollama_base_url Char (empty) Base URL for local Ollama server

Email Template

The addon includes a pre-configured email template (email_template_blog_published) that:

  • Displays blog post title and metadata
  • Includes social media copy for each enabled platform
  • Links to the published post
  • Shows generation stats (tokens, model, time)

Customize by editing the template in EmailEmail Templates.

Architecture

Models

  • itsulu.blog.topic — Topic queue with priority and state
  • itsulu.blog.generation.log — Audit trail for all generation attempts
  • itsulu.blog.post.social — Social media copy linked to blog posts
  • itsulu.blog.schedule — Configurable cron slots for scheduled generation

Services

  • llm_router.py — Dispatcher that routes requests to provider APIs
  • anthropic_provider.py — Anthropic Claude integration
  • openai_provider.py — OpenAI ChatGPT integration
  • gemini_provider.py — Google Gemini integration
  • ollama_provider.py — Local Ollama model integration
  • image_router.py — Optional cover image generation (DALL·E, Imagen, Stable Diffusion)

Development

Running Tests

# All tests
pytest addons/itsulu_blog_publisher/tests/ -v

# Specific test file
pytest addons/itsulu_blog_publisher/tests/test_blog_topic.py -v

# Specific test
pytest addons/itsulu_blog_publisher/tests/test_blog_topic.py::TestBlogTopicPriority::test_get_next_topic_returns_highest_priority_pending_topic -v

# BDD scenarios only
pytest addons/itsulu_blog_publisher/tests/test_bdd_steps.py -v

TDD Workflow

This project follows strict TDD discipline (see CLAUDE.md):

  1. RED — Write failing test
  2. GREEN — Write minimum code to pass
  3. REFACTOR — Clean up while test stays green
  4. REPEAT — Next feature

Code Quality

Pre-commit hooks enforce:

  • Black code formatting
  • isort import sorting
  • pylint-odoo linting

Run before committing:

pre-commit run --all-files

Troubleshooting

"Configuration error: API key not configured"

Cause: API key parameter not set in Settings
Solution: Go to Settings → Technical → Parameters and add the required key for your LLM provider

"Unknown provider: "

Cause: Provider name misspelled or provider not supported
Solution: Use one of: anthropic, openai, gemini, ollama

"No blog.post record was created"

Cause: LLM response parsing failed or validation error
Check: Generation log for error message; verify LLM response format

"No email sent for published post"

Cause: Notification email template not found or recipients not configured
Solution:

  1. Ensure template exists: EmailEmail Templates → "Blog Post Published"
  2. Set notification emails: Settings → Technical → Parameters → itsulu_blog_publisher.notification_emails

Documentation

License

MIT License. See LICENSE for details.

Support

For issues, feature requests, or questions, open an issue on GitLab:
https://gitlab.com/itsulu-odoo/itsulu-blog-publisher/issues

Roadmap

Phase 3 (Runboat E2E)

  • Playwright end-to-end scenarios
  • UI-driven generation workflow testing
  • Multi-user collaboration scenarios

Phase 4 (Performance & Scale)

  • Query optimization (N+1 detection)
  • Batch generation for multiple topics
  • Template caching and pre-rendering
  • Performance benchmarks (throughput, latency)

Current Status: v0.4.8 — test suite green (69/69 passing on ITSulu K8s)
Last Updated: 2026-05-30
Maintainer: Nicholas Riegel (nicholasr@itsulu.com)