function generate_minutes
Flask route handler that processes uploaded meeting transcripts and optional supporting documents to generate structured meeting minutes using AI, with configurable output styles and validation.
/tf/active/vicechatdev/leexi/app.py
136 - 294
complex
Purpose
This function serves as the main endpoint for a web application that generates meeting minutes. It handles file uploads (transcript, PowerPoint, previous reports), validates inputs, processes documents through an AI-powered generator with customizable parameters (rigor level, detail level, action focus, output style), handles potential output truncation, validates the generated content, saves the results, and returns both HTML and Markdown formatted minutes. It's designed for teams needing automated, consistent meeting documentation with quality checks.
Source Code
def generate_minutes():
"""Generate meeting minutes based on form input"""
try:
# Get form data
meeting_title = request.form.get('meeting_title', 'Development Team Meeting')
model = request.form.get('model', 'gpt-4o')
user_instructions = request.form.get('user_instructions', '')
# Get configuration options
rigor_level = request.form.get('rigor_level', 'balanced')
detail_level = request.form.get('detail_level', 'comprehensive')
action_focus = request.form.get('action_focus', 'standard')
output_style = request.form.get('output_style', 'professional')
# Handle file uploads
uploaded_files = {}
# Transcript file (required)
if 'transcript' not in request.files:
return jsonify({'error': 'No transcript file provided'}), 400
transcript_file = request.files['transcript']
if transcript_file.filename == '':
return jsonify({'error': 'No transcript file selected'}), 400
if transcript_file and allowed_file(transcript_file.filename):
filename = secure_filename(transcript_file.filename)
transcript_path = UPLOAD_FOLDER / f"transcript_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}"
transcript_file.save(transcript_path)
uploaded_files['transcript'] = transcript_path
else:
return jsonify({'error': 'Invalid transcript file type'}), 400
# PowerPoint file (optional)
powerpoint_path = None
if 'powerpoint' in request.files:
powerpoint_file = request.files['powerpoint']
if powerpoint_file and powerpoint_file.filename != '' and allowed_file(powerpoint_file.filename):
filename = secure_filename(powerpoint_file.filename)
powerpoint_path = UPLOAD_FOLDER / f"powerpoint_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}"
powerpoint_file.save(powerpoint_path)
uploaded_files['powerpoint'] = powerpoint_path
# Previous reports (optional, multiple files)
previous_reports = []
if 'previous_reports' in request.files:
files = request.files.getlist('previous_reports')
for file in files:
if file and file.filename != '' and allowed_file(file.filename):
filename = secure_filename(file.filename)
file_path = UPLOAD_FOLDER / f"previous_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}"
file.save(file_path)
previous_reports.append(file_path)
# Initialize generator
generator = EnhancedMeetingMinutesGenerator(model=model)
# Load transcript
with open(uploaded_files['transcript'], 'r', encoding='utf-8') as f:
transcript = f.read()
# Process PowerPoint if provided
ppt_content = None
if powerpoint_path:
ppt_content = generator.process_powerpoint_content(str(powerpoint_path))
# Extract previous reports summary
previous_reports_summary = ""
if previous_reports:
previous_reports_summary = extract_previous_reports_summary([str(p) for p in previous_reports])
# Generate enhanced meeting minutes with configuration
minutes = generator.generate_meeting_minutes_with_config(
transcript=transcript,
ppt_content=ppt_content,
meeting_title=meeting_title,
previous_reports_summary=previous_reports_summary,
user_instructions=user_instructions,
rigor_level=rigor_level,
detail_level=detail_level,
action_focus=action_focus,
output_style=output_style
)
# Handle potential truncation with automatic regeneration
minutes = handle_potential_truncation(
generator,
minutes,
transcript=transcript,
ppt_content=ppt_content,
meeting_title=meeting_title,
previous_reports_summary=previous_reports_summary,
user_instructions=user_instructions,
rigor_level=rigor_level,
detail_level=detail_level,
action_focus=action_focus,
output_style=output_style
)
# Check for remaining validation issues
validation_notes = []
if "## Meeting Agenda" in minutes and "## Meeting Discussion by Agenda Item" in minutes:
agenda_section = minutes.split("## Meeting Agenda")[1].split("##")[0]
agenda_items = [line.strip() for line in agenda_section.split('\n') if line.strip() and any(line.strip().startswith(f'{i}.') for i in range(1, 10))]
discussion_section = minutes.split("## Meeting Discussion by Agenda Item")[1].split("## Action Items")[0] if "## Action Items" in minutes else minutes.split("## Meeting Discussion by Agenda Item")[1]
discussion_items = [line.strip() for line in discussion_section.split('\n') if line.strip().startswith('###')]
if len(agenda_items) > len(discussion_items):
validation_notes.append(f"⚠️ WARNING: Agenda lists {len(agenda_items)} items but only {len(discussion_items)} discussion sections generated.")
validation_notes.append("Output may be incomplete. Consider regenerating with modified instructions.")
# Add validation warnings to the output if needed
if validation_notes:
minutes += "\n\n## Generation Validation Warnings\n"
for note in validation_notes:
minutes += f"- {note}\n"
# Save the generated report
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
report_filename = f"meeting_minutes_{timestamp}.md"
report_path = REPORTS_FOLDER / report_filename
with open(report_path, 'w', encoding='utf-8') as f:
f.write(minutes)
# Convert to HTML for display
html_content = markdown.markdown(minutes, extensions=['tables', 'fenced_code'])
# Store session data for potential regeneration
session_data = {
'transcript_path': str(uploaded_files['transcript']),
'powerpoint_path': str(powerpoint_path) if powerpoint_path else None,
'previous_reports': [str(p) for p in previous_reports],
'meeting_title': meeting_title,
'model': model,
'user_instructions': user_instructions,
'rigor_level': rigor_level,
'detail_level': detail_level,
'action_focus': action_focus,
'output_style': output_style,
'report_path': str(report_path)
}
session_file = REPORTS_FOLDER / f"session_{timestamp}.json"
with open(session_file, 'w') as f:
json.dump(session_data, f)
return jsonify({
'success': True,
'html_content': html_content,
'markdown_content': minutes,
'report_path': str(report_path),
'session_id': timestamp
})
except Exception as e:
logger.error(f"Error generating minutes: {str(e)}")
return jsonify({'error': f'Error generating minutes: {str(e)}'}), 500
Return Value
Returns a Flask JSON response. On success (200): {'success': True, 'html_content': string (HTML-formatted minutes), 'markdown_content': string (raw Markdown minutes), 'report_path': string (file path to saved report), 'session_id': string (timestamp identifier)}. On error (400/500): {'error': string (error message)} with appropriate HTTP status code.
Dependencies
flaskwerkzeugmarkdownopenaipathlibdatetimejsonloggingtempfileos
Required Imports
from flask import Flask, render_template, request, jsonify, send_file, flash, redirect, url_for
from werkzeug.utils import secure_filename
import markdown
import logging
from datetime import datetime
from pathlib import Path
import json
import os
import tempfile
import openai
from enhanced_meeting_minutes_generator import EnhancedMeetingMinutesGenerator
from document_extractor import DocumentExtractor
Usage Example
# Flask application setup
from flask import Flask, request, jsonify
from pathlib import Path
import logging
app = Flask(__name__)
UPLOAD_FOLDER = Path('./uploads')
REPORTS_FOLDER = Path('./reports')
UPLOAD_FOLDER.mkdir(exist_ok=True)
REPORTS_FOLDER.mkdir(exist_ok=True)
logger = logging.getLogger(__name__)
# Helper functions (must be implemented)
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'txt', 'md', 'pptx', 'pdf'}
def extract_previous_reports_summary(file_paths):
return "Summary of previous reports"
def handle_potential_truncation(generator, minutes, **kwargs):
return minutes
# Route decorator and function
@app.route('/generate', methods=['POST'])
def generate_minutes():
# Function implementation as shown in source code
pass
# Client-side usage (JavaScript/HTML form)
# <form method="POST" action="/generate" enctype="multipart/form-data">
# <input type="text" name="meeting_title" value="Team Meeting">
# <select name="model"><option value="gpt-4o">GPT-4o</option></select>
# <input type="file" name="transcript" required>
# <input type="file" name="powerpoint">
# <input type="file" name="previous_reports" multiple>
# <textarea name="user_instructions"></textarea>
# <select name="rigor_level"><option value="balanced">Balanced</option></select>
# <button type="submit">Generate Minutes</button>
# </form>
Best Practices
- Ensure UPLOAD_FOLDER and REPORTS_FOLDER directories exist and have write permissions before starting the Flask app
- Implement proper file validation in allowed_file() to prevent security vulnerabilities (limit file types and sizes)
- Configure OpenAI API key securely through environment variables, not hardcoded
- Implement rate limiting on this endpoint to prevent abuse of AI API calls
- Add file size limits to prevent disk space exhaustion from large uploads
- Clean up old uploaded files periodically to manage disk space
- The function saves session data for regeneration - implement cleanup for old session files
- Validation warnings are appended to output - consider separating them for better UX
- Error messages may expose internal paths - sanitize error responses in production
- Consider implementing async processing for large files to avoid request timeouts
- The function uses UTF-8 encoding - ensure all uploaded files are text-based or properly decoded
- Test with various file formats and edge cases (empty files, corrupted files, very large transcripts)
- Implement proper logging for debugging and monitoring AI generation quality
- Consider adding authentication/authorization before allowing access to this endpoint
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function regenerate_minutes 81.4% similar
-
class MeetingMinutesGenerator 72.5% similar
-
function test_web_ui 71.0% similar
-
function main_v15 70.4% similar
-
class MeetingMinutesGenerator_v1 70.3% similar