account, schduels, timezone profile and many imporant updates
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# Generated by Django 5.2.9 on 2026-01-19 00:00
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def add_team_invite_template(apps, schema_editor):
|
||||
EmailTemplate = apps.get_model('system', 'EmailTemplate')
|
||||
|
||||
EmailTemplate.objects.get_or_create(
|
||||
template_name='team_invite',
|
||||
defaults={
|
||||
'template_path': 'emails/team_invite.html',
|
||||
'display_name': 'Team Invitation',
|
||||
'description': 'Sent when a team member is invited to join an account',
|
||||
'template_type': 'auth',
|
||||
'default_subject': "You're invited to join IGNY8",
|
||||
'required_context': ['inviter_name', 'invited_name', 'account_name', 'reset_url', 'frontend_url'],
|
||||
'sample_context': {
|
||||
'inviter_name': 'Alex Johnson',
|
||||
'invited_name': 'Jamie Lee',
|
||||
'account_name': 'Acme Co',
|
||||
'reset_url': 'https://app.igny8.com/reset-password?token=example',
|
||||
'frontend_url': 'https://app.igny8.com',
|
||||
},
|
||||
'is_active': True,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def remove_team_invite_template(apps, schema_editor):
|
||||
EmailTemplate = apps.get_model('system', 'EmailTemplate')
|
||||
EmailTemplate.objects.filter(template_name='team_invite').delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0025_delete_accountintegrationoverride'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(add_team_invite_template, remove_team_invite_template),
|
||||
]
|
||||
@@ -1595,8 +1595,9 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
}
|
||||
"""
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from igny8_core.business.integration.models import Site, SitePublishingSettings
|
||||
from igny8_core.auth.models import Site
|
||||
from igny8_core.business.integration.models import PublishingSettings
|
||||
from igny8_core.tasks.publishing_scheduler import _calculate_available_slots
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -1621,7 +1622,7 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
# Get site and publishing settings
|
||||
try:
|
||||
site = Site.objects.get(id=site_id)
|
||||
pub_settings = SitePublishingSettings.objects.filter(site=site).first()
|
||||
pub_settings, _ = PublishingSettings.get_or_create_for_site(site)
|
||||
except Site.DoesNotExist:
|
||||
return error_response(
|
||||
error=f'Site {site_id} not found',
|
||||
@@ -1629,41 +1630,25 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
request=request
|
||||
)
|
||||
|
||||
# Default settings if none exist
|
||||
base_time_str = '09:00 AM'
|
||||
stagger_interval = 15 # minutes
|
||||
timezone_str = 'America/New_York'
|
||||
|
||||
if pub_settings:
|
||||
base_time_str = pub_settings.auto_publish_time or base_time_str
|
||||
stagger_interval = pub_settings.stagger_interval_minutes or stagger_interval
|
||||
timezone_str = pub_settings.timezone or timezone_str
|
||||
|
||||
# Get content items
|
||||
content_qs = self.get_queryset().filter(id__in=content_ids)
|
||||
|
||||
# Generate schedule preview
|
||||
# Generate schedule preview using the same slot logic as automation
|
||||
schedule_preview = []
|
||||
now = timezone.now()
|
||||
|
||||
# Parse base time (format: "09:00 AM" or "14:30")
|
||||
try:
|
||||
from datetime import datetime
|
||||
if 'AM' in base_time_str or 'PM' in base_time_str:
|
||||
time_obj = datetime.strptime(base_time_str, '%I:%M %p').time()
|
||||
else:
|
||||
time_obj = datetime.strptime(base_time_str, '%H:%M').time()
|
||||
except ValueError:
|
||||
time_obj = datetime.strptime('09:00', '%H:%M').time()
|
||||
|
||||
# Start from tomorrow at base time
|
||||
start_date = now.replace(hour=time_obj.hour, minute=time_obj.minute, second=0, microsecond=0)
|
||||
if start_date <= now:
|
||||
start_date += timedelta(days=1)
|
||||
|
||||
available_slots = _calculate_available_slots(pub_settings, site)
|
||||
|
||||
if not available_slots:
|
||||
return error_response(
|
||||
error='No available publishing slots based on site defaults',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
# Create schedule for each content item
|
||||
for index, content in enumerate(content_qs):
|
||||
scheduled_at = start_date + timedelta(minutes=stagger_interval * index)
|
||||
if index >= len(available_slots):
|
||||
break
|
||||
scheduled_at = available_slots[index]
|
||||
schedule_preview.append({
|
||||
'content_id': content.id,
|
||||
'title': content.title,
|
||||
@@ -1671,15 +1656,25 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
})
|
||||
|
||||
logger.info(f"[bulk_schedule_preview] Generated preview for {len(schedule_preview)} items")
|
||||
|
||||
# Base time for display should match first scheduled slot
|
||||
display_base_time = '09:00'
|
||||
if schedule_preview:
|
||||
try:
|
||||
from datetime import datetime
|
||||
first_dt = datetime.fromisoformat(schedule_preview[0]['scheduled_at'])
|
||||
display_base_time = first_dt.strftime('%H:%M')
|
||||
except Exception:
|
||||
display_base_time = '09:00'
|
||||
|
||||
return success_response(
|
||||
data={
|
||||
'scheduled_count': len(schedule_preview),
|
||||
'schedule_preview': schedule_preview,
|
||||
'site_settings': {
|
||||
'base_time': base_time_str,
|
||||
'stagger_interval': stagger_interval,
|
||||
'timezone': timezone_str,
|
||||
'base_time': display_base_time,
|
||||
'stagger_interval': pub_settings.stagger_interval_minutes,
|
||||
'timezone': site.account.account_timezone if hasattr(site, 'account') else 'UTC',
|
||||
},
|
||||
},
|
||||
message=f'Preview generated for {len(schedule_preview)} items',
|
||||
@@ -1699,8 +1694,9 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
}
|
||||
"""
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from igny8_core.business.integration.models import Site, SitePublishingSettings
|
||||
from igny8_core.auth.models import Site
|
||||
from igny8_core.business.integration.models import PublishingSettings
|
||||
from igny8_core.tasks.publishing_scheduler import _calculate_available_slots
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -1726,7 +1722,7 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
# Get site and publishing settings
|
||||
try:
|
||||
site = Site.objects.get(id=site_id)
|
||||
pub_settings = SitePublishingSettings.objects.filter(site=site).first()
|
||||
pub_settings, _ = PublishingSettings.get_or_create_for_site(site)
|
||||
except Site.DoesNotExist:
|
||||
return error_response(
|
||||
error=f'Site {site_id} not found',
|
||||
@@ -1734,39 +1730,28 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
request=request
|
||||
)
|
||||
|
||||
# Default settings if none exist
|
||||
base_time_str = '09:00 AM'
|
||||
stagger_interval = 15 # minutes
|
||||
|
||||
if pub_settings and use_site_defaults:
|
||||
base_time_str = pub_settings.auto_publish_time or base_time_str
|
||||
stagger_interval = pub_settings.stagger_interval_minutes or stagger_interval
|
||||
|
||||
# Get content items
|
||||
content_qs = self.get_queryset().filter(id__in=content_ids)
|
||||
|
||||
# Generate schedule and apply
|
||||
# Generate schedule and apply using the same slot logic as automation
|
||||
available_slots = _calculate_available_slots(pub_settings, site) if use_site_defaults else []
|
||||
|
||||
if use_site_defaults and not available_slots:
|
||||
return error_response(
|
||||
error='No available publishing slots based on site defaults',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
now = timezone.now()
|
||||
|
||||
# Parse base time
|
||||
try:
|
||||
from datetime import datetime
|
||||
if 'AM' in base_time_str or 'PM' in base_time_str:
|
||||
time_obj = datetime.strptime(base_time_str, '%I:%M %p').time()
|
||||
else:
|
||||
time_obj = datetime.strptime(base_time_str, '%H:%M').time()
|
||||
except ValueError:
|
||||
time_obj = datetime.strptime('09:00', '%H:%M').time()
|
||||
|
||||
# Start from tomorrow at base time
|
||||
start_date = now.replace(hour=time_obj.hour, minute=time_obj.minute, second=0, microsecond=0)
|
||||
if start_date <= now:
|
||||
start_date += timedelta(days=1)
|
||||
|
||||
# Schedule each content item
|
||||
scheduled_count = 0
|
||||
for index, content in enumerate(content_qs):
|
||||
scheduled_at = start_date + timedelta(minutes=stagger_interval * index)
|
||||
if use_site_defaults:
|
||||
if index >= len(available_slots):
|
||||
break
|
||||
scheduled_at = available_slots[index]
|
||||
else:
|
||||
scheduled_at = now
|
||||
content.site_status = 'scheduled'
|
||||
content.scheduled_publish_at = scheduled_at
|
||||
content.site_status_updated_at = now
|
||||
|
||||
Reference in New Issue
Block a user