Initial commit: Django Digital Branding Management System

- Core Django project structure with branding_system and dashboard apps
- Platform, ContentDeliverable, and Strategy models with relationships
- Comprehensive Django admin interface with custom displays
- Dashboard views with completion tracking and overdue detection
- Tailwind CSS styling with custom color scheme
- WARP.md documentation for development guidance
This commit is contained in:
thecyberlearn 2025-09-07 10:16:37 +05:30
commit c10d4692e6
32 changed files with 1280 additions and 0 deletions

184
WARP.md Normal file
View File

@ -0,0 +1,184 @@
# WARP.md
This file provides guidance to WARP (warp.dev) when working with code in this repository.
## Project Overview
This is a Django-based Digital Branding Management System designed to help manage content creation and publication across multiple digital platforms. The system tracks platforms, content deliverables, and strategies with comprehensive admin functionality.
## Architecture
### Core Models & Relationships
The application is built around three main models with clear relationships:
- **Platform**: Represents social media/content platforms (LinkedIn, YouTube, Blog, etc.)
- Has many ContentDeliverable and Strategy records
- Includes completion rate calculations and status tracking
- **ContentDeliverable**: Individual content items with progress tracking
- Belongs to a Platform
- Tracks status progression: committed → drafted → published
- Includes due date tracking and overdue detection
- **Strategy**: Platform-specific strategic objectives and tactics
- Belongs to a Platform
- Follows OTAC structure (Objective, Tactics, Actions, Control)
- Has priority levels and active/inactive states
### Application Structure
```
digital-branding-system/
├── branding_system/ # Django project settings
├── dashboard/ # Main app with models, views, admin
├── templates/ # HTML templates with Tailwind CSS
├── static/ # Static files
└── manage.py # Django management script
```
## Development Commands
### Environment Setup
```bash
# Activate virtual environment
source venv/bin/activate
# Install dependencies (if requirements.txt is created)
pip install -r requirements.txt
```
### Django Management
```bash
# Run development server
python manage.py runserver
# Database operations
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
# Testing
python manage.py test
python manage.py test dashboard
# Database shell and management
python manage.py shell
python manage.py shell_plus # Enhanced shell via django-extensions
python manage.py dbshell
```
### Django Extensions Commands
The project uses django-extensions which provides additional helpful commands:
```bash
# Show URL patterns
python manage.py show_urls
# Generate model graphs
python manage.py graph_models -a -o models.png
# Reset database (development only)
python manage.py reset_db
# Export data as Python script
python manage.py dumpscript dashboard > backup_data.py
# Show all available commands
python manage.py help
```
### Data Management
```bash
# Create sample data via admin interface at /admin/
# Default admin is accessible once superuser is created
# Export/import data
python manage.py dumpdata dashboard > dashboard_data.json
python manage.py loaddata dashboard_data.json
```
## Key Features
### Admin Interface Customizations
- Comprehensive admin interface with custom displays and filters
- Color-coded status indicators and progress bars
- Bulk actions for status changes
- Custom admin site branding: "Digital Branding Management System"
### Dashboard Views
- Main dashboard with platform overview cards
- Platform-specific detail views
- Real-time completion rate calculations
- Overdue content tracking
### Model Properties & Methods
Models include calculated properties for:
- Completion rates and counts by status
- Overdue detection and days until due
- Total deliverable counts per platform
## Database Schema Notes
### Current Setup
- Uses SQLite database (`db.sqlite3`)
- Includes Django REST Framework (though no API endpoints are currently defined)
- Models use standard Django field types with proper relationships
### Important Field Validations
- Priority levels: 1-5 scale (1 = highest priority)
- Completion percentage: 0-100% validation
- Status choices are enforced at model level
- Date validations for target vs actual dates
## Styling & Frontend
### CSS Framework
- Uses Tailwind CSS via CDN
- Custom color scheme defined:
- brand-blue (#3B82F6)
- brand-green (#10B981)
- brand-orange (#F59E0B)
- brand-red (#EF4444)
- brand-purple (#8B5CF6)
### Template Structure
- Base template with navigation and common styling
- Dashboard template extends base template
- Font Awesome icons integrated
## Testing & Development
### Test Structure
- Basic test framework is in place (`dashboard/tests.py`)
- Tests should be added for models, views, and admin functionality
### Development Workflow
1. Activate virtual environment
2. Run migrations if needed
3. Start development server
4. Access admin interface for data management
5. View dashboard at root URL
## Deployment Considerations
### Security Settings
- SECRET_KEY is currently hardcoded (should be moved to environment variable)
- DEBUG=True (should be False in production)
- ALLOWED_HOSTS is empty (needs to be configured for production)
### Static Files
- Static files configured for development
- STATIC_ROOT set to 'staticfiles' for production collection
### Database
- Currently uses SQLite (consider PostgreSQL for production)
- Database migrations are tracked in dashboard/migrations/
## URL Structure
- `/` - Main dashboard
- `/admin/` - Django admin interface
- `/platform/<id>/` - Platform detail view
## Dependencies
Key packages:
- Django 5.2.6
- django-extensions 4.1
- djangorestframework 3.16.1

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
branding_system/asgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
ASGI config for branding_system project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branding_system.settings')
application = get_asgi_application()

129
branding_system/settings.py Normal file
View File

@ -0,0 +1,129 @@
"""
Django settings for branding_system project.
Generated by 'django-admin startproject' using Django 5.2.6.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-0%lol6add_bnxa)-r4e#9w-!ld-mxgq5q7u98$@@cb5y0+n4u^'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'django_extensions',
'dashboard',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'branding_system.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'branding_system.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
STATIC_ROOT = BASE_DIR / 'staticfiles'
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

23
branding_system/urls.py Normal file
View File

@ -0,0 +1,23 @@
"""
URL configuration for branding_system project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('dashboard.urls')),
]

16
branding_system/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for branding_system project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branding_system.settings')
application = get_wsgi_application()

0
dashboard/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

181
dashboard/admin.py Normal file
View File

@ -0,0 +1,181 @@
from django.contrib import admin
from django.utils.html import format_html
from .models import Platform, ContentDeliverable, Strategy
@admin.register(Platform)
class PlatformAdmin(admin.ModelAdmin):
list_display = (
'name', 'platform_type', 'status', 'primary_content_type',
'total_deliverables', 'completion_rate_display', 'created_at'
)
list_filter = ('platform_type', 'status', 'created_at')
search_fields = ('name', 'description', 'primary_content_type')
readonly_fields = ('created_at', 'updated_at')
fieldsets = (
('Basic Information', {
'fields': ('name', 'platform_type', 'status', 'primary_content_type')
}),
('Details', {
'fields': ('description', 'target_audience', 'posting_frequency')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
})
)
def completion_rate_display(self, obj):
rate = obj.completion_rate
color = 'green' if rate >= 70 else 'orange' if rate >= 40 else 'red'
return format_html(
'<span style="color: {};">{:.1f}%</span>',
color, rate
)
completion_rate_display.short_description = 'Completion Rate'
def total_deliverables(self, obj):
return obj.total_deliverables
total_deliverables.short_description = 'Total Content'
class ContentDeliverableInline(admin.TabularInline):
model = ContentDeliverable
extra = 0
fields = ('title', 'content_type', 'status', 'completion_percentage', 'target_date')
readonly_fields = ()
class StrategyInline(admin.TabularInline):
model = Strategy
extra = 0
fields = ('title', 'priority', 'is_active', 'start_date', 'end_date')
@admin.register(ContentDeliverable)
class ContentDeliverableAdmin(admin.ModelAdmin):
list_display = (
'title', 'platform', 'content_type', 'status_display',
'completion_percentage_display', 'target_date', 'is_overdue_display'
)
list_filter = ('status', 'content_type', 'platform', 'target_date', 'created_at')
search_fields = ('title', 'description', 'tags', 'platform__name')
readonly_fields = ('created_at', 'updated_at', 'days_until_due')
date_hierarchy = 'target_date'
fieldsets = (
('Basic Information', {
'fields': ('platform', 'title', 'content_type', 'status')
}),
('Content Details', {
'fields': ('description', 'completion_percentage', 'tags')
}),
('Dates', {
'fields': ('target_date', 'actual_date', 'days_until_due')
}),
('Additional Info', {
'fields': ('url', 'notes')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
})
)
def status_display(self, obj):
status_colors = {
'committed': 'blue',
'drafted': 'orange',
'published': 'green',
'cancelled': 'red'
}
color = status_colors.get(obj.status, 'black')
return format_html(
'<span style="color: {}; font-weight: bold;">{}</span>',
color, obj.get_status_display()
)
status_display.short_description = 'Status'
def completion_percentage_display(self, obj):
percentage = obj.completion_percentage
if percentage == 100:
color = 'green'
elif percentage >= 50:
color = 'orange'
else:
color = 'red'
return format_html(
'<div style="width: 100px; background-color: #f0f0f0; border-radius: 3px; padding: 2px;">' +
'<div style="width: {}%; background-color: {}; height: 20px; border-radius: 2px; text-align: center; color: white; line-height: 20px; font-size: 12px;">' +
'{}%</div></div>',
percentage, color, percentage
)
completion_percentage_display.short_description = 'Progress'
def is_overdue_display(self, obj):
if obj.is_overdue:
return format_html('<span style="color: red; font-weight: bold;">⚠ OVERDUE</span>')
elif obj.days_until_due is not None and obj.days_until_due <= 3 and obj.status != 'published':
return format_html('<span style="color: orange;">⏰ Due Soon</span>')
return ''
is_overdue_display.short_description = 'Due Status'
actions = ['mark_as_drafted', 'mark_as_published', 'mark_as_committed']
def mark_as_drafted(self, request, queryset):
updated = queryset.update(status='drafted')
self.message_user(request, f'{updated} deliverables marked as drafted.')
mark_as_drafted.short_description = 'Mark selected items as drafted'
def mark_as_published(self, request, queryset):
updated = queryset.update(status='published', completion_percentage=100)
self.message_user(request, f'{updated} deliverables marked as published.')
mark_as_published.short_description = 'Mark selected items as published'
def mark_as_committed(self, request, queryset):
updated = queryset.update(status='committed')
self.message_user(request, f'{updated} deliverables marked as committed.')
mark_as_committed.short_description = 'Mark selected items as committed'
@admin.register(Strategy)
class StrategyAdmin(admin.ModelAdmin):
list_display = ('title', 'platform', 'priority', 'is_active', 'start_date', 'end_date')
list_filter = ('platform', 'priority', 'is_active', 'start_date')
search_fields = ('title', 'objective', 'tactics', 'actions')
readonly_fields = ('created_at', 'updated_at')
fieldsets = (
('Basic Information', {
'fields': ('platform', 'title', 'priority', 'is_active')
}),
('Strategy Details', {
'fields': ('objective', 'tactics', 'actions', 'control')
}),
('Timeline', {
'fields': ('start_date', 'end_date')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
})
)
actions = ['activate_strategies', 'deactivate_strategies']
def activate_strategies(self, request, queryset):
updated = queryset.update(is_active=True)
self.message_user(request, f'{updated} strategies activated.')
activate_strategies.short_description = 'Activate selected strategies'
def deactivate_strategies(self, request, queryset):
updated = queryset.update(is_active=False)
self.message_user(request, f'{updated} strategies deactivated.')
deactivate_strategies.short_description = 'Deactivate selected strategies'
# Customize admin site headers
admin.site.site_header = "Digital Branding Management System"
admin.site.site_title = "Branding Admin"
admin.site.index_title = "Welcome to Digital Branding Management"

6
dashboard/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class DashboardConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'dashboard'

View File

@ -0,0 +1 @@
# Management package

View File

@ -0,0 +1 @@
# Management commands package

View File

@ -0,0 +1,79 @@
# Generated by Django 5.2.6 on 2025-09-06 17:37
import datetime
import django.core.validators
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Platform',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text="Platform name (e.g., 'LinkedIn Business')", max_length=100)),
('platform_type', models.CharField(choices=[('linkedin', 'LinkedIn'), ('youtube', 'YouTube'), ('blog', 'Blog'), ('instagram', 'Instagram'), ('twitter', 'Twitter'), ('facebook', 'Facebook'), ('tiktok', 'TikTok'), ('podcast', 'Podcast'), ('newsletter', 'Newsletter'), ('other', 'Other')], max_length=20)),
('description', models.TextField(blank=True, help_text="Brief description of this platform's purpose")),
('status', models.CharField(choices=[('active', 'Active'), ('inactive', 'Inactive'), ('planning', 'Planning'), ('paused', 'Paused')], default='active', max_length=20)),
('primary_content_type', models.CharField(help_text="Main type of content for this platform (e.g., 'Articles', 'Videos', 'Posts')", max_length=100)),
('target_audience', models.CharField(blank=True, help_text='Target audience for this platform', max_length=200)),
('posting_frequency', models.CharField(blank=True, help_text="How often content is posted (e.g., 'Daily', '3x per week')", max_length=100)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'ordering': ['name'],
},
),
migrations.CreateModel(
name='ContentDeliverable',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(help_text='Content title or brief description', max_length=200)),
('content_type', models.CharField(choices=[('article', 'Article'), ('video', 'Video'), ('post', 'Social Media Post'), ('story', 'Story'), ('reel', 'Reel/Short Video'), ('carousel', 'Carousel'), ('infographic', 'Infographic'), ('podcast', 'Podcast Episode'), ('newsletter', 'Newsletter'), ('other', 'Other')], max_length=20)),
('status', models.CharField(choices=[('committed', 'Committed'), ('drafted', 'Drafted'), ('published', 'Published'), ('cancelled', 'Cancelled')], default='committed', max_length=20)),
('description', models.TextField(blank=True, help_text='Detailed description of the content')),
('completion_percentage', models.IntegerField(default=0, help_text='Completion progress (0-100%)', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100)])),
('target_date', models.DateField(blank=True, help_text='Target publication date', null=True)),
('actual_date', models.DateField(blank=True, help_text='Actual publication date', null=True)),
('url', models.URLField(blank=True, help_text='URL of published content')),
('notes', models.TextField(blank=True, help_text='Additional notes or comments')),
('tags', models.CharField(blank=True, help_text="Comma-separated tags (e.g., 'marketing, tutorial, beginner')", max_length=200)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('platform', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dashboard.platform')),
],
options={
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='Strategy',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(help_text='Strategy title', max_length=200)),
('objective', models.TextField(help_text='What you want to achieve')),
('tactics', models.TextField(help_text='How you plan to achieve the objective')),
('actions', models.TextField(help_text='Specific actions to implement the tactics')),
('control', models.TextField(blank=True, help_text="How you'll measure success and control/adjust the strategy")),
('priority', models.IntegerField(default=1, help_text='Priority level (1 = highest, 5 = lowest)', validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)])),
('start_date', models.DateField(default=datetime.date.today)),
('end_date', models.DateField(blank=True, null=True)),
('is_active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('platform', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='strategies', to='dashboard.platform')),
],
options={
'verbose_name_plural': 'strategies',
'ordering': ['priority', '-created_at'],
},
),
]

View File

181
dashboard/models.py Normal file
View File

@ -0,0 +1,181 @@
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.urls import reverse
from datetime import date
class Platform(models.Model):
"""Model representing different social media/content platforms"""
PLATFORM_TYPES = [
('linkedin', 'LinkedIn'),
('youtube', 'YouTube'),
('blog', 'Blog'),
('instagram', 'Instagram'),
('twitter', 'Twitter'),
('facebook', 'Facebook'),
('tiktok', 'TikTok'),
('podcast', 'Podcast'),
('newsletter', 'Newsletter'),
('other', 'Other'),
]
STATUS_CHOICES = [
('active', 'Active'),
('inactive', 'Inactive'),
('planning', 'Planning'),
('paused', 'Paused'),
]
name = models.CharField(max_length=100, help_text="Platform name (e.g., 'LinkedIn Business')")
platform_type = models.CharField(max_length=20, choices=PLATFORM_TYPES)
description = models.TextField(blank=True, help_text="Brief description of this platform's purpose")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
primary_content_type = models.CharField(
max_length=100,
help_text="Main type of content for this platform (e.g., 'Articles', 'Videos', 'Posts')"
)
target_audience = models.CharField(
max_length=200,
blank=True,
help_text="Target audience for this platform"
)
posting_frequency = models.CharField(
max_length=100,
blank=True,
help_text="How often content is posted (e.g., 'Daily', '3x per week')"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['name']
def __str__(self):
return f"{self.name} ({self.get_platform_type_display()})"
def get_absolute_url(self):
return reverse('platform-detail', args=[str(self.id)])
@property
def total_deliverables(self):
return self.contentdeliverable_set.count()
@property
def committed_count(self):
return self.contentdeliverable_set.filter(status='committed').count()
@property
def drafted_count(self):
return self.contentdeliverable_set.filter(status='drafted').count()
@property
def published_count(self):
return self.contentdeliverable_set.filter(status='published').count()
@property
def completion_rate(self):
total = self.total_deliverables
if total == 0:
return 0
published = self.published_count
return round((published / total) * 100, 1)
class Strategy(models.Model):
"""Model for platform strategies containing objectives, tactics, and actions"""
platform = models.ForeignKey(Platform, on_delete=models.CASCADE, related_name='strategies')
title = models.CharField(max_length=200, help_text="Strategy title")
objective = models.TextField(help_text="What you want to achieve")
tactics = models.TextField(help_text="How you plan to achieve the objective")
actions = models.TextField(help_text="Specific actions to implement the tactics")
control = models.TextField(
blank=True,
help_text="How you'll measure success and control/adjust the strategy"
)
priority = models.IntegerField(
default=1,
validators=[MinValueValidator(1), MaxValueValidator(5)],
help_text="Priority level (1 = highest, 5 = lowest)"
)
start_date = models.DateField(default=date.today)
end_date = models.DateField(blank=True, null=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['priority', '-created_at']
verbose_name_plural = 'strategies'
def __str__(self):
return f"{self.platform.name} - {self.title}"
class ContentDeliverable(models.Model):
"""Model for tracking content deliverables and their progress"""
STATUS_CHOICES = [
('committed', 'Committed'),
('drafted', 'Drafted'),
('published', 'Published'),
('cancelled', 'Cancelled'),
]
CONTENT_TYPES = [
('article', 'Article'),
('video', 'Video'),
('post', 'Social Media Post'),
('story', 'Story'),
('reel', 'Reel/Short Video'),
('carousel', 'Carousel'),
('infographic', 'Infographic'),
('podcast', 'Podcast Episode'),
('newsletter', 'Newsletter'),
('other', 'Other'),
]
platform = models.ForeignKey(Platform, on_delete=models.CASCADE)
title = models.CharField(max_length=200, help_text="Content title or brief description")
content_type = models.CharField(max_length=20, choices=CONTENT_TYPES)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='committed')
description = models.TextField(blank=True, help_text="Detailed description of the content")
completion_percentage = models.IntegerField(
default=0,
validators=[MinValueValidator(0), MaxValueValidator(100)],
help_text="Completion progress (0-100%)"
)
target_date = models.DateField(blank=True, null=True, help_text="Target publication date")
actual_date = models.DateField(blank=True, null=True, help_text="Actual publication date")
url = models.URLField(blank=True, help_text="URL of published content")
notes = models.TextField(blank=True, help_text="Additional notes or comments")
tags = models.CharField(
max_length=200,
blank=True,
help_text="Comma-separated tags (e.g., 'marketing, tutorial, beginner')"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return f"{self.platform.name} - {self.title} ({self.get_status_display()})"
def get_absolute_url(self):
return reverse('deliverable-detail', args=[str(self.id)])
@property
def is_overdue(self):
if self.target_date and self.status not in ['published', 'cancelled']:
return date.today() > self.target_date
return False
@property
def days_until_due(self):
if self.target_date:
delta = self.target_date - date.today()
return delta.days
return None

3
dashboard/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

7
dashboard/urls.py Normal file
View File

@ -0,0 +1,7 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.dashboard_view, name='dashboard'),
path('platform/<int:platform_id>/', views.platform_detail_view, name='platform-detail'),
]

122
dashboard/views.py Normal file
View File

@ -0,0 +1,122 @@
from django.shortcuts import render
from django.db import models
from django.db.models import Count, Q
from datetime import date
from .models import Platform, ContentDeliverable, Strategy
def dashboard_view(request):
"""Main dashboard view with statistics and platform cards"""
# Overall statistics
total_platforms = Platform.objects.filter(status='active').count()
total_deliverables = ContentDeliverable.objects.count()
committed_count = ContentDeliverable.objects.filter(status='committed').count()
drafted_count = ContentDeliverable.objects.filter(status='drafted').count()
published_count = ContentDeliverable.objects.filter(status='published').count()
# Calculate overall completion rate
if total_deliverables > 0:
overall_completion_rate = round((published_count / total_deliverables) * 100, 1)
else:
overall_completion_rate = 0
# Platform cards with detailed stats
platforms = Platform.objects.filter(status='active').prefetch_related(
'contentdeliverable_set', 'strategies'
).annotate(
total_content=Count('contentdeliverable'),
committed_content=Count('contentdeliverable', filter=Q(contentdeliverable__status='committed')),
drafted_content=Count('contentdeliverable', filter=Q(contentdeliverable__status='drafted')),
published_content=Count('contentdeliverable', filter=Q(contentdeliverable__status='published')),
total_strategies=Count('strategies', filter=Q(strategies__is_active=True))
)
# Add calculated fields to each platform
for platform in platforms:
if platform.total_content > 0:
platform.completion_percentage = round(
(platform.published_content / platform.total_content) * 100, 1
)
else:
platform.completion_percentage = 0
# Recent deliverables (last 10)
recent_deliverables = ContentDeliverable.objects.select_related(
'platform'
).order_by('-updated_at')[:10]
# Overdue deliverables
overdue_deliverables = ContentDeliverable.objects.filter(
target_date__lt=date.today(),
status__in=['committed', 'drafted']
).select_related('platform').order_by('target_date')
# Active strategies count
total_active_strategies = Strategy.objects.filter(is_active=True).count()
# Calculate percentages for progress bar
if total_deliverables > 0:
published_percentage = round((published_count / total_deliverables) * 100, 1)
drafted_percentage = round((drafted_count / total_deliverables) * 100, 1)
committed_percentage = round((committed_count / total_deliverables) * 100, 1)
else:
published_percentage = 0
drafted_percentage = 0
committed_percentage = 0
context = {
# Overall stats
'total_platforms': total_platforms,
'total_deliverables': total_deliverables,
'committed_count': committed_count,
'drafted_count': drafted_count,
'published_count': published_count,
'overall_completion_rate': overall_completion_rate,
'total_active_strategies': total_active_strategies,
# Platform data
'platforms': platforms,
# Recent activity
'recent_deliverables': recent_deliverables,
'overdue_deliverables': overdue_deliverables,
# Status for progress bars
'status_data': {
'committed': committed_count,
'drafted': drafted_count,
'published': published_count,
},
# Percentages for chart
'published_percentage': published_percentage,
'drafted_percentage': drafted_percentage,
'committed_percentage': committed_percentage,
}
return render(request, 'dashboard/dashboard.html', context)
def platform_detail_view(request, platform_id):
"""Detailed view for a specific platform"""
platform = Platform.objects.get(id=platform_id)
# Platform deliverables
deliverables = ContentDeliverable.objects.filter(
platform=platform
).order_by('-created_at')
# Platform strategies
strategies = Strategy.objects.filter(
platform=platform,
is_active=True
).order_by('priority')
context = {
'platform': platform,
'deliverables': deliverables,
'strategies': strategies,
}
return render(request, 'dashboard/platform_detail.html', context)

22
manage.py Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branding_system.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

66
templates/base.html Normal file
View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Digital Branding Management System{% endblock %}</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
'brand-blue': '#3B82F6',
'brand-green': '#10B981',
'brand-orange': '#F59E0B',
'brand-red': '#EF4444',
'brand-purple': '#8B5CF6',
}
}
}
}
</script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
{% block extra_css %}{% endblock %}
</head>
<body class="bg-gray-50 min-h-screen">
<!-- Navigation -->
<nav class="bg-white shadow-sm border-b border-gray-200">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<h1 class="text-xl font-semibold text-gray-900">
<i class="fas fa-chart-pie text-brand-blue mr-2"></i>
Digital Branding Management
</h1>
</div>
<div class="flex items-center space-x-4">
<a href="/" class="text-gray-700 hover:text-brand-blue px-3 py-2 rounded-md text-sm font-medium">
Dashboard
</a>
<a href="/admin/" class="text-gray-700 hover:text-brand-blue px-3 py-2 rounded-md text-sm font-medium">
Admin
</a>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
{% block content %}
{% endblock %}
</main>
<!-- Footer -->
<footer class="bg-white border-t border-gray-200 mt-12">
<div class="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8">
<p class="text-center text-sm text-gray-500">
&copy; 2024 Digital Branding Management System
</p>
</div>
</footer>
{% block extra_js %}{% endblock %}
</body>
</html>

View File

@ -0,0 +1,243 @@
{% extends "base.html" %}
{% block title %}Dashboard - Digital Branding Management{% endblock %}
{% block content %}
<div class="px-4 sm:px-6 lg:px-8">
<!-- Page Header -->
<div class="mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-2">Content Strategy Dashboard</h2>
<p class="text-gray-600">Monitor your content progress across all platforms</p>
</div>
<!-- Overall Statistics Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mb-8">
<!-- Total Platforms -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-globe text-2xl text-brand-blue"></i>
</div>
<div class="ml-4">
<p class="text-sm text-gray-500">Active Platforms</p>
<p class="text-2xl font-semibold text-gray-900">{{ total_platforms }}</p>
</div>
</div>
</div>
<!-- Committed -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-clipboard-list text-2xl text-brand-blue"></i>
</div>
<div class="ml-4">
<p class="text-sm text-gray-500">Committed</p>
<p class="text-2xl font-semibold text-brand-blue">{{ committed_count }}</p>
</div>
</div>
</div>
<!-- Drafted -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-edit text-2xl text-brand-orange"></i>
</div>
<div class="ml-4">
<p class="text-sm text-gray-500">Drafted</p>
<p class="text-2xl font-semibold text-brand-orange">{{ drafted_count }}</p>
</div>
</div>
</div>
<!-- Published -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-check-circle text-2xl text-brand-green"></i>
</div>
<div class="ml-4">
<p class="text-sm text-gray-500">Published</p>
<p class="text-2xl font-semibold text-brand-green">{{ published_count }}</p>
</div>
</div>
</div>
<!-- Completion Rate -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-chart-line text-2xl text-brand-purple"></i>
</div>
<div class="ml-4">
<p class="text-sm text-gray-500">Completion Rate</p>
<p class="text-2xl font-semibold text-brand-purple">{{ overall_completion_rate }}%</p>
</div>
</div>
</div>
</div>
<!-- Content by Status Chart -->
<div class="bg-white rounded-lg shadow p-6 mb-8">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Content Status Overview</h3>
<div class="w-full bg-gray-200 rounded-full h-6 mb-4">
{% if total_deliverables > 0 %}
<div class="bg-gray-200 rounded-full h-6 relative overflow-hidden">
<div class="bg-brand-green h-full absolute top-0 left-0" style="width: {{ published_percentage }}%"></div>
<div class="bg-brand-orange h-full absolute top-0" style="left: {{ published_percentage }}%; width: {{ drafted_percentage }}%"></div>
<div class="bg-brand-blue h-full absolute top-0" style="left: calc({{ published_percentage }}% + {{ drafted_percentage }}%); width: {{ committed_percentage }}%"></div>
</div>
{% endif %}
</div>
<div class="flex justify-between text-sm text-gray-600">
<span><i class="fas fa-circle text-brand-blue mr-1"></i>Committed ({{ committed_count }})</span>
<span><i class="fas fa-circle text-brand-orange mr-1"></i>Drafted ({{ drafted_count }})</span>
<span><i class="fas fa-circle text-brand-green mr-1"></i>Published ({{ published_count }})</span>
</div>
</div>
<!-- Platform Cards - Kanban Style -->
<div class="mb-8">
<h3 class="text-xl font-semibold text-gray-900 mb-6">Platform Progress</h3>
{% if platforms %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{% for platform in platforms %}
<div class="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-300 overflow-hidden">
<!-- Platform Header -->
<div class="px-6 py-4 bg-gradient-to-r from-brand-blue to-brand-purple">
<div class="flex items-center justify-between">
<h4 class="text-lg font-semibold text-white truncate">{{ platform.name }}</h4>
<span class="px-2 py-1 bg-white bg-opacity-20 rounded-full text-xs text-white">
{{ platform.get_platform_type_display }}
</span>
</div>
<p class="text-sm text-blue-100 mt-1">{{ platform.primary_content_type }}</p>
</div>
<!-- Platform Stats -->
<div class="px-6 py-4">
<div class="flex justify-between items-center mb-4">
<span class="text-sm text-gray-500">Content Progress</span>
<span class="text-sm font-semibold text-gray-900">{{ platform.completion_percentage }}%</span>
</div>
<!-- Progress Bar -->
<div class="w-full bg-gray-200 rounded-full h-2 mb-4">
<div class="bg-gradient-to-r from-brand-green to-green-400 h-2 rounded-full"
style="width: {{ platform.completion_percentage }}%"></div>
</div>
<!-- Content Status Breakdown -->
<div class="grid grid-cols-3 gap-3 mb-4">
<div class="text-center">
<div class="text-lg font-semibold text-brand-blue">{{ platform.committed_content }}</div>
<div class="text-xs text-gray-500">Committed</div>
</div>
<div class="text-center">
<div class="text-lg font-semibold text-brand-orange">{{ platform.drafted_content }}</div>
<div class="text-xs text-gray-500">Drafted</div>
</div>
<div class="text-center">
<div class="text-lg font-semibold text-brand-green">{{ platform.published_content }}</div>
<div class="text-xs text-gray-500">Published</div>
</div>
</div>
<!-- Additional Info -->
<div class="flex justify-between items-center text-sm text-gray-500 mb-4">
<span><i class="fas fa-list-ul mr-1"></i>Total: {{ platform.total_content }}</span>
<span><i class="fas fa-lightbulb mr-1"></i>{{ platform.total_strategies }} strategies</span>
</div>
<!-- Action Button -->
<a href="{% url 'platform-detail' platform.id %}"
class="w-full bg-brand-blue text-white py-2 px-4 rounded-md text-sm font-medium hover:bg-blue-700 transition duration-200 flex items-center justify-center">
<i class="fas fa-eye mr-2"></i>View Details
</a>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-12 bg-white rounded-lg shadow">
<i class="fas fa-plus-circle text-4xl text-gray-400 mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 mb-2">No platforms yet</h3>
<p class="text-gray-500 mb-4">Get started by adding your first platform</p>
<a href="/admin/dashboard/platform/add/" class="bg-brand-blue text-white py-2 px-4 rounded-md text-sm font-medium hover:bg-blue-700">
Add Platform
</a>
</div>
{% endif %}
</div>
<!-- Recent Activity & Alerts -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Recent Deliverables -->
<div class="bg-white rounded-lg shadow">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-900">Recent Activity</h3>
</div>
<div class="divide-y divide-gray-200">
{% for deliverable in recent_deliverables|slice:":5" %}
<div class="px-6 py-4">
<div class="flex items-center justify-between">
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 truncate">{{ deliverable.title }}</p>
<p class="text-sm text-gray-500">{{ deliverable.platform.name }} • {{ deliverable.get_content_type_display }}</p>
</div>
<div class="flex items-center">
<span class="px-2 py-1 text-xs rounded-full
{% if deliverable.status == 'published' %}bg-green-100 text-green-800
{% elif deliverable.status == 'drafted' %}bg-orange-100 text-orange-800
{% elif deliverable.status == 'committed' %}bg-blue-100 text-blue-800
{% else %}bg-gray-100 text-gray-800{% endif %}">
{{ deliverable.get_status_display }}
</span>
</div>
</div>
</div>
{% empty %}
<div class="px-6 py-8 text-center text-gray-500">
No recent activity
</div>
{% endfor %}
</div>
</div>
<!-- Overdue & Alerts -->
<div class="bg-white rounded-lg shadow">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-900">Alerts & Overdue</h3>
</div>
<div class="divide-y divide-gray-200">
{% for deliverable in overdue_deliverables|slice:":5" %}
<div class="px-6 py-4">
<div class="flex items-start">
<i class="fas fa-exclamation-triangle text-red-500 mt-1 mr-3"></i>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900">{{ deliverable.title }}</p>
<p class="text-sm text-gray-500">{{ deliverable.platform.name }}</p>
<p class="text-xs text-red-600 mt-1">Due: {{ deliverable.target_date }}</p>
</div>
</div>
</div>
{% empty %}
<div class="px-6 py-8 text-center text-gray-500">
<i class="fas fa-check-circle text-green-500 text-2xl mb-2"></i>
<p>All caught up! No overdue items</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Add any dashboard-specific JavaScript here
console.log('Dashboard loaded');
</script>
{% endblock %}