Initial release: DictIA v0.8.14-alpha (fork de Speakr, AGPL-3.0)
This commit is contained in:
173
src/api/events.py
Normal file
173
src/api/events.py
Normal file
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
Calendar event extraction and export.
|
||||
|
||||
This blueprint was auto-generated from app.py route extraction.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, send_file, Response, current_app, make_response
|
||||
from flask_login import login_required, current_user
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from src.database import db
|
||||
from src.models import *
|
||||
from src.utils import *
|
||||
from src.services.calendar import generate_ics_content
|
||||
|
||||
# Create blueprint
|
||||
events_bp = Blueprint('events', __name__)
|
||||
|
||||
# Configuration from environment
|
||||
ENABLE_INQUIRE_MODE = os.environ.get('ENABLE_INQUIRE_MODE', 'false').lower() == 'true'
|
||||
ENABLE_AUTO_DELETION = os.environ.get('ENABLE_AUTO_DELETION', 'false').lower() == 'true'
|
||||
USERS_CAN_DELETE = os.environ.get('USERS_CAN_DELETE', 'true').lower() == 'true'
|
||||
ENABLE_INTERNAL_SHARING = os.environ.get('ENABLE_INTERNAL_SHARING', 'false').lower() == 'true'
|
||||
USE_ASR_ENDPOINT = os.environ.get('USE_ASR_ENDPOINT', 'false').lower() == 'true'
|
||||
|
||||
# Global helpers (will be injected from app)
|
||||
has_recording_access = None
|
||||
bcrypt = None
|
||||
csrf = None
|
||||
limiter = None
|
||||
|
||||
def init_events_helpers(**kwargs):
|
||||
"""Initialize helper functions and extensions from app."""
|
||||
global has_recording_access, bcrypt, csrf, limiter
|
||||
has_recording_access = kwargs.get('has_recording_access')
|
||||
bcrypt = kwargs.get('bcrypt')
|
||||
csrf = kwargs.get('csrf')
|
||||
limiter = kwargs.get('limiter')
|
||||
|
||||
|
||||
# --- Routes ---
|
||||
|
||||
@events_bp.route('/api/recording/<int:recording_id>/events', methods=['GET'])
|
||||
@login_required
|
||||
def get_recording_events(recording_id):
|
||||
"""Get all events extracted from a recording."""
|
||||
try:
|
||||
recording = db.session.get(Recording, recording_id)
|
||||
if not recording:
|
||||
return jsonify({'error': 'Recording not found'}), 404
|
||||
|
||||
if not has_recording_access(recording, current_user):
|
||||
return jsonify({'error': 'Unauthorized'}), 403
|
||||
|
||||
events = Event.query.filter_by(recording_id=recording_id).all()
|
||||
return jsonify({'events': [event.to_dict() for event in events]})
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Error fetching events for recording {recording_id}: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@events_bp.route('/api/event/<int:event_id>/ics', methods=['GET'])
|
||||
@login_required
|
||||
def download_event_ics(event_id):
|
||||
"""Generate and download an ICS file for a single event."""
|
||||
try:
|
||||
event = db.session.get(Event, event_id)
|
||||
if not event:
|
||||
return jsonify({'error': 'Event not found'}), 404
|
||||
|
||||
# Check permissions through recording access
|
||||
if not has_recording_access(event.recording, current_user):
|
||||
return jsonify({'error': 'Unauthorized'}), 403
|
||||
|
||||
# Generate ICS content
|
||||
ics_content = generate_ics_content(event)
|
||||
|
||||
# Create response with ICS file
|
||||
response = make_response(ics_content)
|
||||
response.headers['Content-Type'] = 'text/calendar; charset=utf-8'
|
||||
response.headers['Content-Disposition'] = f'attachment; filename="{secure_filename(event.title)}.ics"'
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Error generating ICS for event {event_id}: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@events_bp.route('/api/recording/<int:recording_id>/events/ics', methods=['GET'])
|
||||
@login_required
|
||||
def download_all_events_ics(recording_id):
|
||||
"""Generate and download an ICS file containing all events from a recording."""
|
||||
try:
|
||||
recording = db.session.get(Recording, recording_id)
|
||||
if not recording:
|
||||
return jsonify({'error': 'Recording not found'}), 404
|
||||
|
||||
if not has_recording_access(recording, current_user):
|
||||
return jsonify({'error': 'Unauthorized'}), 403
|
||||
|
||||
# Get all events for this recording
|
||||
events = Event.query.filter_by(recording_id=recording_id).all()
|
||||
if not events:
|
||||
return jsonify({'error': 'No events found for this recording'}), 404
|
||||
|
||||
# Generate combined ICS content
|
||||
ics_lines = []
|
||||
ics_lines.append("BEGIN:VCALENDAR")
|
||||
ics_lines.append("VERSION:2.0")
|
||||
ics_lines.append("PRODID:-//Speakr//Event Export//EN")
|
||||
ics_lines.append("CALSCALE:GREGORIAN")
|
||||
ics_lines.append("METHOD:PUBLISH")
|
||||
|
||||
# Add each event
|
||||
for event in events:
|
||||
# Get the individual event's ICS content and extract just the VEVENT portion
|
||||
individual_ics = generate_ics_content(event)
|
||||
# Extract VEVENT block from individual ICS
|
||||
lines = individual_ics.split('\n')
|
||||
in_event = False
|
||||
for line in lines:
|
||||
if line.startswith('BEGIN:VEVENT'):
|
||||
in_event = True
|
||||
if in_event:
|
||||
ics_lines.append(line)
|
||||
if line.startswith('END:VEVENT'):
|
||||
in_event = False
|
||||
|
||||
ics_lines.append("END:VCALENDAR")
|
||||
ics_content = '\r\n'.join(ics_lines)
|
||||
|
||||
# Create response with ICS file
|
||||
response = make_response(ics_content)
|
||||
response.headers['Content-Type'] = 'text/calendar; charset=utf-8'
|
||||
safe_title = secure_filename(recording.title) if recording.title else f'recording-{recording_id}'
|
||||
response.headers['Content-Disposition'] = f'attachment; filename="{safe_title}-events.ics"'
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Error generating ICS for all events in recording {recording_id}: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@events_bp.route('/api/event/<int:event_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def delete_event(event_id):
|
||||
"""Delete a single event."""
|
||||
try:
|
||||
event = db.session.get(Event, event_id)
|
||||
if not event:
|
||||
return jsonify({'error': 'Event not found'}), 404
|
||||
|
||||
# Check permissions through recording access
|
||||
if not has_recording_access(event.recording, current_user):
|
||||
return jsonify({'error': 'Unauthorized'}), 403
|
||||
|
||||
db.session.delete(event)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({'success': True})
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Error deleting event {event_id}: {e}")
|
||||
db.session.rollback()
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
Reference in New Issue
Block a user