Merges ac-helper (PHP Activation Calendar) and brief-extractor (Python AI) into a single Docker app with React/TypeScript frontend. Features: - Brief upload → AI extraction → review → Activation Calendar import - Handsontable v17 spreadsheet with dependent dropdowns (148 categories) - AI natural language commands via Gemini (YOLO mode, voice input) - Azure AD MSAL SPA PKCE authentication, user roles (user/admin) - CSV Activation Calendar export - Real-time WebSocket job progress - Admin: user management, dropdown Excel upload - Multi-stage Dockerfile, docker-compose, nginx proxy instructions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
62 lines
2 KiB
Python
62 lines
2 KiB
Python
"""
|
|
CSV export — Activation Calendar format.
|
|
Mirrors the export logic from script.js in ac-helper.
|
|
"""
|
|
|
|
import csv
|
|
import io
|
|
import logging
|
|
|
|
from quart import Blueprint, make_response
|
|
|
|
from ..auth.middleware import auth_required, get_user_id
|
|
from ..sheets.manager import load_sheet_data
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
export_bp = Blueprint('export', __name__, url_prefix='/api/sheets')
|
|
|
|
# Activation Calendar column order
|
|
AC_HEADERS = [
|
|
'Number', 'Title', 'Status', 'Category', 'Media', 'Sub media',
|
|
'Destination', 'Format', 'Supply date', 'Live date', 'End date',
|
|
'Special instructions', 'Language', 'Country', 'Quantity',
|
|
]
|
|
|
|
|
|
@export_bp.route('/<sheet_id>/export', methods=['GET'])
|
|
@auth_required
|
|
async def export_csv(sheet_id: str):
|
|
user_id = get_user_id()
|
|
data = load_sheet_data(user_id, sheet_id)
|
|
if data is None:
|
|
return {'error': 'not_found'}, 404
|
|
|
|
output = io.StringIO()
|
|
writer = csv.DictWriter(output, fieldnames=AC_HEADERS, extrasaction='ignore')
|
|
writer.writeheader()
|
|
|
|
for row in data:
|
|
writer.writerow({
|
|
'Number': '', # cleared on export
|
|
'Title': row.get('Title', ''),
|
|
'Status': row.get('Status', ''),
|
|
'Category': row.get('Category', ''),
|
|
'Media': row.get('Media', ''),
|
|
'Sub media': row.get('Sub-media', ''),
|
|
'Destination': '',
|
|
'Format': row.get('Format', ''),
|
|
'Supply date': row.get('Supply date', ''),
|
|
'Live date': row.get('Live date', ''),
|
|
'End date': '',
|
|
'Special instructions': '',
|
|
'Language': row.get('Language', ''),
|
|
'Country': row.get('Country', ''),
|
|
'Quantity': '1.00',
|
|
})
|
|
|
|
csv_content = output.getvalue()
|
|
response = await make_response(csv_content)
|
|
response.headers['Content-Type'] = 'text/csv'
|
|
response.headers['Content-Disposition'] = f'attachment; filename="activation_calendar_{sheet_id}.csv"'
|
|
return response
|