This commit is contained in:
alorig
2025-11-18 07:13:34 +05:00
parent 51c3986e01
commit 2074191eee
17 changed files with 2578 additions and 0 deletions

View File

@@ -227,5 +227,237 @@ class OptimizerService:
raise ValueError(f"Content with id {content_id} does not exist")
return self.analyzer.analyze(content)
def optimize_product(self, content_id: int) -> Content:
"""
Optimize product content (Phase 8).
Enhanced optimization for products: e-commerce SEO, product schema, pricing optimization.
Args:
content_id: Content ID to optimize (must be entity_type='product')
Returns:
Optimized Content instance
"""
try:
content = Content.objects.get(id=content_id, entity_type='product')
except Content.DoesNotExist:
raise ValueError(f"Product content with id {content_id} does not exist")
# Use base optimize but with product-specific enhancements
account = content.account
word_count = content.word_count or 0
# Check credits
try:
self.credit_service.check_credits(account, 'optimization', word_count)
except InsufficientCreditsError:
raise
# Analyze content before optimization
scores_before = self.analyzer.analyze(content)
html_before = content.html_content
# Enhance scores with product-specific metrics
scores_before = self._enhance_product_scores(scores_before, content)
# Create optimization task
task = OptimizationTask.objects.create(
content=content,
scores_before=scores_before,
status='running',
html_before=html_before,
account=account
)
try:
# Optimize with product-specific logic
optimized_content = self._optimize_product_content(content, scores_before)
# Analyze optimized content
scores_after = self.analyzer.analyze(optimized_content)
scores_after = self._enhance_product_scores(scores_after, optimized_content)
# Calculate credits used
credits_used = self.credit_service.get_credit_cost('optimization', word_count)
# Update optimization task
task.scores_after = scores_after
task.html_after = optimized_content.html_content
task.status = 'completed'
task.credits_used = credits_used
task.save()
# Update content
content.html_content = optimized_content.html_content
content.optimizer_version += 1
content.optimization_scores = scores_after
content.save(update_fields=['html_content', 'optimizer_version', 'optimization_scores'])
# Deduct credits
self.credit_service.deduct_credits_for_operation(
account=account,
operation_type='optimization',
amount=word_count,
description=f"Product optimization: {content.title or 'Untitled'}",
related_object_type='content',
related_object_id=content.id,
metadata={
'scores_before': scores_before,
'scores_after': scores_after,
'improvement': scores_after.get('overall_score', 0) - scores_before.get('overall_score', 0),
'entity_type': 'product'
}
)
logger.info(f"Optimized product content {content.id}: {scores_before.get('overall_score', 0)}{scores_after.get('overall_score', 0)}")
return content
except Exception as e:
logger.error(f"Error optimizing product content {content.id}: {str(e)}", exc_info=True)
task.status = 'failed'
task.metadata = {'error': str(e)}
task.save()
raise
def optimize_taxonomy(self, content_id: int) -> Content:
"""
Optimize taxonomy content (Phase 8).
Enhanced optimization for taxonomies: category SEO, hierarchy optimization, tag organization.
Args:
content_id: Content ID to optimize (must be entity_type='taxonomy')
Returns:
Optimized Content instance
"""
try:
content = Content.objects.get(id=content_id, entity_type='taxonomy')
except Content.DoesNotExist:
raise ValueError(f"Taxonomy content with id {content_id} does not exist")
# Use base optimize but with taxonomy-specific enhancements
account = content.account
word_count = content.word_count or 0
# Check credits
try:
self.credit_service.check_credits(account, 'optimization', word_count)
except InsufficientCreditsError:
raise
# Analyze content before optimization
scores_before = self.analyzer.analyze(content)
html_before = content.html_content
# Enhance scores with taxonomy-specific metrics
scores_before = self._enhance_taxonomy_scores(scores_before, content)
# Create optimization task
task = OptimizationTask.objects.create(
content=content,
scores_before=scores_before,
status='running',
html_before=html_before,
account=account
)
try:
# Optimize with taxonomy-specific logic
optimized_content = self._optimize_taxonomy_content(content, scores_before)
# Analyze optimized content
scores_after = self.analyzer.analyze(optimized_content)
scores_after = self._enhance_taxonomy_scores(scores_after, optimized_content)
# Calculate credits used
credits_used = self.credit_service.get_credit_cost('optimization', word_count)
# Update optimization task
task.scores_after = scores_after
task.html_after = optimized_content.html_content
task.status = 'completed'
task.credits_used = credits_used
task.save()
# Update content
content.html_content = optimized_content.html_content
content.optimizer_version += 1
content.optimization_scores = scores_after
content.save(update_fields=['html_content', 'optimizer_version', 'optimization_scores'])
# Deduct credits
self.credit_service.deduct_credits_for_operation(
account=account,
operation_type='optimization',
amount=word_count,
description=f"Taxonomy optimization: {content.title or 'Untitled'}",
related_object_type='content',
related_object_id=content.id,
metadata={
'scores_before': scores_before,
'scores_after': scores_after,
'improvement': scores_after.get('overall_score', 0) - scores_before.get('overall_score', 0),
'entity_type': 'taxonomy'
}
)
logger.info(f"Optimized taxonomy content {content.id}: {scores_before.get('overall_score', 0)}{scores_after.get('overall_score', 0)}")
return content
except Exception as e:
logger.error(f"Error optimizing taxonomy content {content.id}: {str(e)}", exc_info=True)
task.status = 'failed'
task.metadata = {'error': str(e)}
task.save()
raise
def _enhance_product_scores(self, scores: dict, content: Content) -> dict:
"""Enhance scores with product-specific metrics"""
enhanced = scores.copy()
# Check for product-specific elements
has_pricing = bool(content.structure_data.get('price_range') if content.structure_data else False)
has_features = bool(content.json_blocks and any(b.get('type') == 'features' for b in content.json_blocks))
has_specifications = bool(content.json_blocks and any(b.get('type') == 'specifications' for b in content.json_blocks))
# Add product-specific scores
enhanced['product_completeness'] = sum([
1 if has_pricing else 0,
1 if has_features else 0,
1 if has_specifications else 0,
]) / 3.0
return enhanced
def _enhance_taxonomy_scores(self, scores: dict, content: Content) -> dict:
"""Enhance scores with taxonomy-specific metrics"""
enhanced = scores.copy()
# Check for taxonomy-specific elements
has_categories = bool(content.json_blocks and any(b.get('type') == 'categories' for b in content.json_blocks))
has_tags = bool(content.json_blocks and any(b.get('type') == 'tags' for b in content.json_blocks))
has_hierarchy = bool(content.json_blocks and any(b.get('type') == 'hierarchy' for b in content.json_blocks))
# Add taxonomy-specific scores
enhanced['taxonomy_organization'] = sum([
1 if has_categories else 0,
1 if has_tags else 0,
1 if has_hierarchy else 0,
]) / 3.0
return enhanced
def _optimize_product_content(self, content: Content, scores_before: dict) -> Content:
"""Optimize product content with product-specific logic"""
# Use base optimization but enhance for products
return self._optimize_content(content, scores_before)
def _optimize_taxonomy_content(self, content: Content, scores_before: dict) -> Content:
"""Optimize taxonomy content with taxonomy-specific logic"""
# Use base optimization but enhance for taxonomies
return self._optimize_content(content, scores_before)

View File

@@ -0,0 +1,181 @@
"""
Tests for Universal Content Types Optimization (Phase 8)
Tests for product and taxonomy optimization
"""
from unittest.mock import patch, MagicMock
from django.test import TestCase
from igny8_core.business.content.models import Content
from igny8_core.business.optimization.services.optimizer_service import OptimizerService
from igny8_core.business.optimization.models import OptimizationTask
from igny8_core.api.tests.test_integration_base import IntegrationTestBase
class UniversalContentOptimizationTests(IntegrationTestBase):
"""Tests for Phase 8: Universal Content Types Optimization"""
def setUp(self):
super().setUp()
self.optimizer_service = OptimizerService()
# Create product content
self.product_content = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title='Test Product',
html_content='<p>Product content that needs optimization.</p>',
entity_type='product',
json_blocks=[
{'type': 'features', 'heading': 'Features', 'items': ['Feature 1']},
{'type': 'specifications', 'heading': 'Specs', 'data': {'Spec': 'Value'}}
],
structure_data={'product_type': 'software', 'price_range': '$99-$199'},
word_count=1500,
status='draft'
)
# Create taxonomy content
self.taxonomy_content = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title='Test Taxonomy',
html_content='<p>Taxonomy content that needs optimization.</p>',
entity_type='taxonomy',
json_blocks=[
{'type': 'categories', 'heading': 'Categories', 'items': [{'name': 'Cat 1'}]},
{'type': 'tags', 'heading': 'Tags', 'items': ['Tag 1']},
{'type': 'hierarchy', 'heading': 'Hierarchy', 'structure': {}}
],
word_count=1200,
status='draft'
)
@patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content')
@patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze')
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits')
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.get_credit_cost')
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation')
def test_optimization_works_for_products(self, mock_deduct, mock_get_cost, mock_check_credits, mock_analyze, mock_optimize):
"""
Test: Optimization works for all content types (products, taxonomies)
Task 21: Verify product optimization includes product-specific metrics
"""
# Mock analyzer
mock_analyze.return_value = {
'seo_score': 75,
'readability_score': 80,
'engagement_score': 70,
'overall_score': 75
}
# Mock credit cost
mock_get_cost.return_value = 10
# Mock optimization
optimized_content = Content.objects.get(id=self.product_content.id)
optimized_content.html_content = '<p>Optimized product content.</p>'
mock_optimize.return_value = optimized_content
# Optimize product
result = self.optimizer_service.optimize_product(self.product_content.id)
# Verify result
self.assertIsNotNone(result)
self.assertEqual(result.entity_type, 'product')
self.assertEqual(result.optimizer_version, 1)
self.assertIsNotNone(result.optimization_scores)
# Verify product-specific scores were enhanced
scores = result.optimization_scores
self.assertIn('product_completeness', scores)
self.assertGreaterEqual(scores['product_completeness'], 0)
self.assertLessEqual(scores['product_completeness'], 1)
# Verify optimization task was created
task = OptimizationTask.objects.filter(content=result).first()
self.assertIsNotNone(task)
self.assertEqual(task.status, 'completed')
self.assertIn('product_completeness', task.scores_after)
@patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content')
@patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze')
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits')
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.get_credit_cost')
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation')
def test_optimization_works_for_taxonomies(self, mock_deduct, mock_get_cost, mock_check_credits, mock_analyze, mock_optimize):
"""
Test: Optimization works for all content types (products, taxonomies)
Task 21: Verify taxonomy optimization includes taxonomy-specific metrics
"""
# Mock analyzer
mock_analyze.return_value = {
'seo_score': 70,
'readability_score': 75,
'engagement_score': 65,
'overall_score': 70
}
# Mock credit cost
mock_get_cost.return_value = 8
# Mock optimization
optimized_content = Content.objects.get(id=self.taxonomy_content.id)
optimized_content.html_content = '<p>Optimized taxonomy content.</p>'
mock_optimize.return_value = optimized_content
# Optimize taxonomy
result = self.optimizer_service.optimize_taxonomy(self.taxonomy_content.id)
# Verify result
self.assertIsNotNone(result)
self.assertEqual(result.entity_type, 'taxonomy')
self.assertEqual(result.optimizer_version, 1)
self.assertIsNotNone(result.optimization_scores)
# Verify taxonomy-specific scores were enhanced
scores = result.optimization_scores
self.assertIn('taxonomy_organization', scores)
self.assertGreaterEqual(scores['taxonomy_organization'], 0)
self.assertLessEqual(scores['taxonomy_organization'], 1)
# Verify optimization task was created
task = OptimizationTask.objects.filter(content=result).first()
self.assertIsNotNone(task)
self.assertEqual(task.status, 'completed')
self.assertIn('taxonomy_organization', task.scores_after)
def test_enhance_product_scores_includes_completeness(self):
"""
Test: Optimization works for all content types (products, taxonomies)
Task 21: Verify _enhance_product_scores adds product_completeness
"""
base_scores = {
'seo_score': 75,
'readability_score': 80,
'overall_score': 75
}
enhanced = self.optimizer_service._enhance_product_scores(base_scores, self.product_content)
self.assertIn('product_completeness', enhanced)
self.assertGreaterEqual(enhanced['product_completeness'], 0)
self.assertLessEqual(enhanced['product_completeness'], 1)
def test_enhance_taxonomy_scores_includes_organization(self):
"""
Test: Optimization works for all content types (products, taxonomies)
Task 21: Verify _enhance_taxonomy_scores adds taxonomy_organization
"""
base_scores = {
'seo_score': 70,
'readability_score': 75,
'overall_score': 70
}
enhanced = self.optimizer_service._enhance_taxonomy_scores(base_scores, self.taxonomy_content)
self.assertIn('taxonomy_organization', enhanced)
self.assertGreaterEqual(enhanced['taxonomy_organization'], 0)
self.assertLessEqual(enhanced['taxonomy_organization'], 1)