File size: 11,219 Bytes
164d23a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4e1f583
 
 
164d23a
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
"""
Meeting Summarizer - Applicazione Gradio
Web app per l'analisi e sintesi automatica di meeting tramite GPT-4o-mini.
"""

import os
import tempfile
import shutil
import gradio as gr
from typing import Tuple, Optional

# Import moduli locali
from utils.text_extraction import extract_text, get_supported_extensions
from utils.transcription import transcribe_audio, is_audio_file, get_supported_audio_extensions
from utils.llm_analysis import analyze_meeting, format_analysis_for_display
from utils.pdf_generator import generate_pdf, cleanup_temp_pdf
from utils.data_persistence import save_meeting_to_dataset

# Configurazione logging
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Variabili globali per file temporanei
_temp_files = []



def process_meeting(file, api_key: str, hf_token: str = "") -> Tuple[str, str, str, str, str]:
    """
    Processa un file di meeting e restituisce l'analisi completa.
    
    Args:
        file: File caricato dall'utente
        api_key (str): Chiave API OpenAI
        hf_token (str): Token Hugging Face (opzionale)
        
    Returns:
        Tuple[str, str, str, str, str]: (summary, topics, keywords, pdf_path, message)
    """
    global _temp_files

    try:
        # Verifica input e debug
        logger.info(f"DEBUG: file ricevuto tipo={type(file)} valore={file}")
        
        if not file:
            return "", "", "", None, "❌ Error: No file uploaded"
        
        if not api_key:
            return "", "", "", None, "❌ Error: OpenAI API key required"
        
        # Gestisci l'oggetto file di Gradio
        # Nelle versioni recenti di Gradio, il file puΓ² essere una stringa (path) o un oggetto
        if isinstance(file, str):
            file_path = file
        else:
            # Se Γ¨ un oggetto file, estrai il path
            file_path = file.name if hasattr(file, 'name') else str(file)
        
        logger.info(f"DEBUG: file_path={file_path} exists={os.path.exists(file_path)} isfile={os.path.isfile(file_path) if os.path.exists(file_path) else 'N/A'}")
        
        # Verify that the file exists and is a file (not a directory)
        if not os.path.exists(file_path):
            return "", "", "", None, "❌ Error: File not found or invalid"
        
        if not os.path.isfile(file_path):
            return "", "", "", None, f"❌ Error: Path is a directory, not a file: {file_path}"
        
        # Estrai testo dal file
        text = ""
        file_name = os.path.basename(file_path)
        
        logger.info(f"Processamento file: {file_name}")
        logger.info(f"Percorso file: {file_path}")
        
        # Determine if it's an audio file
        if is_audio_file(file_name):
            logger.info("Audio file detected, starting transcription...")
            text = transcribe_audio(file_path)
            if not text:
                return "", "", "", None, "❌ Error: Transcription failed"
            logger.info("Transcription completed")
        else:
            # Extract text from document
            logger.info("Document file detected, extracting text...")
            text = extract_text(file_path)
            if not text:
                return "", "", "", None, "❌ Error: Text extraction failed"
            logger.info("Text extraction completed")
        
        # Verify that the text is not empty
        if not text.strip():
            return "", "", "", None, "❌ Error: No text extracted from file"
        
        # Analyze with GPT-4o-mini
        logger.info("Starting analysis with GPT-4o-mini...")
        analysis = analyze_meeting(text, api_key)
        if not analysis:
            return "", "", "", None, "❌ Error: Analysis failed"
        
        # Formatta per display
        formatted_analysis = format_analysis_for_display(analysis)
        
        # Generate PDF
        logger.info("Generating PDF...")
        pdf_path = generate_pdf(analysis)
        
        # Debug: verify that pdf_path is valid
        if pdf_path:
            logger.info(f"PDF generated: {pdf_path}")
            logger.info(f"PDF exists: {os.path.exists(pdf_path)}")
            logger.info(f"PDF is a file: {os.path.isfile(pdf_path)}")
            # If not a valid file, set to None
            if not os.path.isfile(pdf_path):
                logger.warning(f"PDF path invalid: {pdf_path}")
                pdf_path = None
        
        # Save to dataset if token provided
        if hf_token:
            logger.info("Saving to Hugging Face Dataset...")
            meeting_data = {
                "file_name": file_name,
                "transcription": text,
                "summary": analysis.get("summary", ""),
                "topics": analysis.get("topics", []),
                "keywords": analysis.get("keywords", [])
            }
            
            if save_meeting_to_dataset(meeting_data, hf_token):
                logger.info("Meeting saved to HF Dataset")
            else:
                logger.warning("Saving to HF Dataset failed")
        
        # Add PDF to temporary files for cleanup
        if pdf_path:
            _temp_files.append(pdf_path)
        
        # Success message
        success_msg = f"βœ… Meeting analyzed successfully!\n\nπŸ“„ File: {file_name}\nπŸ“ Characters analyzed: {len(text)}\nπŸ“Š Topics identified: {len(analysis.get('topics', []))}\nπŸ”‘ Keywords: {len(analysis.get('keywords', []))}"
        
        if hf_token:
            success_msg += "\nπŸ’Ύ Data saved to Hugging Face Dataset"
        
        return (
            formatted_analysis["summary"],
            formatted_analysis["topics"], 
            formatted_analysis["keywords"],
            pdf_path if pdf_path else None,
            success_msg
        )
        
    except Exception as e:
        logger.error(f"Error during processing: {str(e)}")
        return "", "", "", None, f"❌ Error: {str(e)}"


def cleanup_temp_files():
    """Clean up temporary files."""
    global _temp_files
    for file_path in _temp_files:
        if os.path.exists(file_path):
            try:
                os.remove(file_path)
            except Exception as e:
                logger.warning(f"Unable to delete {file_path}: {str(e)}")
    _temp_files.clear()


def create_interface():
    """Create the Gradio interface."""
    
    # Supported extensions
    supported_extensions = get_supported_extensions() + get_supported_audio_extensions()
    
    with gr.Blocks(
        title="Meeting Summarizer",
        theme=gr.themes.Soft(),
        css="""
        .gradio-container {
            max-width: 1200px !important;
        }
        .success-message {
            background-color: #d4edda;
            border: 1px solid #c3e6cb;
            color: #155724;
            padding: 10px;
            border-radius: 5px;
        }
        """
    ) as app:
        
        gr.Markdown(
            """
            # 🎯 Meeting Summarizer
            
            Upload a meeting file (audio, PDF, DOCX, TXT) and automatically get:
            - πŸ“ **Complete summary** of the meeting
            - 🏷️ **Main topics** discussed  
            - πŸ”‘ **Relevant keywords**
            - πŸ“„ **Downloadable PDF** with all results
            
            ---
            """
        )
        
        with gr.Row():
            with gr.Column(scale=1):
                # Input file
                file_input = gr.File(
                    label="πŸ“ Upload Meeting File",
                    file_types=supported_extensions,
                    file_count="single"
                )
                
                # API Key OpenAI
                api_key_input = gr.Textbox(
                    label="πŸ”‘ OpenAI API Key",
                    placeholder="Enter your OpenAI API key...",
                    type="password",
                    info="Required for analysis with GPT-4o-mini"
                )
                
                # HF Token (optional)
                hf_token_input = gr.Textbox(
                    label="πŸ€— Hugging Face Token (Optional)",
                    placeholder="Enter your HF token to save data...",
                    type="password",
                    info="Optional: to save results to Hugging Face Dataset"
                )
                
                # Analyze button
                analyze_btn = gr.Button(
                    "πŸš€ Analyze Meeting",
                    variant="primary",
                    size="lg"
                )
                
                # Status message
                status_msg = gr.Textbox(
                    label="πŸ“Š Status",
                    interactive=False,
                    visible=True
                )
            
            with gr.Column(scale=2):
                # Output summary
                summary_output = gr.Markdown(
                    label="πŸ“ Meeting Summary",
                    value="The summary will appear here after analysis..."
                )
                
                # Output topics
                topics_output = gr.Markdown(
                    label="🏷️ Main Topics",
                    value="The main topics will appear here..."
                )
                
                # Output keywords
                keywords_output = gr.Markdown(
                    label="πŸ”‘ Keywords",
                    value="The keywords will appear here..."
                )
                
                # Download PDF
                pdf_download = gr.File(
                    label="πŸ“„ Download PDF Report",
                    visible=True
                )
        
        # Footer
        gr.Markdown(
            """
            ---
            ### ℹ️ Information
            
            **Supported formats:**
            - 🎡 **Audio**: MP3, WAV, M4A, FLAC, OGG
            - πŸ“„ **Documents**: PDF, DOCX, TXT
            
            **Features:**
            - 🎀 Automatic audio transcription with Whisper
            - πŸ€– Intelligent analysis with GPT-4o-mini
            - πŸ“Š Topic and keyword extraction
            - πŸ’Ύ Save to Hugging Face Datasets
            - πŸ“„ Professional PDF generation
            
            **Notes:**
            - Audio files are automatically transcribed
            - Analysis is optimized for meetings
            - Data is saved only if you provide an HF token
            """
        )
        
        # Eventi
        analyze_btn.click(
            fn=process_meeting,
            inputs=[file_input, api_key_input, hf_token_input],
            outputs=[summary_output, topics_output, keywords_output, pdf_download, status_msg],
            show_progress=True
        )
        
        # Cleanup al chiudere
        app.unload(cleanup_temp_files)
    
    return app


def main():
    """Main function."""
    logger.info("Starting Meeting Summarizer...")
    
    # Create interface
    app = create_interface()
    
    # Launch server
    app.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        show_error=True,
        quiet=False
    )


if __name__ == "__main__":
    main()