this is my present app.py code 
"
from flask import Flask, render_template, request, jsonify, send_file
import google.generativeai as genai
from dotenv import load_dotenv
import os
from werkzeug.utils import secure_filename
from PIL import Image
import PIL
import io
from io import BytesIO
import logging
from langdetect import detect
import requests
from requests import get
from google.generativeai.types import GenerationConfig 
from markdown import markdown
from google.generativeai.types import GenerationConfig, HarmCategory, HarmBlockThreshold
import re
import json
from mailjet_rest import Client
from pdf2image import convert_from_bytes
from docx import Document
from docx.shared import Inches
import tempfile
import PyPDF2
import base64
from docx import Document
from docx.shared import Inches, Pt
from docx.enum.style import WD_STYLE_TYPE
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.shared import RGBColor
import fitz  # PyMuPDF

app = Flask(__name__)

# Load environment variables
load_dotenv()
mail_API_KEY = os.environ.get("mail_API_KEY")
mail_API_SECRET = os.environ.get("mail_API_SECRET")
mailjet = Client(auth=(mail_API_KEY, mail_API_SECRET), version='v3.1')
api_key = ""
unsplash_api_key = os.getenv('UNSPLASH_API_KEY')
API_KEY = os.getenv('OPENWEATHERMAP_API_KEY')

# Set up logging
logging.basicConfig(level=logging.INFO)

# Configure the Google Generative AI API
genai.configure(api_key=api_key)

# Session key
user_data = {}  # Dictionary to store user data

import os
import uuid
import time
from threading import Lock
from flask import Flask, render_template, request, jsonify, send_file
from werkzeug.utils import secure_filename
from PIL import Image
import io
import logging
import base64
import fitz  # PyMuPDF
# Firebase imports
import firebase_admin
from firebase_admin import credentials, firestore, storage

# Initialize Firebase
cred = credentials.Certificate('C:\\Users\\vamsi\\Downloads\\KV-Nexus-main (9)\\KV-Nexus-main\\V1 copy 18\\key2.json')
firebase_admin.initialize_app(cred, {
    'storageBucket': ''
})
db = firestore.client()
bucket = storage.bucket()
# Generation configurations
generation_config = GenerationConfig(
    temperature=0.9,
    top_p=1,
    top_k=1,
    max_output_tokens=2048,
    candidate_count=1
)
generation_config_health = GenerationConfig(
    temperature=0.7,
    top_p=1,
    top_k=1,
    max_output_tokens=512,
    candidate_count=1
)

# Safety Settings
safety_settings = {
    HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
    HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
    HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
    HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
}

# Create model instances
chat_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
chef_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
story_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
psychology_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
code_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
algorithm_model = genai.GenerativeModel("gemini-1.5-flash", generation_config=generation_config)
model_vision = genai.GenerativeModel('gemini-1.5-flash', generation_config=generation_config_health)
model_text = genai.GenerativeModel('gemini-1.5-flash', generation_config=generation_config_health)
model = genai.GenerativeModel('gemini-1.5-flash')

@app.route('/')
def index():
    return render_template('index.html')



def format_response(response_text):
    """Formats the response text for display."""
    lines = [line.strip() for line in response_text.split('\n') if line.strip()]
    formatted_text = '<br>'.join(lines)
    return formatted_text

# ... [Other route handlers remain unchanged] ...
import os
import uuid
import time
import json
from threading import Lock

from flask import Flask, render_template, request, jsonify, send_file
import google.generativeai as genai
from dotenv import load_dotenv
from werkzeug.utils import secure_filename
from PIL import Image
import io
import logging
import base64
import fitz  # PyMuPDF

# Import for persistent storage
from tinydb import TinyDB, Query


# Document summarizer with improved formatting
import tempfile
import time
from threading import Lock
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
from docx.enum.table import WD_ALIGN_VERTICAL

# Rate limiting parameters
REQUEST_LIMIT = 15  # Adjust as needed
TIME_WINDOW = 60
request_count = 0
last_request_time = 0
rate_limit_lock = Lock()


def process_pdf_in_batches(file):
    """Processes PDF in batches to avoid exceeding Vercel's time limit"""
    pdf_document = fitz.open(stream=file.read(), filetype="pdf")
    total_pages = len(pdf_document)
    all_summaries = []
    batch_size = 1  # Process one page at a time

    for i in range(0, total_pages, batch_size):
        batch_texts = []
        batch_images = []
        for page_num in range(i, min(i + batch_size, total_pages)):
            page = pdf_document[page_num]
            text = page.get_text("text")
            batch_texts.append(text)

            # Image processing
            image_list = page.get_images(full=True)
            for img_index, img in enumerate(image_list):
                xref = img[0]
                base_image = pdf_document.extract_image(xref)
                image_bytes = base_image["image"]
                img = Image.open(io.BytesIO(image_bytes))
                batch_images.append(img)  # Directly append PIL Image

        batch_summary = generate_summary(batch_texts, batch_images)
        all_summaries.append(batch_summary)
        time.sleep(1)  # Adjust sleep based on response time

    final_summary = "\n\n".join(all_summaries)
    return final_summary, total_pages
from functools import wraps
def rate_limited(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        global request_count, last_request_time
        with rate_limit_lock:
            current_time = time.time()
            if current_time - last_request_time > TIME_WINDOW:
                request_count = 0
                last_request_time = current_time

            request_count += 1
            if request_count > REQUEST_LIMIT:
                remaining_time = TIME_WINDOW - (current_time - last_request_time)
                return jsonify({'error': f'Rate limit exceeded. Please try again in {int(remaining_time)} seconds.'}), 429

            last_request_time = current_time
        return func(*args, **kwargs)

    return wrapper

@app.route('/document_summarizer', methods=['GET', 'POST'])
def document_summarizer():
    return render_template('document_summarizer.html')

def process_pdf(file):
    pdf_document = fitz.open(stream=file.read(), filetype="pdf")
    images = []
    texts = []

    for page_num in range(len(pdf_document)):
        page = pdf_document[page_num]
        # Extract text
        text = page.get_text()
        texts.append(text)

        # Extract images
        image_list = page.get_images(full=True)
        for img_index, img in enumerate(image_list):
            xref = img[0]
            base_image = pdf_document.extract_image(xref)
            image_bytes = base_image["image"]
            # Convert to base64
            image_base64 = base64.b64encode(image_bytes).decode('utf-8')
            images.append(image_base64)

    return texts, images

@app.route('/process_pdf', methods=['POST'])
@rate_limited
def process_pdf_endpoint():
    data = request.get_json()
    pdf_id = data.get('pdf_id')
    if not pdf_id:
        return jsonify({'error': 'No PDF ID provided.'}), 400

    # Retrieve processing status from Firestore
    doc_ref = db.collection('pdf_processes').document(pdf_id)
    doc = doc_ref.get()
    if not doc.exists:
        return jsonify({'error': 'Invalid PDF ID.'}), 400
    result = doc.to_dict()

    try:
        blob = bucket.blob(f'pdfs/{pdf_id}.pdf')
        if not blob.exists():
            return jsonify({'error': 'File not found.'}), 404

        pdf_bytes = blob.download_as_bytes()
        pdf_document = fitz.open(stream=pdf_bytes, filetype="pdf")
        total_pages = result.get('total_pages', len(pdf_document))

        current_page = result['current_page']
        summary = result['summary']

        start_time = time.time()

        # Process pages until time limit is reached
        while current_page < total_pages and (time.time() - start_time) < 8:
            page = pdf_document[current_page]
            text = page.get_text()
            images = []

            # Extract images
            image_list = page.get_images(full=True)
            for img_index, img in enumerate(image_list):
                xref = img[0]
                base_image = pdf_document.extract_image(xref)
                image_bytes = base_image["image"]
                image_base64 = base64.b64encode(image_bytes).decode('utf-8')
                images.append(image_base64)

            # Generate summary for the page
            page_summary = generate_summary([text], images)
            summary += page_summary + '\n\n'

            current_page += 1
            # Update the processing status in Firestore
            doc_ref.update({'current_page': current_page, 'summary': summary})

        pdf_document.close()

        if current_page >= total_pages:
            # Processing complete
            doc_ref.update({'status': 'completed'})
            # Optionally delete the PDF from storage
            blob.delete()
            return jsonify({'status': 'completed'}), 200
        else:
            # Processing not complete
            return jsonify({'status': 'processing', 'current_page': current_page, 'total_pages': total_pages}), 200

    except Exception as e:
        return jsonify({'error': str(e)}), 500

def generate_summary(texts, images):
    # Modified prompt to encourage better formatting
    prompt = [
    """Summarize the following text, incorporating details from any included images.
    Ensure that the summary is well-structured with clear headings and subheadings.
    Use the following formatting guidelines:

    - Use 'TITLE: [Your Title]' for main sections.
    - Use 'SUBTITLE: [Your Subtitle]' for subsections.
    - For emphasis, use 'BOLD:[Your Text]' to make the text bold.
    - For important points, use 'ITALIC:[Your Text]' to italicize the text.
    - For lists, use 'LIST_ITEM: [Your List Item]' at the start of each item.
    - For tables, present data in a structured format using 'TABLE_START', 'TABLE_ROW', and 'TABLE_END' tags.

    **Please avoid using asterisks (*) or other symbols for formatting. Use only the specified formatting markers.**

    Example:

    TITLE: Introduction
    SUBTITLE: Background
    BOLD: This is an important concept.
    ITALIC: Note this subtle point.
    LIST_ITEM: First point
    LIST_ITEM: Second point
    TABLE_START
    TABLE_ROW: Column1 | Column2 | Column3
    TABLE_ROW: Data1 | Data2 | Data3
    TABLE_END

    Use simple and easy English. Keep the summary concise but informative.
    """,
    "\n".join(texts)
]

    for img in images:
        prompt.append(img)
        
    try:
        response = model_vision.generate_content(prompt, safety_settings=safety_settings)
        return response.text
    except Exception as e:
        print(f"Error in Gemini API call: {e}")
        return f"Error summarizing: {e}"

# ... [Imports and other code remain unchanged] ...

def create_word_document(summary):
    doc = Document()
    
    # Define and customize styles
    styles = doc.styles
    
    # Title style
    title_style = styles.add_style('CustomTitle', WD_STYLE_TYPE.PARAGRAPH)
    title_style.font.name = 'Calibri Light'
    title_style.font.size = Pt(26)
    title_style.font.bold = True
    title_style.font.color.rgb = RGBColor(31, 56, 100)  # Dark blue
    title_style.paragraph_format.space_before = Pt(24)
    title_style.paragraph_format.space_after = Pt(12)
    title_style.paragraph_format.keep_with_next = True

    # Subtitle style
    subtitle_style = styles.add_style('CustomSubtitle', WD_STYLE_TYPE.PARAGRAPH)
    subtitle_style.font.name = 'Calibri'
    subtitle_style.font.size = Pt(18)
    subtitle_style.font.bold = True
    subtitle_style.font.color.rgb = RGBColor(54, 95, 145)  # Medium blue
    subtitle_style.paragraph_format.space_before = Pt(18)
    subtitle_style.paragraph_format.space_after = Pt(6)
    subtitle_style.paragraph_format.keep_with_next = True

    # Normal text style
    normal_style = styles.add_style('CustomNormal', WD_STYLE_TYPE.PARAGRAPH)
    normal_style.font.name = 'Calibri'
    normal_style.font.size = Pt(12)
    normal_style.paragraph_format.space_after = Pt(8)
    normal_style.paragraph_format.line_spacing = 1.15

    # List item style
    list_style = styles.add_style('CustomList', WD_STYLE_TYPE.PARAGRAPH)
    list_style.font.name = 'Calibri'
    list_style.font.size = Pt(12)
    list_style.paragraph_format.left_indent = Inches(0.25)
    list_style.paragraph_format.first_line_indent = Inches(-0.25)
    list_style.paragraph_format.space_after = Pt(4)
    list_style.paragraph_format.line_spacing = 1.15

    # Process the summary
    paragraphs = summary.split('\n')
    in_table = False
    table_rows = []
    
    # Function to process inline formatting
    def process_text_line(paragraph, text, default_style=None):
        import re
        # Pattern to match **bold**, *italic*
        pattern = re.compile(r'\*\*(.*?)\*\*|\*(.*?)\*')
        last_end = 0
        for match in pattern.finditer(text):
            start, end = match.span()
            # Text before the match
            if start > last_end:
                run = paragraph.add_run(text[last_end:start])
                if default_style == 'bold':
                    run.bold = True
                elif default_style == 'italic':
                    run.italic = True
            # Matched text
            if match.group(1):  # Bold text
                run = paragraph.add_run(match.group(1))
                run.bold = True
                if default_style == 'bold':
                    run.bold = True
                elif default_style == 'italic':
                    run.italic = True
            elif match.group(2):  # Italic text
                run = paragraph.add_run(match.group(2))
                run.italic = True
                if default_style == 'bold':
                    run.bold = True
                elif default_style == 'italic':
                    run.italic = True
            last_end = end
        # Text after the last match
        if last_end < len(text):
            run = paragraph.add_run(text[last_end:])
            if default_style == 'bold':
                run.bold = True
            elif default_style == 'italic':
                run.italic = True

    for para in paragraphs:
        para = para.strip()
    
        if para.startswith('TITLE:'):
            if in_table:
                add_table_to_document(doc, table_rows)
                table_rows = []
                in_table = False
            p = doc.add_paragraph('', style='CustomTitle')
            process_text_line(p, para[6:].strip())
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
        elif para.startswith('SUBTITLE:'):
            if in_table:
                add_table_to_document(doc, table_rows)
                table_rows = []
                in_table = False
            p = doc.add_paragraph('', style='CustomSubtitle')
            process_text_line(p, para[9:].strip())
    
        elif para.startswith('BOLD:'):
            if in_table:
                add_table_to_document(doc, table_rows)
                table_rows = []
                in_table = False
            p = doc.add_paragraph('', style='CustomNormal')
            process_text_line(p, para[5:].strip(), default_style='bold')
    
        elif para.startswith('ITALIC:'):
            if in_table:
                add_table_to_document(doc, table_rows)
                table_rows = []
                in_table = False
            p = doc.add_paragraph('', style='CustomNormal')
            process_text_line(p, para[7:].strip(), default_style='italic')
    
        elif para.startswith('LIST_ITEM:'):
            if in_table:
                add_table_to_document(doc, table_rows)
                table_rows = []
                in_table = False
            p = doc.add_paragraph('', style='CustomList')
            run = p.add_run('• ')
            run.font.size = Pt(10)
            process_text_line(p, para[10:].strip())
    
        elif para.startswith('TABLE_START'):
            in_table = True
            table_rows = []
    
        elif para.startswith('TABLE_ROW:'):
            in_table = True
            row_data = [cell.strip() for cell in para[10:].split('|')]
            table_rows.append(row_data)
    
        elif para.startswith('TABLE_END'):
            add_table_to_document(doc, table_rows)
            table_rows = []
            in_table = False
    
        elif para:
            if in_table:
                add_table_to_document(doc, table_rows)
                table_rows = []
                in_table = False
            p = doc.add_paragraph('', style='CustomNormal')
            process_text_line(p, para.strip())
    
    if in_table:
        add_table_to_document(doc, table_rows)
    
    # Add page borders
    set_page_border(doc)
    
    # Set up the document layout
    section = doc.sections[0]
    section.page_height = Inches(11)
    section.page_width = Inches(8.5)
    section.left_margin = Inches(1)
    section.right_margin = Inches(1)
    section.top_margin = Inches(1)
    section.bottom_margin = Inches(1)
    
    docx_buffer = io.BytesIO()
    doc.save(docx_buffer)
    docx_buffer.seek(0)
    return docx_buffer

# ... [Rest of the code remains unchanged] ...
def add_table_to_document(doc, rows):
    if not rows:
        return
    table = doc.add_table(rows=len(rows), cols=len(rows[0]))
    table.style = 'Light List Accent 1'  # Choose a more visually appealing style

    # Set column widths and alignment
    widths = [Inches(2.0)] * len(rows[0])  # Adjust column widths as needed
    for row_idx, row in enumerate(rows):
        for col_idx, cell_text in enumerate(row):
            cell = table.cell(row_idx, col_idx)
            cell.text = cell_text
            cell.width = widths[col_idx]
            cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
            cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER

    # Make the header row bold and with background color
    hdr_cells = table.rows[0].cells
    for cell in hdr_cells:
        for paragraph in cell.paragraphs:
            for run in paragraph.runs:
                run.font.bold = True
        shading_elm = OxmlElement('w:shd')
        shading_elm.set(qn('w:fill'), 'D9E1F2')  # Light blue background
        cell._tc.get_or_add_tcPr().append(shading_elm)

    table.allow_autofit = True
    doc.add_paragraph()  # Add space after table

def set_page_border(doc):
    """
    Adds a wider double border to every page of the document.
    """
    for section in doc.sections:
        sectPr = section._sectPr
        pgBorders = OxmlElement('w:pgBorders')
        pgBorders.set(qn('w:offsetFrom'), 'page')
        for border_position in ('top', 'left', 'bottom', 'right'):
            border_el = OxmlElement(f'w:{border_position}')
            border_el.set(qn('w:val'), 'double')
            border_el.set(qn('w:sz'), '24')  # Adjusted border width
            border_el.set(qn('w:space'), '24')
            border_el.set(qn('w:color'), '5B9BD5')  # Blue border color
            pgBorders.append(border_el)
        sectPr.append(pgBorders)

@app.route('/upload', methods=['POST'])
@rate_limited
def upload_file():
    if 'file' not in request.files:
        return jsonify({'error': 'No file part'}), 400
    file = request.files['file']
    if file.filename == '':
        return jsonify({'error': 'No selected file'}), 400

    if file and file.filename.endswith('.pdf'):
        try:
            # Generate a unique PDF ID
            pdf_id = str(uuid.uuid4())

            # Upload the PDF to Firebase Storage
            blob = bucket.blob(f'pdfs/{pdf_id}.pdf')
            blob.upload_from_file(file)

            # Get the total number of pages
            pdf_document = fitz.open(stream=blob.download_as_bytes(), filetype="pdf")
            total_pages = len(pdf_document)
            pdf_document.close()

            # Initialize processing status in Firestore
            db.collection('pdf_processes').document(pdf_id).set({
                'status': 'processing',
                'current_page': 0,
                'total_pages': total_pages,
                'summary': '',
                'timestamp': firestore.SERVER_TIMESTAMP
            })

            return jsonify({'pdf_id': pdf_id}), 200
        except Exception as e:
            return jsonify({'error': str(e)}), 500
    else:
        return jsonify({'error': 'Invalid file type. Please upload a PDF.'}), 400
# ... [Other route handlers remain unchanged] ...
@app.route('/check_status', methods=['GET'])
def check_status():
    pdf_id = request.args.get('pdf_id')
    if not pdf_id:
        return jsonify({'error': 'No PDF ID provided.'}), 400

    # Retrieve processing status from Firestore
    doc_ref = db.collection('pdf_processes').document(pdf_id)
    doc = doc_ref.get()
    if not doc.exists:
        return jsonify({'error': 'Invalid PDF ID.'}), 400
    result = doc.to_dict()

    status = result.get('status', 'processing')
    if status == 'completed':
        # Create word document
        summary = result['summary']
        docx_buffer = create_word_document(summary)
        # Encode the docx file to base64
        docx_base64 = base64.b64encode(docx_buffer.getvalue()).decode('utf-8')
        return jsonify({
            'status': 'completed',
            'docx': docx_base64,
            'total_pages': result.get('total_pages', 0),
            'processing_time': 'N/A'  # Optionally calculate processing time
        }), 200
    else:
        return jsonify({
            'status': 'processing',
            'current_page': result.get('current_page', 0),
            'total_pages': result.get('total_pages', 0)
        }), 200

# ... [Include the rest of your functions like create_word_document, generate_summary, etc.] ...

# Test upload to Cloud Storage

if __name__ == '__main__':
    app.run(debug=True)
"
i am still getting the words "ITALIC" "BOLD" etc " LIST_ITEM:* " etc..please refine the code and make sure that no unnecessary things comes out...also i am getting quota exhausted error sometimes..even i have 15rpm for that model ...so based on this fix it