ikraamkb commited on
Commit
7839da1
·
verified ·
1 Parent(s): 2026cd8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +214 -11
app.py CHANGED
@@ -1,18 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  @app.post("/summarize/")
2
  async def summarize_api(file: UploadFile = File(...), length: str = Form("medium")):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  try:
4
- # Process document and generate summary
5
- summary = "This would be your generated summary text"
6
- audio_url = "/files/summary_audio.mp3" # Generated audio path
7
- pdf_url = "/files/summary.pdf" # Generated PDF path
8
-
 
 
 
 
 
 
 
 
 
9
  return {
10
  "summary": summary,
11
- "audio_url": audio_url,
12
- "pdf_url": pdf_url
13
  }
 
 
 
14
  except Exception as e:
15
- return JSONResponse(
16
- {"detail": str(e)},
17
- status_code=500
18
- )
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile, File, Form, HTTPException
2
+ from fastapi.responses import JSONResponse
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ import os
5
+ import tempfile
6
+ from gtts import gTTS
7
+ from fpdf import FPDF
8
+ import datetime
9
+ import fitz # PyMuPDF
10
+ import docx
11
+ import pptx
12
+ import openpyxl
13
+ import re
14
+ import nltk
15
+ from nltk.tokenize import sent_tokenize
16
+ from transformers import pipeline, AutoTokenizer, AutoModelForSeq2SeqLM
17
+ import torch
18
+ import easyocr
19
+ import shutil
20
+ import hashlib
21
+
22
+ nltk.download('punkt', quiet=True)
23
+
24
+ app = FastAPI()
25
+
26
+ # CORS Configuration
27
+ app.add_middleware(
28
+ CORSMiddleware,
29
+ allow_origins=["*"],
30
+ allow_credentials=True,
31
+ allow_methods=["*"],
32
+ allow_headers=["*"],
33
+ )
34
+
35
+ # Initialize models
36
+ MODEL_NAME = "facebook/bart-large-cnn"
37
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
38
+ model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME)
39
+ model.eval()
40
+ summarizer = pipeline("summarization", model=model, tokenizer=tokenizer, device=-1, batch_size=4)
41
+
42
+ reader = easyocr.Reader(['en'], gpu=torch.cuda.is_available())
43
+ summary_cache = {}
44
+
45
+ def clean_text(text: str) -> str:
46
+ text = re.sub(r'\s+', ' ', text)
47
+ text = re.sub(r'\u2022\s*|\d\.\s+', '', text)
48
+ text = re.sub(r'\[.*?\]|\(.*?\)', '', text)
49
+ text = re.sub(r'\bPage\s*\d+\b', '', text, flags=re.IGNORECASE)
50
+ return text.strip()
51
+
52
+ def extract_text(file_path: str, file_extension: str):
53
+ try:
54
+ if file_extension == "pdf":
55
+ with fitz.open(file_path) as doc:
56
+ text = "\n".join(page.get_text("text") for page in doc)
57
+ if len(text.strip()) < 50:
58
+ images = [page.get_pixmap() for page in doc]
59
+ temp_img = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
60
+ images[0].save(temp_img.name)
61
+ ocr_result = reader.readtext(temp_img.name, detail=0)
62
+ os.unlink(temp_img.name)
63
+ text = "\n".join(ocr_result) if ocr_result else text
64
+ return clean_text(text), ""
65
+
66
+ elif file_extension == "docx":
67
+ doc = docx.Document(file_path)
68
+ return clean_text("\n".join(p.text for p in doc.paragraphs), ""
69
+
70
+ elif file_extension == "pptx":
71
+ prs = pptx.Presentation(file_path)
72
+ text = [shape.text for slide in prs.slides for shape in slide.shapes if hasattr(shape, "text")]
73
+ return clean_text("\n".join(text)), ""
74
+
75
+ elif file_extension == "xlsx":
76
+ wb = openpyxl.load_workbook(file_path, read_only=True)
77
+ text = [" ".join(str(cell) for cell in row if cell) for sheet in wb.sheetnames for row in wb[sheet].iter_rows(values_only=True)]
78
+ return clean_text("\n".join(text)), ""
79
+
80
+ return "", "Unsupported file format"
81
+ except Exception as e:
82
+ return "", f"Error reading {file_extension.upper()} file: {str(e)}"
83
+
84
+ def chunk_text(text: str, max_tokens: int = 950):
85
+ try:
86
+ sentences = sent_tokenize(text)
87
+ except:
88
+ words = text.split()
89
+ sentences = [' '.join(words[i:i+20]) for i in range(0, len(words), 20]
90
+
91
+ chunks = []
92
+ current_chunk = ""
93
+ for sentence in sentences:
94
+ token_length = len(tokenizer.encode(current_chunk + " " + sentence))
95
+ if token_length <= max_tokens:
96
+ current_chunk += " " + sentence
97
+ else:
98
+ chunks.append(current_chunk.strip())
99
+ current_chunk = sentence
100
+
101
+ if current_chunk:
102
+ chunks.append(current_chunk.strip())
103
+
104
+ return chunks
105
+
106
+ def generate_summary(text: str, length: str = "medium") -> str:
107
+ cache_key = hashlib.md5((text + length).encode()).hexdigest()
108
+ if cache_key in summary_cache:
109
+ return summary_cache[cache_key]
110
+
111
+ length_params = {
112
+ "short": {"max_length": 80, "min_length": 30},
113
+ "medium": {"max_length": 200, "min_length": 80},
114
+ "long": {"max_length": 300, "min_length": 210}
115
+ }
116
+ chunks = chunk_text(text)
117
+ try:
118
+ summaries = summarizer(
119
+ chunks,
120
+ max_length=length_params[length]["max_length"],
121
+ min_length=length_params[length]["min_length"],
122
+ do_sample=False,
123
+ truncation=True,
124
+ no_repeat_ngram_size=2,
125
+ num_beams=2,
126
+ early_stopping=True
127
+ )
128
+ summary_texts = [s['summary_text'] for s in summaries]
129
+ except Exception as e:
130
+ summary_texts = [f"[Batch error: {str(e)}]"]
131
+
132
+ final_summary = " ".join(summary_texts)
133
+ final_summary = ". ".join(s.strip().capitalize() for s in final_summary.split(". ") if s.strip())
134
+ final_summary = final_summary if len(final_summary) > 25 else "Summary too short - document may be too brief"
135
+
136
+ summary_cache[cache_key] = final_summary
137
+ return final_summary
138
+
139
+ def text_to_speech(text: str):
140
+ try:
141
+ tts = gTTS(text)
142
+ temp_audio = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3")
143
+ tts.save(temp_audio.name)
144
+ return temp_audio.name
145
+ except Exception as e:
146
+ print(f"Error in text-to-speech: {e}")
147
+ return ""
148
+
149
+ def create_pdf(summary: str, original_filename: str):
150
+ try:
151
+ pdf = FPDF()
152
+ pdf.add_page()
153
+ pdf.set_font("Arial", size=12)
154
+ pdf.set_font("Arial", 'B', 16)
155
+ pdf.cell(200, 10, txt="Document Summary", ln=1, align='C')
156
+ pdf.set_font("Arial", size=12)
157
+ pdf.cell(200, 10, txt=f"Original file: {original_filename}", ln=1)
158
+ pdf.cell(200, 10, txt=f"Generated on: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", ln=1)
159
+ pdf.ln(10)
160
+ pdf.multi_cell(0, 10, txt=summary)
161
+ temp_pdf = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
162
+ pdf.output(temp_pdf.name)
163
+ return temp_pdf.name
164
+ except Exception as e:
165
+ print(f"Error creating PDF: {e}")
166
+ return ""
167
+
168
  @app.post("/summarize/")
169
  async def summarize_api(file: UploadFile = File(...), length: str = Form("medium")):
170
+ # Validate file type
171
+ valid_types = [
172
+ 'application/pdf',
173
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
174
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
175
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
176
+ ]
177
+
178
+ if file.content_type not in valid_types:
179
+ raise HTTPException(
180
+ status_code=400,
181
+ detail="Please upload a valid document (PDF, DOCX, PPTX, or XLSX)"
182
+ )
183
+
184
  try:
185
+ # Save temp file
186
+ with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as temp:
187
+ shutil.copyfileobj(file.file, temp)
188
+ temp_path = temp.name
189
+
190
+ # Process file
191
+ text, error = extract_text(temp_path, os.path.splitext(file.filename)[1][1:].lower())
192
+ if error:
193
+ raise HTTPException(status_code=400, detail=error)
194
+
195
+ summary = generate_summary(text, length)
196
+ audio_path = text_to_speech(summary)
197
+ pdf_path = create_pdf(summary, file.filename)
198
+
199
  return {
200
  "summary": summary,
201
+ "audio_url": f"/files/{os.path.basename(audio_path)}" if audio_path else None,
202
+ "pdf_url": f"/files/{os.path.basename(pdf_path)}" if pdf_path else None
203
  }
204
+
205
+ except HTTPException:
206
+ raise
207
  except Exception as e:
208
+ raise HTTPException(
209
+ status_code=500,
210
+ detail=f"Summarization failed: {str(e)}"
211
+ )
212
+ finally:
213
+ if 'temp_path' in locals() and os.path.exists(temp_path):
214
+ os.unlink(temp_path)
215
+
216
+ @app.get("/files/{filename}")
217
+ async def get_file(filename: str):
218
+ file_path = os.path.join(tempfile.gettempdir(), filename)
219
+ if os.path.exists(file_path):
220
+ return FileResponse(file_path)
221
+ raise HTTPException(status_code=404, detail="File not found")