blurpritn adn site builde cleanup

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-01 02:22:02 +00:00
parent 3f2385d4d9
commit a7a772a78c
33 changed files with 1591 additions and 1308 deletions

View File

@@ -1,250 +1,12 @@
"""
Admin interface for Site Builder models
Admin interface for Site Building
Legacy SiteBlueprint admin removed - models deprecated.
"""
from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from igny8_core.admin.base import SiteSectorAdminMixin
from .models import (
SiteBlueprint,
PageBlueprint,
BusinessType,
AudienceProfile,
BrandPersonality,
HeroImageryDirection,
)
class PageBlueprintInline(admin.TabularInline):
"""Inline admin for Page Blueprints within Site Blueprint"""
model = PageBlueprint
extra = 0
fields = ['slug', 'title', 'type', 'status', 'order']
readonly_fields = ['slug', 'title', 'type', 'status', 'order']
can_delete = False
show_change_link = True
@admin.register(SiteBlueprint)
class SiteBlueprintAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
"""Admin interface for Site Blueprints"""
list_display = [
'name',
'get_site_display',
'get_sector_display',
'status',
'hosting_type',
'version',
'get_pages_count',
'created_at',
]
list_filter = ['status', 'hosting_type', 'site', 'sector', 'account', 'created_at']
search_fields = ['name', 'description', 'site__name', 'sector__name']
readonly_fields = ['created_at', 'updated_at', 'version', 'deployed_version']
ordering = ['-created_at']
inlines = [PageBlueprintInline]
fieldsets = (
('Basic Information', {
'fields': ('name', 'description', 'account', 'site', 'sector')
}),
('Status & Configuration', {
'fields': ('status', 'hosting_type', 'version', 'deployed_version')
}),
('Configuration Data', {
'fields': ('config_json',),
'classes': ('collapse',),
}),
('Structure Data', {
'fields': ('structure_json',),
'classes': ('collapse',),
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',),
}),
)
def get_site_display(self, obj):
"""Safely get site name"""
try:
if obj.site:
url = reverse('admin:igny8_core_auth_site_change', args=[obj.site.id])
return format_html('<a href="{}">{}</a>', url, obj.site.name)
return '-'
except:
return '-'
get_site_display.short_description = 'Site'
def get_sector_display(self, obj):
"""Safely get sector name"""
try:
return obj.sector.name if obj.sector else '-'
except:
return '-'
get_sector_display.short_description = 'Sector'
def get_pages_count(self, obj):
"""Get count of pages for this blueprint"""
try:
count = obj.pages.count()
if count > 0:
url = reverse('admin:site_building_pageblueprint_changelist')
return format_html('<a href="{}?site_blueprint__id__exact={}">{} pages</a>', url, obj.id, count)
return '0 pages'
except:
return '0 pages'
get_pages_count.short_description = 'Pages'
@admin.register(PageBlueprint)
class PageBlueprintAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
"""Admin interface for Page Blueprints"""
list_display = [
'title',
'slug',
'get_site_blueprint_display',
'type',
'status',
'order',
'get_site_display',
'get_sector_display',
'created_at',
]
list_filter = ['status', 'type', 'site_blueprint', 'site', 'sector', 'created_at']
search_fields = ['title', 'slug', 'site_blueprint__name', 'site__name']
readonly_fields = ['created_at', 'updated_at']
ordering = ['site_blueprint', 'order', 'created_at']
fieldsets = (
('Basic Information', {
'fields': ('site_blueprint', 'title', 'slug', 'type', 'status', 'order')
}),
('Account & Site', {
'fields': ('account', 'site', 'sector'),
'classes': ('collapse',),
}),
('Content Blocks', {
'fields': ('blocks_json',),
'classes': ('collapse',),
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',),
}),
)
def get_site_blueprint_display(self, obj):
"""Safely get site blueprint name with link"""
try:
if obj.site_blueprint:
url = reverse('admin:site_building_siteblueprint_change', args=[obj.site_blueprint.id])
return format_html('<a href="{}">{}</a>', url, obj.site_blueprint.name)
return '-'
except:
return '-'
get_site_blueprint_display.short_description = 'Site Blueprint'
def get_site_display(self, obj):
"""Safely get site name"""
try:
if obj.site:
url = reverse('admin:igny8_core_auth_site_change', args=[obj.site.id])
return format_html('<a href="{}">{}</a>', url, obj.site.name)
return '-'
except:
return '-'
get_site_display.short_description = 'Site'
def get_sector_display(self, obj):
"""Safely get sector name"""
try:
return obj.sector.name if obj.sector else '-'
except:
return '-'
get_sector_display.short_description = 'Sector'
@admin.register(BusinessType)
class BusinessTypeAdmin(admin.ModelAdmin):
"""Admin interface for Business Types"""
list_display = ['name', 'description', 'is_active', 'order', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['name', 'description']
list_editable = ['is_active', 'order']
ordering = ['order', 'name']
fieldsets = (
('Basic Information', {
'fields': ('name', 'description', 'is_active', 'order')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',),
}),
)
readonly_fields = ['created_at', 'updated_at']
@admin.register(AudienceProfile)
class AudienceProfileAdmin(admin.ModelAdmin):
"""Admin interface for Audience Profiles"""
list_display = ['name', 'description', 'is_active', 'order', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['name', 'description']
list_editable = ['is_active', 'order']
ordering = ['order', 'name']
fieldsets = (
('Basic Information', {
'fields': ('name', 'description', 'is_active', 'order')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',),
}),
)
readonly_fields = ['created_at', 'updated_at']
@admin.register(BrandPersonality)
class BrandPersonalityAdmin(admin.ModelAdmin):
"""Admin interface for Brand Personalities"""
list_display = ['name', 'description', 'is_active', 'order', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['name', 'description']
list_editable = ['is_active', 'order']
ordering = ['order', 'name']
fieldsets = (
('Basic Information', {
'fields': ('name', 'description', 'is_active', 'order')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',),
}),
)
readonly_fields = ['created_at', 'updated_at']
@admin.register(HeroImageryDirection)
class HeroImageryDirectionAdmin(admin.ModelAdmin):
"""Admin interface for Hero Imagery Directions"""
list_display = ['name', 'description', 'is_active', 'order', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['name', 'description']
list_editable = ['is_active', 'order']
ordering = ['order', 'name']
fieldsets = (
('Basic Information', {
'fields': ('name', 'description', 'is_active', 'order')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',),
}),
)
readonly_fields = ['created_at', 'updated_at']
# All SiteBuilder admin classes removed:
# - SiteBlueprintAdmin
# - PageBlueprintAdmin
# - BusinessTypeAdmin, AudienceProfileAdmin, BrandPersonalityAdmin, HeroImageryDirectionAdmin
#
# Site Builder functionality has been deprecated and removed from the system.

View File

@@ -0,0 +1,53 @@
# Generated manually on 2025-12-01
# Remove SiteBlueprint, PageBlueprint, SiteBlueprintCluster, and SiteBlueprintTaxonomy models
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('site_building', '0001_initial'), # Changed from 0002_initial
]
operations = [
# Drop tables in reverse dependency order
migrations.RunSQL(
sql=[
# Drop foreign key constraints first
"ALTER TABLE igny8_publishing_records DROP CONSTRAINT IF EXISTS igny8_publishing_recor_site_blueprint_id_9f4e8c7a_fk_igny8_sit CASCADE;",
"ALTER TABLE igny8_deployment_records DROP CONSTRAINT IF EXISTS igny8_deployment_recor_site_blueprint_id_3a2b7c1d_fk_igny8_sit CASCADE;",
# Drop the tables
"DROP TABLE IF EXISTS igny8_site_blueprint_taxonomies CASCADE;",
"DROP TABLE IF EXISTS igny8_site_blueprint_clusters CASCADE;",
"DROP TABLE IF EXISTS igny8_page_blueprints CASCADE;",
"DROP TABLE IF EXISTS igny8_site_blueprints CASCADE;",
"DROP TABLE IF EXISTS igny8_site_builder_business_types CASCADE;",
"DROP TABLE IF EXISTS igny8_site_builder_audience_profiles CASCADE;",
"DROP TABLE IF EXISTS igny8_site_builder_brand_personalities CASCADE;",
"DROP TABLE IF EXISTS igny8_site_builder_hero_imagery CASCADE;",
],
reverse_sql=[
# Reverse migration not supported - this is a destructive operation
"SELECT 1;"
],
),
# Also drop the site_blueprint_id column from PublishingRecord
migrations.RunSQL(
sql=[
"ALTER TABLE igny8_publishing_records DROP COLUMN IF EXISTS site_blueprint_id CASCADE;",
"DROP INDEX IF EXISTS igny8_publishing_recor_site_blueprint_id_des_b7c4e5f8_idx;",
],
reverse_sql=["SELECT 1;"],
),
# Drop the site_blueprint_id column from DeploymentRecord
migrations.RunSQL(
sql=[
"ALTER TABLE igny8_deployment_records DROP COLUMN IF EXISTS site_blueprint_id CASCADE;",
],
reverse_sql=["SELECT 1;"],
),
]

View File

@@ -1,346 +1,15 @@
"""
Site Builder Models
Phase 3: Site Builder
Site Building Models
Legacy SiteBuilder module has been removed.
This file is kept for backwards compatibility with migrations.
"""
from django.db import models
from django.core.validators import MinValueValidator
from igny8_core.auth.models import SiteSectorBaseModel
class SiteBlueprint(SiteSectorBaseModel):
"""
Site Blueprint model for storing AI-generated site structures.
"""
STATUS_CHOICES = [
('draft', 'Draft'),
('generating', 'Generating'),
('ready', 'Ready'),
('deployed', 'Deployed'),
]
HOSTING_TYPE_CHOICES = [
('igny8_sites', 'IGNY8 Sites'),
('wordpress', 'WordPress'),
('shopify', 'Shopify'),
('multi', 'Multiple Destinations'),
]
name = models.CharField(max_length=255, help_text="Site name")
description = models.TextField(blank=True, null=True, help_text="Site description")
# Site configuration (from wizard)
config_json = models.JSONField(
default=dict,
help_text="Wizard configuration: business_type, style, objectives, etc."
)
# Generated structure (from AI)
structure_json = models.JSONField(
default=dict,
help_text="AI-generated structure: pages, layout, theme, etc."
)
# Status tracking
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='draft',
db_index=True,
help_text="Blueprint status"
)
# Hosting configuration
hosting_type = models.CharField(
max_length=50,
choices=HOSTING_TYPE_CHOICES,
default='igny8_sites',
help_text="Target hosting platform"
)
# Version tracking
version = models.IntegerField(default=1, validators=[MinValueValidator(1)], help_text="Blueprint version")
deployed_version = models.IntegerField(null=True, blank=True, help_text="Currently deployed version")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
app_label = 'site_building'
db_table = 'igny8_site_blueprints'
ordering = ['-created_at']
verbose_name = 'Site Blueprint'
verbose_name_plural = 'Site Blueprints'
indexes = [
models.Index(fields=['status']),
models.Index(fields=['hosting_type']),
models.Index(fields=['site', 'sector']),
models.Index(fields=['account', 'status']),
]
def __str__(self):
return f"{self.name} ({self.get_status_display()})"
class PageBlueprint(SiteSectorBaseModel):
"""
Page Blueprint model for storing individual page definitions.
"""
PAGE_TYPE_CHOICES = [
('home', 'Home'),
('about', 'About'),
('services', 'Services'),
('products', 'Products'),
('blog', 'Blog'),
('contact', 'Contact'),
('custom', 'Custom'),
]
STATUS_CHOICES = [
('draft', 'Draft'),
('generating', 'Generating'),
('ready', 'Ready'),
('published', 'Published'),
]
site_blueprint = models.ForeignKey(
SiteBlueprint,
on_delete=models.CASCADE,
related_name='pages',
help_text="The site blueprint this page belongs to"
)
slug = models.SlugField(max_length=255, help_text="Page URL slug")
title = models.CharField(max_length=255, help_text="Page title")
# Page type
type = models.CharField(
max_length=50,
choices=PAGE_TYPE_CHOICES,
default='custom',
help_text="Page type"
)
# Page content (blocks)
blocks_json = models.JSONField(
default=list,
help_text="Page content blocks: [{'type': 'hero', 'data': {...}}, ...]"
)
# Status
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='draft',
db_index=True,
help_text="Page status"
)
# Order
order = models.IntegerField(default=0, help_text="Page order in navigation")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
app_label = 'site_building'
db_table = 'igny8_page_blueprints'
ordering = ['order', 'created_at']
verbose_name = 'Page Blueprint'
verbose_name_plural = 'Page Blueprints'
unique_together = [['site_blueprint', 'slug']]
indexes = [
models.Index(fields=['site_blueprint', 'status']),
models.Index(fields=['type']),
models.Index(fields=['site_blueprint', 'order']),
]
def save(self, *args, **kwargs):
"""Automatically set account, site, and sector from site_blueprint"""
if self.site_blueprint:
self.account = self.site_blueprint.account
self.site = self.site_blueprint.site
self.sector = self.site_blueprint.sector
super().save(*args, **kwargs)
def __str__(self):
return f"{self.title} ({self.site_blueprint.name})"
class SiteBlueprintCluster(SiteSectorBaseModel):
"""
Mapping table that connects planner clusters to site blueprints
with role/coverage metadata.
"""
ROLE_CHOICES = [
('hub', 'Hub Page'),
('supporting', 'Supporting Page'),
('attribute', 'Attribute Page'),
]
COVERAGE_CHOICES = [
('pending', 'Pending'),
('in_progress', 'In Progress'),
('complete', 'Complete'),
]
site_blueprint = models.ForeignKey(
SiteBlueprint,
on_delete=models.CASCADE,
related_name='cluster_links',
help_text="Site blueprint that is planning coverage for the cluster",
)
cluster = models.ForeignKey(
'planner.Clusters',
on_delete=models.CASCADE,
related_name='blueprint_links',
help_text="Planner cluster being mapped into the site blueprint",
)
role = models.CharField(max_length=50, choices=ROLE_CHOICES, default='hub')
coverage_status = models.CharField(max_length=50, choices=COVERAGE_CHOICES, default='pending')
metadata = models.JSONField(
default=dict,
blank=True,
help_text="Additional coverage metadata (target pages, keyword counts, ai hints)",
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
app_label = 'site_building'
db_table = 'igny8_site_blueprint_clusters'
unique_together = [['site_blueprint', 'cluster', 'role']]
verbose_name = 'Site Blueprint Cluster'
verbose_name_plural = 'Site Blueprint Clusters'
indexes = [
models.Index(fields=['site_blueprint', 'cluster']),
models.Index(fields=['site_blueprint', 'coverage_status']),
models.Index(fields=['cluster', 'role']),
]
def save(self, *args, **kwargs):
if self.site_blueprint:
self.account = self.site_blueprint.account
self.site = self.site_blueprint.site
self.sector = self.site_blueprint.sector
super().save(*args, **kwargs)
def __str__(self):
return f"{self.site_blueprint.name}{self.cluster.name}"
class SiteBlueprintTaxonomy(SiteSectorBaseModel):
"""
Taxonomy blueprint entity for categories/tags/product attributes tied to clusters.
"""
TAXONOMY_TYPE_CHOICES = [
('blog_category', 'Blog Category'),
('blog_tag', 'Blog Tag'),
('product_category', 'Product Category'),
('product_tag', 'Product Tag'),
('product_attribute', 'Product Attribute'),
('service_category', 'Service Category'),
]
site_blueprint = models.ForeignKey(
SiteBlueprint,
on_delete=models.CASCADE,
related_name='taxonomies',
help_text="Site blueprint this taxonomy belongs to",
)
name = models.CharField(max_length=255, help_text="Display name")
slug = models.SlugField(max_length=255, help_text="Slug/identifier within the site blueprint")
taxonomy_type = models.CharField(max_length=50, choices=TAXONOMY_TYPE_CHOICES, default='blog_category')
description = models.TextField(blank=True, null=True)
metadata = models.JSONField(default=dict, blank=True, help_text="Additional taxonomy metadata or AI hints")
clusters = models.ManyToManyField(
'planner.Clusters',
blank=True,
related_name='blueprint_taxonomies',
help_text="Planner clusters that this taxonomy maps to",
)
external_reference = models.CharField(
max_length=255,
blank=True,
null=True,
help_text="External system ID (WordPress/WooCommerce/etc.)",
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
app_label = 'site_building'
db_table = 'igny8_site_blueprint_taxonomies'
unique_together = [['site_blueprint', 'slug']]
verbose_name = 'Site Blueprint Taxonomy'
verbose_name_plural = 'Site Blueprint Taxonomies'
indexes = [
models.Index(fields=['site_blueprint', 'taxonomy_type']),
models.Index(fields=['taxonomy_type']),
]
def save(self, *args, **kwargs):
if self.site_blueprint:
self.account = self.site_blueprint.account
self.site = self.site_blueprint.site
self.sector = self.site_blueprint.sector
super().save(*args, **kwargs)
def __str__(self):
return f"{self.name} ({self.get_taxonomy_type_display()})"
class SiteBuilderOption(models.Model):
"""
Base model for Site Builder dropdown metadata.
"""
name = models.CharField(max_length=120, unique=True)
description = models.CharField(max_length=255, blank=True)
is_active = models.BooleanField(default=True)
order = models.PositiveIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
ordering = ['order', 'name']
def __str__(self):
return self.name
class BusinessType(SiteBuilderOption):
class Meta(SiteBuilderOption.Meta):
app_label = 'site_building'
db_table = 'igny8_site_builder_business_types'
verbose_name = 'Business Type'
verbose_name_plural = 'Business Types'
class AudienceProfile(SiteBuilderOption):
class Meta(SiteBuilderOption.Meta):
app_label = 'site_building'
db_table = 'igny8_site_builder_audience_profiles'
verbose_name = 'Audience Profile'
verbose_name_plural = 'Audience Profiles'
class BrandPersonality(SiteBuilderOption):
class Meta(SiteBuilderOption.Meta):
app_label = 'site_building'
db_table = 'igny8_site_builder_brand_personalities'
verbose_name = 'Brand Personality'
verbose_name_plural = 'Brand Personalities'
class HeroImageryDirection(SiteBuilderOption):
class Meta(SiteBuilderOption.Meta):
app_label = 'site_building'
db_table = 'igny8_site_builder_hero_imagery'
verbose_name = 'Hero Imagery Direction'
verbose_name_plural = 'Hero Imagery Directions'
# All SiteBuilder models have been removed:
# - SiteBlueprint
# - PageBlueprint
# - SiteBlueprintCluster
# - SiteBlueprintTaxonomy
# - BusinessType, AudienceProfile, BrandPersonality, HeroImageryDirection
#
# Taxonomy functionality moved to ContentTaxonomy model in business/content/models.py