Files
ALwrity/ToBeMigrated/content_scheduler/ui/dashboard.py
2025-08-06 16:29:49 +05:30

386 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Main dashboard implementation for the Content Scheduler.
"""
import streamlit as st
import pandas as pd
from datetime import datetime, timedelta
from typing import List, Dict, Any
import plotly.express as px
import plotly.graph_objects as go
from lib.database.models import ContentItem, Schedule, ScheduleStatus, ContentType, Platform, get_engine, get_session, init_db
engine = get_engine()
init_db(engine)
session = get_session(engine)
def run_dashboard():
"""Run the Streamlit dashboard."""
st.title("📅 Alwrity Content Scheduler Dashboard")
# Sidebar navigation
st.sidebar.title("Navigation")
page = st.sidebar.radio(
"Go to",
["Overview", "Schedule Management", "Create Schedule", "Job Monitor", "Analytics"]
)
if page == "Overview":
show_overview()
elif page == "Schedule Management":
show_schedule_management()
elif page == "Create Schedule":
show_create_schedule()
elif page == "Job Monitor":
show_job_monitor()
else:
show_analytics()
def show_overview():
"""Display the overview dashboard."""
st.header("📊 Overview")
# Get data from unified database
all_content = session.query(ContentItem).all()
all_schedules = session.query(Schedule).all()
# Display metrics
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Total Content Items", len(all_content))
with col2:
scheduled_count = len([s for s in all_schedules if s.status == ScheduleStatus.SCHEDULED])
st.metric("Scheduled Items", scheduled_count)
with col3:
completed_count = len([s for s in all_schedules if s.status == ScheduleStatus.COMPLETED])
st.metric("Completed", completed_count)
with col4:
failed_count = len([s for s in all_schedules if s.status == ScheduleStatus.FAILED])
st.metric("Failed", failed_count)
# Recent content
st.subheader("📝 Recent Content Items")
if all_content:
recent_content = sorted(all_content, key=lambda x: x.created_at, reverse=True)[:5]
for item in recent_content:
with st.expander(f"{item.title} ({item.content_type.value})"):
st.write(f"**Description:** {item.description or 'No description'}")
st.write(f"**Platforms:** {', '.join(item.platforms) if isinstance(item.platforms, list) else item.platforms}")
st.write(f"**Status:** {item.status}")
st.write(f"**Created:** {item.created_at}")
# Show associated schedules
item_schedules = [s for s in all_schedules if s.content_item_id == item.id]
if item_schedules:
st.write("**Schedules:**")
for schedule in item_schedules:
st.write(f" - {schedule.scheduled_time} ({schedule.status.value})")
else:
st.info("No content items found. Create some content in the Content Calendar first!")
def show_schedule_management():
"""Display the schedule management interface."""
st.header("📅 Schedule Management")
# Get all schedules
all_schedules = session.query(Schedule).all()
if not all_schedules:
st.info("No schedules found. Create schedules from the 'Create Schedule' tab.")
return
# Filter options
col1, col2 = st.columns(2)
with col1:
status_filter = st.selectbox(
"Filter by Status",
options=["All"] + [status.value for status in ScheduleStatus],
key="schedule_status_filter"
)
with col2:
date_filter = st.date_input(
"Filter by Date (from)",
value=datetime.now().date() - timedelta(days=30),
key="schedule_date_filter"
)
# Apply filters
filtered_schedules = all_schedules
if status_filter != "All":
filtered_schedules = [s for s in filtered_schedules if s.status.value == status_filter]
filtered_schedules = [s for s in filtered_schedules if s.scheduled_time.date() >= date_filter]
# Display schedules
st.subheader(f"📋 Schedules ({len(filtered_schedules)} items)")
for schedule in sorted(filtered_schedules, key=lambda x: x.scheduled_time, reverse=True):
content_item = session.query(ContentItem).get(schedule.content_item_id)
if content_item:
with st.expander(f"{content_item.title} - {schedule.scheduled_time.strftime('%Y-%m-%d %H:%M')} ({schedule.status.value})"):
col1, col2 = st.columns(2)
with col1:
st.write(f"**Content:** {content_item.title}")
st.write(f"**Type:** {content_item.content_type.value}")
st.write(f"**Platforms:** {', '.join(content_item.platforms) if isinstance(content_item.platforms, list) else content_item.platforms}")
st.write(f"**Scheduled Time:** {schedule.scheduled_time}")
st.write(f"**Status:** {schedule.status.value}")
with col2:
st.write(f"**Recurrence:** {schedule.recurrence or 'One-time'}")
st.write(f"**Priority:** {schedule.priority}")
st.write(f"**Created:** {schedule.created_at}")
if schedule.result:
st.write(f"**Result:** {schedule.result}")
# Action buttons
col1, col2, col3 = st.columns(3)
with col1:
if st.button(f"Edit Schedule", key=f"edit_{schedule.id}"):
st.session_state.edit_schedule_id = schedule.id
st.rerun()
with col2:
if schedule.status == ScheduleStatus.SCHEDULED:
if st.button(f"Cancel", key=f"cancel_{schedule.id}"):
schedule.status = ScheduleStatus.CANCELLED
session.commit()
st.success("Schedule cancelled!")
st.rerun()
with col3:
if st.button(f"Delete", key=f"delete_{schedule.id}"):
session.delete(schedule)
session.commit()
st.success("Schedule deleted!")
st.rerun()
def show_create_schedule():
"""Display the schedule creation interface."""
st.header(" Create New Schedule")
# Get available content items
content_items = session.query(ContentItem).all()
if not content_items:
st.warning("No content items available. Please create content in the Content Calendar first.")
return
# Create schedule form
with st.form("create_schedule_form"):
st.subheader("Schedule Configuration")
# Select content item
content_options = {f"{item.title} ({item.content_type.value})": item.id for item in content_items}
selected_content = st.selectbox(
"Select Content Item",
options=list(content_options.keys()),
key="schedule_content_select"
)
# Schedule timing
col1, col2 = st.columns(2)
with col1:
schedule_date = st.date_input(
"Schedule Date",
value=datetime.now().date() + timedelta(days=1),
key="schedule_date"
)
with col2:
schedule_time = st.time_input(
"Schedule Time",
value=datetime.now().time(),
key="schedule_time"
)
# Combine date and time
schedule_datetime = datetime.combine(schedule_date, schedule_time)
# Recurrence options
recurrence = st.selectbox(
"Recurrence",
options=["none", "daily", "weekly", "monthly"],
key="schedule_recurrence"
)
# Priority
priority = st.slider(
"Priority",
min_value=1,
max_value=10,
value=5,
key="schedule_priority"
)
# Platform selection (override content item platforms if needed)
content_item_id = content_options[selected_content]
content_item = session.query(ContentItem).get(content_item_id)
if content_item:
current_platforms = content_item.platforms if isinstance(content_item.platforms, list) else [content_item.platforms]
st.write(f"**Current Platforms:** {', '.join(current_platforms)}")
override_platforms = st.checkbox("Override Platforms", key="override_platforms")
if override_platforms:
available_platforms = [p.value for p in Platform]
selected_platforms = st.multiselect(
"Select Platforms",
options=available_platforms,
default=current_platforms,
key="schedule_platforms"
)
else:
selected_platforms = current_platforms
# Submit button
submitted = st.form_submit_button("Create Schedule")
if submitted:
try:
# Create new schedule
new_schedule = Schedule(
content_item_id=content_item_id,
scheduled_time=schedule_datetime,
status=ScheduleStatus.SCHEDULED,
recurrence=recurrence if recurrence != "none" else None,
priority=priority
)
session.add(new_schedule)
session.commit()
st.success(f"✅ Schedule created successfully! Content will be published on {schedule_datetime}")
# Show schedule details
with st.expander("Schedule Details", expanded=True):
st.write(f"**Content:** {content_item.title}")
st.write(f"**Scheduled Time:** {schedule_datetime}")
st.write(f"**Platforms:** {', '.join(selected_platforms)}")
st.write(f"**Recurrence:** {recurrence}")
st.write(f"**Priority:** {priority}")
except Exception as e:
st.error(f"❌ Error creating schedule: {str(e)}")
def show_job_monitor():
"""Display the job monitoring interface."""
st.header("🔍 Job Monitor")
# Get all schedules with their status
all_schedules = session.query(Schedule).all()
if not all_schedules:
st.info("No jobs to monitor.")
return
# Status distribution
status_counts = {}
for schedule in all_schedules:
status = schedule.status.value
status_counts[status] = status_counts.get(status, 0) + 1
# Display status chart
if status_counts:
fig = px.pie(
values=list(status_counts.values()),
names=list(status_counts.keys()),
title="Job Status Distribution"
)
st.plotly_chart(fig, use_container_width=True)
# Recent job activity
st.subheader("📊 Recent Job Activity")
recent_schedules = sorted(all_schedules, key=lambda x: x.updated_at, reverse=True)[:10]
for schedule in recent_schedules:
content_item = session.query(ContentItem).get(schedule.content_item_id)
if content_item:
status_color = {
ScheduleStatus.SCHEDULED: "🟡",
ScheduleStatus.RUNNING: "🔵",
ScheduleStatus.COMPLETED: "🟢",
ScheduleStatus.FAILED: "🔴",
ScheduleStatus.CANCELLED: ""
}.get(schedule.status, "")
st.write(f"{status_color} **{content_item.title}** - {schedule.status.value} - {schedule.updated_at.strftime('%Y-%m-%d %H:%M')}")
if schedule.result:
st.write(f" └─ {schedule.result}")
def show_analytics():
"""Display the analytics dashboard."""
st.header("📈 Analytics")
# Get data
all_content = session.query(ContentItem).all()
all_schedules = session.query(Schedule).all()
if not all_schedules:
st.info("No data available for analytics.")
return
# Time-based analytics
st.subheader("📅 Schedule Timeline")
# Create timeline data
timeline_data = []
for schedule in all_schedules:
content_item = session.query(ContentItem).get(schedule.content_item_id)
if content_item:
timeline_data.append({
'Date': schedule.scheduled_time.date(),
'Content': content_item.title,
'Status': schedule.status.value,
'Type': content_item.content_type.value
})
if timeline_data:
df = pd.DataFrame(timeline_data)
# Schedule frequency by date
date_counts = df.groupby('Date').size().reset_index(name='Count')
fig = px.line(date_counts, x='Date', y='Count', title='Scheduled Content Over Time')
st.plotly_chart(fig, use_container_width=True)
# Content type distribution
type_counts = df['Type'].value_counts()
fig = px.bar(x=type_counts.index, y=type_counts.values, title='Content Type Distribution')
st.plotly_chart(fig, use_container_width=True)
# Status breakdown
status_counts = df['Status'].value_counts()
fig = px.pie(values=status_counts.values, names=status_counts.index, title='Status Distribution')
st.plotly_chart(fig, use_container_width=True)
# Performance metrics
st.subheader("📊 Performance Metrics")
col1, col2, col3 = st.columns(3)
with col1:
total_schedules = len(all_schedules)
st.metric("Total Schedules", total_schedules)
with col2:
completed_schedules = len([s for s in all_schedules if s.status == ScheduleStatus.COMPLETED])
success_rate = (completed_schedules / total_schedules * 100) if total_schedules > 0 else 0
st.metric("Success Rate", f"{success_rate:.1f}%")
with col3:
failed_schedules = len([s for s in all_schedules if s.status == ScheduleStatus.FAILED])
failure_rate = (failed_schedules / total_schedules * 100) if total_schedules > 0 else 0
st.metric("Failure Rate", f"{failure_rate:.1f}%")