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 controls.
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 (simplified)
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'txt', 'md', 'pptx', 'pdf'}
def extract_previous_reports_summary(paths):
return "Summary of previous reports"
def handle_potential_truncation(generator, minutes, **kwargs):
return minutes
@app.route('/generate', methods=['POST'])
def generate_minutes():
# Function implementation here
pass
# Client-side usage (JavaScript/HTML form)
# <form method="POST" action="/generate" enctype="multipart/form-data">
# <input type="text" name="meeting_title" value="Team Sync">
# <select name="model"><option value="gpt-4o">GPT-4o</option></select>
# <select name="rigor_level"><option value="balanced">Balanced</option></select>
# <input type="file" name="transcript" required>
# <input type="file" name="powerpoint">
# <button type="submit">Generate Minutes</button>
# </form>
if __name__ == '__main__':
app.run(debug=True)
Best Practices
- Ensure UPLOAD_FOLDER and REPORTS_FOLDER directories exist and have write permissions before starting the application
- Configure proper file size limits in Flask to prevent memory issues with large uploads
- Implement rate limiting to prevent API abuse and excessive OpenAI API costs
- Set up proper error logging and monitoring for production environments
- Clean up old uploaded files periodically to prevent disk space issues
- Validate and sanitize all user inputs, especially user_instructions, to prevent prompt injection
- Consider implementing authentication/authorization before deploying to production
- Set appropriate timeouts for AI generation calls to prevent hanging requests
- Store session files with expiration and implement cleanup routines
- Use environment variables for sensitive configuration like API keys
- Test with various file types and sizes to ensure robust error handling
- Consider implementing async processing for large files to improve user experience
- Add CORS configuration if the API will be accessed from different domains
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function regenerate_minutes 81.5% similar
-
class MeetingMinutesGenerator 72.5% similar
-
function test_web_ui 70.6% similar
-
function main_v27 70.3% similar
-
class MeetingMinutesGenerator_v1 70.3% similar