Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
app.py
ADDED
@@ -0,0 +1,953 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import streamlit as st
|
3 |
+
from openai import OpenAI
|
4 |
+
from PyPDF2 import PdfReader
|
5 |
+
import requests
|
6 |
+
from youtube_transcript_api import YouTubeTranscriptApi, NoTranscriptFound, TranscriptsDisabled
|
7 |
+
from urllib.parse import urlparse, parse_qs
|
8 |
+
from pinecone import Pinecone
|
9 |
+
import uuid
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
import time
|
12 |
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
13 |
+
from itertools import islice
|
14 |
+
import unicodedata
|
15 |
+
import re
|
16 |
+
from tiktoken import encoding_for_model
|
17 |
+
import json
|
18 |
+
import datetime
|
19 |
+
import tiktoken
|
20 |
+
import pandas as pd
|
21 |
+
import io
|
22 |
+
from fpdf import FPDF
|
23 |
+
import tempfile
|
24 |
+
from PyPDF2 import PdfReader
|
25 |
+
import base64
|
26 |
+
from pathlib import Path
|
27 |
+
import numpy as np
|
28 |
+
from pymongo import MongoClient
|
29 |
+
import traceback
|
30 |
+
from docx import Document
|
31 |
+
import pandas as pd
|
32 |
+
import io
|
33 |
+
import time
|
34 |
+
import traceback
|
35 |
+
|
36 |
+
load_dotenv()
|
37 |
+
|
38 |
+
# Set up OpenAI client
|
39 |
+
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
40 |
+
|
41 |
+
# Set up Pinecone
|
42 |
+
pc = Pinecone(api_key=os.getenv("PINECONE_API_KEY"))
|
43 |
+
|
44 |
+
# Get index name and URL from .env
|
45 |
+
index_name = os.getenv("PINECONE_INDEX_NAME")
|
46 |
+
index_url = os.getenv("PINECONE_INDEX_URL")
|
47 |
+
|
48 |
+
index = pc.Index(index_name, url=index_url)
|
49 |
+
|
50 |
+
# Set up MongoDB connection
|
51 |
+
mongo_client = MongoClient(os.getenv("MONGODB_URI"))
|
52 |
+
db = mongo_client.get_database("finance")
|
53 |
+
users_collection = db["users"]
|
54 |
+
|
55 |
+
def get_embedding(text):
|
56 |
+
response = client.embeddings.create(input=text, model="text-embedding-3-large")
|
57 |
+
return response.data[0].embedding
|
58 |
+
|
59 |
+
def chunk_text(text, content_type):
|
60 |
+
sanitized_text = sanitize_text(text)
|
61 |
+
if content_type == "YouTube":
|
62 |
+
chunk_size = 2000 # Adjust this value as needed
|
63 |
+
content_length = len(sanitized_text)
|
64 |
+
return [sanitized_text[i:i+chunk_size] for i in range(0, content_length, chunk_size)]
|
65 |
+
else: # Default for PDF and Web Link
|
66 |
+
chunk_size = 2000
|
67 |
+
content_length = len(sanitized_text)
|
68 |
+
return [sanitized_text[i:i+chunk_size] for i in range(0, content_length, chunk_size)]
|
69 |
+
|
70 |
+
def process_pdf(file):
|
71 |
+
reader = PdfReader(file)
|
72 |
+
text = []
|
73 |
+
for page in reader.pages:
|
74 |
+
text.append(page.extract_text())
|
75 |
+
return " ".join(text) # Join all pages into a single string
|
76 |
+
|
77 |
+
def process_web_link(url):
|
78 |
+
response = requests.get(url)
|
79 |
+
return chunk_text(response.text, "Web")
|
80 |
+
|
81 |
+
def process_youtube_link(url):
|
82 |
+
try:
|
83 |
+
video_id = extract_video_id(url)
|
84 |
+
transcript = YouTubeTranscriptApi.get_transcript(video_id)
|
85 |
+
full_text = " ".join([entry['text'] for entry in transcript])
|
86 |
+
print("Transcript obtained from YouTube API")
|
87 |
+
return chunk_text(full_text, "YouTube") # Use chunk_text function to split large transcripts
|
88 |
+
except NoTranscriptFound:
|
89 |
+
print("No transcript found for this YouTube video.")
|
90 |
+
return []
|
91 |
+
except TranscriptsDisabled:
|
92 |
+
print("Transcripts are disabled for this YouTube video.")
|
93 |
+
return []
|
94 |
+
except Exception as e:
|
95 |
+
print(f"An error occurred while processing the YouTube link: {str(e)}")
|
96 |
+
return []
|
97 |
+
|
98 |
+
def extract_video_id(url):
|
99 |
+
parsed_url = urlparse(url)
|
100 |
+
if parsed_url.hostname == 'youtu.be':
|
101 |
+
return parsed_url.path[1:]
|
102 |
+
if parsed_url.hostname in ('www.youtube.com', 'youtube.com'):
|
103 |
+
if parsed_url.path == '/watch':
|
104 |
+
return parse_qs(parsed_url.query)['v'][0]
|
105 |
+
if parsed_url.path[:7] == '/embed/':
|
106 |
+
return parsed_url.path.split('/')[2]
|
107 |
+
if parsed_url.path[:3] == '/v/':
|
108 |
+
return parsed_url.path.split('/')[2]
|
109 |
+
return None
|
110 |
+
|
111 |
+
def process_upload(upload_type, file_or_link, file_name=None):
|
112 |
+
print(f"Starting process_upload for {upload_type}")
|
113 |
+
doc_id = str(uuid.uuid4())
|
114 |
+
print(f"Generated doc_id: {doc_id}")
|
115 |
+
|
116 |
+
if upload_type == "PDF":
|
117 |
+
chunks = process_pdf(file_or_link)
|
118 |
+
doc_name = file_name or "Uploaded PDF"
|
119 |
+
elif upload_type == "Web Link":
|
120 |
+
chunks = process_web_link(file_or_link)
|
121 |
+
doc_name = file_or_link
|
122 |
+
elif upload_type == "YouTube Link":
|
123 |
+
chunks = process_youtube_link(file_or_link)
|
124 |
+
doc_name = f"YouTube: {file_or_link}"
|
125 |
+
else:
|
126 |
+
print("Invalid upload type")
|
127 |
+
return "Invalid upload type"
|
128 |
+
|
129 |
+
vectors = []
|
130 |
+
with ThreadPoolExecutor() as executor:
|
131 |
+
futures = [executor.submit(process_chunk, chunk, doc_id, i, upload_type, doc_name) for i, chunk in enumerate(chunks)]
|
132 |
+
|
133 |
+
for future in as_completed(futures):
|
134 |
+
vectors.append(future.result())
|
135 |
+
# Update progress
|
136 |
+
progress = len(vectors) / len(chunks)
|
137 |
+
st.session_state.upload_progress.progress(progress)
|
138 |
+
|
139 |
+
print(f"Generated {len(vectors)} vectors")
|
140 |
+
|
141 |
+
# Upsert vectors in batches
|
142 |
+
batch_size = 100 # Adjust this value as needed
|
143 |
+
for i in range(0, len(vectors), batch_size):
|
144 |
+
batch = list(islice(vectors, i, i + batch_size))
|
145 |
+
index.upsert(vectors=batch)
|
146 |
+
print(f"Upserted batch {i//batch_size + 1} of {len(vectors)//batch_size + 1}")
|
147 |
+
|
148 |
+
print("All vectors upserted to Pinecone")
|
149 |
+
|
150 |
+
return f"Processing complete for {upload_type}. Document Name: {doc_name}"
|
151 |
+
|
152 |
+
def process_chunk(chunk, doc_id, i, upload_type, doc_name):
|
153 |
+
# Sanitize the chunk text
|
154 |
+
sanitized_chunk = sanitize_text(chunk)
|
155 |
+
embedding = get_embedding(sanitized_chunk)
|
156 |
+
return (f"{doc_id}_{i}", embedding, {
|
157 |
+
"text": sanitized_chunk,
|
158 |
+
"type": upload_type,
|
159 |
+
"doc_id": doc_id,
|
160 |
+
"doc_name": doc_name,
|
161 |
+
"chunk_index": i
|
162 |
+
})
|
163 |
+
|
164 |
+
def sanitize_text(text):
|
165 |
+
# Remove control characters and normalize Unicode
|
166 |
+
return ''.join(ch for ch in unicodedata.normalize('NFKD', text) if unicodedata.category(ch)[0] != 'C')
|
167 |
+
|
168 |
+
def get_relevant_context(query, top_k=5):
|
169 |
+
print(f"Getting relevant context for query: {query}")
|
170 |
+
query_embedding = get_embedding(query)
|
171 |
+
|
172 |
+
search_results = index.query(vector=query_embedding, top_k=top_k, include_metadata=True)
|
173 |
+
print(f"Found {len(search_results['matches'])} relevant results")
|
174 |
+
|
175 |
+
# Sort results by similarity score (higher is better)
|
176 |
+
sorted_results = sorted(search_results['matches'], key=lambda x: x['score'], reverse=True)
|
177 |
+
|
178 |
+
context = "\n".join([result['metadata']['text'] for result in sorted_results])
|
179 |
+
return context, sorted_results
|
180 |
+
|
181 |
+
def truncate_context(context, max_tokens):
|
182 |
+
enc = encoding_for_model("gpt-4o-mini")
|
183 |
+
encoded = enc.encode(context)
|
184 |
+
if len(encoded) > max_tokens:
|
185 |
+
return enc.decode(encoded[:max_tokens])
|
186 |
+
return context
|
187 |
+
|
188 |
+
def chat_with_ai(message):
|
189 |
+
print(f"Chatting with AI, message: {message}")
|
190 |
+
context, results = get_relevant_context(message)
|
191 |
+
print(f"Retrieved context, length: {len(context)}")
|
192 |
+
|
193 |
+
# Truncate context if it's too long
|
194 |
+
max_tokens = 7000 # Leave some room for the system message and user query
|
195 |
+
context = truncate_context(context, max_tokens)
|
196 |
+
|
197 |
+
messages = [
|
198 |
+
{"role": "system", "content": "You are a helpful assistant. Use the following information to answer the user's question, but don't mention the context directly in your response. If the information isn't in the context, say you don't know."},
|
199 |
+
{"role": "system", "content": f"Context: {context}"},
|
200 |
+
{"role": "user", "content": message}
|
201 |
+
]
|
202 |
+
|
203 |
+
response = client.chat.completions.create(
|
204 |
+
model="gpt-4o-mini",
|
205 |
+
messages=messages
|
206 |
+
)
|
207 |
+
print("Received response from OpenAI")
|
208 |
+
|
209 |
+
ai_response = response.choices[0].message.content
|
210 |
+
|
211 |
+
# Prepare source information
|
212 |
+
sources = [
|
213 |
+
{
|
214 |
+
"doc_id": result['metadata']['doc_id'],
|
215 |
+
"doc_name": result['metadata']['doc_name'],
|
216 |
+
"chunk_index": result['metadata']['chunk_index'],
|
217 |
+
"text": result['metadata']['text'],
|
218 |
+
"type": result['metadata']['type'],
|
219 |
+
"score": result['score']
|
220 |
+
}
|
221 |
+
for result in results
|
222 |
+
]
|
223 |
+
|
224 |
+
return ai_response, sources
|
225 |
+
|
226 |
+
def process_youtube_links(links):
|
227 |
+
results = []
|
228 |
+
for link in links:
|
229 |
+
result = process_upload("YouTube Link", link.strip())
|
230 |
+
results.append(result)
|
231 |
+
return results
|
232 |
+
|
233 |
+
def process_excel(file):
|
234 |
+
dfs = pd.read_excel(file, sheet_name=None) # Read all sheets
|
235 |
+
for sheet_name, df in dfs.items():
|
236 |
+
df.columns = df.columns.astype(str)
|
237 |
+
# Remove any unnamed columns
|
238 |
+
df = df.loc[:, ~df.columns.str.contains('^Unnamed')]
|
239 |
+
return dfs
|
240 |
+
|
241 |
+
def analyze_and_generate_formulas(main_df, other_dfs):
|
242 |
+
# Focus on the 'DETAILS' column and the month columns
|
243 |
+
details_column = main_df.columns[0]
|
244 |
+
month_columns = main_df.columns[1:-1] # Exclude the 'Total' column
|
245 |
+
|
246 |
+
main_summary = f"DETAILS column data: {main_df[details_column].tolist()}\n"
|
247 |
+
for month in month_columns:
|
248 |
+
main_summary += f"{month} column data: {main_df[month].tolist()}\n"
|
249 |
+
|
250 |
+
other_sheets_summary = ""
|
251 |
+
for name, df in other_dfs.items():
|
252 |
+
if len(df.columns) > 0:
|
253 |
+
other_sheets_summary += f"\nSheet '{name}' structure:\n"
|
254 |
+
other_sheets_summary += f"Columns: {df.columns.tolist()}\n"
|
255 |
+
other_sheets_summary += f"First few rows:\n{df.head().to_string()}\n"
|
256 |
+
|
257 |
+
prompt = f"""Analyze the following Excel data and generate Python formulas to fill missing values:
|
258 |
+
|
259 |
+
Main Sheet Structure:
|
260 |
+
{main_summary}
|
261 |
+
|
262 |
+
Other Sheets:
|
263 |
+
{other_sheets_summary}
|
264 |
+
|
265 |
+
Provide Python formulas using pandas to fill missing values in the month columns of the main sheet.
|
266 |
+
Use pandas and numpy functions where appropriate. If a value cannot be determined, use None.
|
267 |
+
Return a dictionary with column names as keys and formulas as values.
|
268 |
+
Example of the expected format: {{'Apr'24': "df['Apr'24'].fillna(method='ffill')"}}
|
269 |
+
"""
|
270 |
+
|
271 |
+
try:
|
272 |
+
response = client.chat.completions.create(
|
273 |
+
model="gpt-4o-mini",
|
274 |
+
messages=[
|
275 |
+
{"role": "system", "content": "You are a data analysis expert skilled in creating concise formulas for data filling."},
|
276 |
+
{"role": "user", "content": prompt}
|
277 |
+
]
|
278 |
+
)
|
279 |
+
|
280 |
+
formulas = eval(response.choices[0].message.content.strip())
|
281 |
+
print(f"Generated formulas: {formulas}")
|
282 |
+
|
283 |
+
return formulas
|
284 |
+
|
285 |
+
except Exception as e:
|
286 |
+
print(f"Error in analyze_and_generate_formulas: {str(e)}")
|
287 |
+
print(traceback.format_exc())
|
288 |
+
return {}
|
289 |
+
|
290 |
+
def apply_formulas(main_df, other_dfs, formulas):
|
291 |
+
filled_df = main_df.copy()
|
292 |
+
|
293 |
+
for column, formula in formulas.items():
|
294 |
+
if column in filled_df.columns:
|
295 |
+
try:
|
296 |
+
print(f"Applying formula for column {column}: {formula}")
|
297 |
+
|
298 |
+
# Create a local namespace with all dataframes
|
299 |
+
namespace = {'df': filled_df, 'np': np, 'pd': pd}
|
300 |
+
|
301 |
+
# Execute the formula in the namespace
|
302 |
+
exec(f"result = {formula}", namespace)
|
303 |
+
|
304 |
+
# Apply the result to the column
|
305 |
+
filled_df[column] = namespace['result']
|
306 |
+
print(f"Successfully applied formula for column {column}")
|
307 |
+
except Exception as e:
|
308 |
+
print(f"Error applying formula for column {column}: {str(e)}")
|
309 |
+
print(traceback.format_exc())
|
310 |
+
|
311 |
+
return filled_df
|
312 |
+
|
313 |
+
def excel_to_pdf(df):
|
314 |
+
pdf = FPDF(orientation='L', unit='mm', format='A4')
|
315 |
+
pdf.add_page()
|
316 |
+
pdf.set_font("Arial", size=8)
|
317 |
+
|
318 |
+
# Calculate column widths
|
319 |
+
col_widths = [pdf.get_string_width(str(col)) + 6 for col in df.columns]
|
320 |
+
|
321 |
+
# Write header
|
322 |
+
for i, col in enumerate(df.columns):
|
323 |
+
pdf.cell(col_widths[i], 10, str(col), border=1)
|
324 |
+
pdf.ln()
|
325 |
+
|
326 |
+
# Write data
|
327 |
+
for _, row in df.iterrows():
|
328 |
+
for i, value in enumerate(row):
|
329 |
+
pdf.cell(col_widths[i], 10, str(value), border=1)
|
330 |
+
pdf.ln()
|
331 |
+
|
332 |
+
# Save to a temporary file
|
333 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_file:
|
334 |
+
pdf.output(temp_file.name)
|
335 |
+
return temp_file.name
|
336 |
+
|
337 |
+
def pdf_to_text(pdf_path):
|
338 |
+
with open(pdf_path, 'rb') as file:
|
339 |
+
pdf = PdfReader(file)
|
340 |
+
text = []
|
341 |
+
for page in pdf.pages:
|
342 |
+
text.append(page.extract_text())
|
343 |
+
return text
|
344 |
+
|
345 |
+
def get_user_feedback(user_id):
|
346 |
+
user = users_collection.find_one({"_id": user_id})
|
347 |
+
return user.get("feedback", "") if user else ""
|
348 |
+
|
349 |
+
def get_category_reports():
|
350 |
+
return {
|
351 |
+
"Default": [], # Changed from "None" to "Default"
|
352 |
+
"Sales KPI": [
|
353 |
+
"Monthwise Sales Table", "Customer-wise Sales Table (top 10)", "Qty Sales",
|
354 |
+
"Customer-wise Churn", "Avg Sales per Customer", "Product-wise Sales Rate",
|
355 |
+
"Geography-wise", "Trend Analysis", "Graphs", "Month-wise Comparison"
|
356 |
+
],
|
357 |
+
"Expenses KPI": [
|
358 |
+
"Vendor-wise Comparison", "Year on Year Monthwise", "Division-wise"
|
359 |
+
],
|
360 |
+
"Purchase Register": [
|
361 |
+
"Vendor-wise Monthwise", "Monthwise", "Material-wise Purchase Rate Analysis"
|
362 |
+
],
|
363 |
+
"Balance Sheet": [
|
364 |
+
"Year on Year Comparison", "Ratios"
|
365 |
+
]
|
366 |
+
}
|
367 |
+
|
368 |
+
def analyze_excel_with_gpt(df, sheet_name, user_feedback, category, reports_needed, use_assistants_api=False):
|
369 |
+
if use_assistants_api:
|
370 |
+
return process_excel_with_assistant(df, category, reports_needed, user_feedback)
|
371 |
+
else:
|
372 |
+
# Existing OCR-based analysis code
|
373 |
+
prompt = f"""Analyze the following Excel data from sheet '{sheet_name}':
|
374 |
+
|
375 |
+
{df.to_string()}
|
376 |
+
|
377 |
+
User's previous feedback and insights:
|
378 |
+
{user_feedback}
|
379 |
+
|
380 |
+
"""
|
381 |
+
|
382 |
+
if category != "general":
|
383 |
+
prompt += f"""Please provide analysis and insights based on the following required reports for the category '{category}':
|
384 |
+
{', '.join(reports_needed)}
|
385 |
+
|
386 |
+
Please provide:
|
387 |
+
1. A comprehensive overview of the data focusing on the {category} category
|
388 |
+
2. Key observations and trends related to the required reports
|
389 |
+
3. Any anomalies, interesting patterns, or correlations relevant to the {category}
|
390 |
+
4. Suggestions for further analysis or visualization based on the required reports
|
391 |
+
5. Address any previous feedback or insights mentioned above, if applicable
|
392 |
+
|
393 |
+
Focus on providing a thorough analysis of all aspects of the data relevant to the {category} and the specified reports."""
|
394 |
+
else:
|
395 |
+
prompt += """Please provide a general analysis of the data, including:
|
396 |
+
1. A comprehensive overview of the data
|
397 |
+
2. Key observations and trends
|
398 |
+
3. Any anomalies, interesting patterns, or correlations
|
399 |
+
4. Suggestions for further analysis or visualization
|
400 |
+
5. Address any previous feedback or insights mentioned above, if applicable
|
401 |
+
|
402 |
+
Focus on providing a thorough analysis of all aspects of the data."""
|
403 |
+
|
404 |
+
response = client.chat.completions.create(
|
405 |
+
model="gpt-4o-mini",
|
406 |
+
messages=[
|
407 |
+
{"role": "system", "content": f"You are a data analyst expert in interpreting Excel data for {'general' if category == 'general' else category} analysis."},
|
408 |
+
{"role": "user", "content": prompt}
|
409 |
+
]
|
410 |
+
)
|
411 |
+
|
412 |
+
return response.choices[0].message.content
|
413 |
+
|
414 |
+
def analyze_document_with_gpt(document_content, user_feedback, category, reports_needed, use_assistants_api=False, file_id=None):
|
415 |
+
if use_assistants_api:
|
416 |
+
assistant = client.beta.assistants.create(
|
417 |
+
name="Document Analyzer",
|
418 |
+
instructions=f"You are a document analysis expert. Analyze the uploaded document and provide insights based on the category: {category}.",
|
419 |
+
model="gpt-4-1106-preview"
|
420 |
+
)
|
421 |
+
|
422 |
+
thread = client.beta.threads.create()
|
423 |
+
|
424 |
+
message = client.beta.threads.messages.create(
|
425 |
+
thread_id=thread.id,
|
426 |
+
role="user",
|
427 |
+
content=f"Analyze the document with file ID: {file_id}. Category: {category}. Required reports: {', '.join(reports_needed)}. User feedback: {user_feedback}",
|
428 |
+
file_ids=[file_id]
|
429 |
+
)
|
430 |
+
|
431 |
+
run = client.beta.threads.runs.create(
|
432 |
+
thread_id=thread.id,
|
433 |
+
assistant_id=assistant.id
|
434 |
+
)
|
435 |
+
|
436 |
+
while run.status != "completed":
|
437 |
+
run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)
|
438 |
+
time.sleep(1)
|
439 |
+
|
440 |
+
messages = client.beta.threads.messages.list(thread_id=thread.id)
|
441 |
+
return messages.data[0].content[0].text.value
|
442 |
+
else:
|
443 |
+
# Existing OCR-based analysis code
|
444 |
+
prompt = f"""Analyze the following document content:
|
445 |
+
|
446 |
+
{document_content}
|
447 |
+
|
448 |
+
User's previous feedback and insights:
|
449 |
+
{user_feedback}
|
450 |
+
|
451 |
+
"""
|
452 |
+
|
453 |
+
if category != "general":
|
454 |
+
prompt += f"""Please provide analysis and insights based on the following required reports for the category '{category}':
|
455 |
+
{', '.join(reports_needed)}
|
456 |
+
|
457 |
+
Please provide:
|
458 |
+
1. A comprehensive overview of the content focusing on the {category} category
|
459 |
+
2. Key points and main ideas related to the required reports
|
460 |
+
3. Any interesting patterns or unique aspects relevant to the {category}
|
461 |
+
4. Suggestions for further analysis or insights based on the required reports
|
462 |
+
5. Any limitations of the analysis due to the document format or OCR process
|
463 |
+
6. Address any previous feedback or insights mentioned above, if applicable
|
464 |
+
|
465 |
+
Focus on providing a thorough analysis of all aspects of the content relevant to the {category} and the specified reports."""
|
466 |
+
else:
|
467 |
+
prompt += """Please provide a general analysis of the document content, including:
|
468 |
+
1. A comprehensive overview of the content
|
469 |
+
2. Key points and main ideas
|
470 |
+
3. Any interesting patterns or unique aspects
|
471 |
+
4. Suggestions for further analysis or insights
|
472 |
+
5. Any limitations of the analysis due to the document format or OCR process
|
473 |
+
6. Address any previous feedback or insights mentioned above, if applicable
|
474 |
+
|
475 |
+
Focus on providing a thorough analysis of all aspects of the content."""
|
476 |
+
|
477 |
+
response = client.chat.completions.create(
|
478 |
+
model="gpt-4o-mini",
|
479 |
+
messages=[
|
480 |
+
{"role": "system", "content": f"You are a data analyst expert in interpreting complex document content for {'general' if category == 'general' else category} analysis."},
|
481 |
+
{"role": "user", "content": prompt}
|
482 |
+
]
|
483 |
+
)
|
484 |
+
|
485 |
+
return response.choices[0].message.content
|
486 |
+
|
487 |
+
def process_uploaded_file(uploaded_file):
|
488 |
+
file_type = uploaded_file.type
|
489 |
+
if file_type in ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.ms-excel"]:
|
490 |
+
# Process Excel file
|
491 |
+
dfs = process_excel(uploaded_file)
|
492 |
+
return "excel", dfs
|
493 |
+
elif file_type == "application/pdf":
|
494 |
+
# Process PDF file using PyPDF2
|
495 |
+
try:
|
496 |
+
pdf_reader = PdfReader(uploaded_file)
|
497 |
+
text = ""
|
498 |
+
for page in pdf_reader.pages:
|
499 |
+
text += page.extract_text()
|
500 |
+
return "text", text
|
501 |
+
except Exception as e:
|
502 |
+
st.error(f"Error processing PDF: {str(e)}")
|
503 |
+
print(f"Error processing PDF: {str(e)}")
|
504 |
+
print(traceback.format_exc())
|
505 |
+
return None, None
|
506 |
+
elif file_type in ["application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/msword"]:
|
507 |
+
# Process Word document
|
508 |
+
try:
|
509 |
+
doc = Document(io.BytesIO(uploaded_file.read()))
|
510 |
+
text = "\n".join([paragraph.text for paragraph in doc.paragraphs])
|
511 |
+
return "text", text
|
512 |
+
except Exception as e:
|
513 |
+
st.error(f"Error processing Word document: {str(e)}")
|
514 |
+
print(f"Error processing Word document: {str(e)}")
|
515 |
+
print(traceback.format_exc())
|
516 |
+
return None, None
|
517 |
+
else:
|
518 |
+
st.error(f"Unsupported file type: {file_type}. Please upload an Excel file, PDF, or Word document.")
|
519 |
+
return None, None
|
520 |
+
|
521 |
+
def chat_with_data(data, user_question, data_type):
|
522 |
+
if data_type == "excel":
|
523 |
+
df_string = data.to_string()
|
524 |
+
data_description = "Excel sheet data"
|
525 |
+
else: # PDF or Word document
|
526 |
+
df_string = data
|
527 |
+
data_description = "document content"
|
528 |
+
|
529 |
+
prompt = f"""You are an AI assistant specialized in analyzing {data_description}. You have access to the following data:
|
530 |
+
|
531 |
+
{df_string}
|
532 |
+
|
533 |
+
Based on this data, please answer the following question:
|
534 |
+
{user_question}
|
535 |
+
|
536 |
+
Provide a detailed and accurate answer based on the given data. If the answer cannot be directly inferred from the data, provide the best possible response based on the available information and your general knowledge about data analysis."""
|
537 |
+
|
538 |
+
response = client.chat.completions.create(
|
539 |
+
model="gpt-4o-mini",
|
540 |
+
messages=[
|
541 |
+
{"role": "system", "content": f"You are a data analysis expert skilled in interpreting {data_description}."},
|
542 |
+
{"role": "user", "content": prompt}
|
543 |
+
]
|
544 |
+
)
|
545 |
+
|
546 |
+
return response.choices[0].message.content
|
547 |
+
|
548 |
+
def extract_challan_data(pdf_text):
|
549 |
+
data = {}
|
550 |
+
patterns = {
|
551 |
+
'ITNS No.': r'ITNS No\.\s*:\s*(\d+)',
|
552 |
+
'TAN': r'TAN\s*:\s*(\w+)',
|
553 |
+
'Name': r'Name\s*:\s*(.+)',
|
554 |
+
'Assessment Year': r'Assessment Year\s*:\s*(\d{4}-\d{2})',
|
555 |
+
'Financial Year': r'Financial Year\s*:\s*(\d{4}-\d{2})',
|
556 |
+
'Amount': r'Amount \(in Rs\.\)\s*:\s*₹\s*([\d,]+)',
|
557 |
+
'CIN': r'CIN\s*:\s*(\w+)',
|
558 |
+
'Date of Deposit': r'Date of Deposit\s*:\s*(\d{2}-\w{3}-\d{4})',
|
559 |
+
'Challan No': r'Challan No\s*:\s*(\d+)',
|
560 |
+
}
|
561 |
+
|
562 |
+
for key, pattern in patterns.items():
|
563 |
+
match = re.search(pattern, pdf_text)
|
564 |
+
if match:
|
565 |
+
data[key] = match.group(1)
|
566 |
+
else:
|
567 |
+
data[key] = 'N/A'
|
568 |
+
|
569 |
+
return data
|
570 |
+
|
571 |
+
def process_challan_pdfs(pdf_files):
|
572 |
+
all_data = []
|
573 |
+
for pdf_file in pdf_files:
|
574 |
+
pdf_text = process_pdf(pdf_file)
|
575 |
+
challan_data = extract_challan_data(pdf_text)
|
576 |
+
all_data.append(challan_data)
|
577 |
+
|
578 |
+
df = pd.DataFrame(all_data)
|
579 |
+
return df
|
580 |
+
|
581 |
+
def process_file_with_assistant(file, file_type, category, reports_needed, user_feedback):
|
582 |
+
print(f"Starting {file_type} processing with Assistant")
|
583 |
+
try:
|
584 |
+
# Upload the file to OpenAI
|
585 |
+
uploaded_file = client.files.create(
|
586 |
+
file=file,
|
587 |
+
purpose='assistants'
|
588 |
+
)
|
589 |
+
print(f"File uploaded successfully. File ID: {uploaded_file.id}")
|
590 |
+
|
591 |
+
# Create an assistant
|
592 |
+
assistant = client.beta.assistants.create(
|
593 |
+
name=f"{file_type} Analyzer",
|
594 |
+
instructions=f"You are an expert in analyzing {file_type} files, focusing on {category}. Provide insights and summaries of the content based on the following reports: {', '.join(reports_needed)}. Consider the user's previous feedback: {user_feedback}",
|
595 |
+
model="gpt-4o",
|
596 |
+
tools=[{"type": "file_search"}]
|
597 |
+
)
|
598 |
+
print(f"Assistant created. Assistant ID: {assistant.id}")
|
599 |
+
|
600 |
+
# Create a thread
|
601 |
+
thread = client.beta.threads.create()
|
602 |
+
print(f"Thread created. Thread ID: {thread.id}")
|
603 |
+
|
604 |
+
# Add a message to the thread with the file attachment
|
605 |
+
message = client.beta.threads.messages.create(
|
606 |
+
thread_id=thread.id,
|
607 |
+
role="user",
|
608 |
+
content=f"Please analyze this file and provide insights for the {category} category, focusing on the following reports: {', '.join(reports_needed)}.",
|
609 |
+
attachments=[
|
610 |
+
{"file_id": uploaded_file.id, "tools": [{"type": "file_search"}]}
|
611 |
+
]
|
612 |
+
)
|
613 |
+
print(f"Message added to thread. Message ID: {message.id}")
|
614 |
+
|
615 |
+
# Run the assistant
|
616 |
+
run = client.beta.threads.runs.create(
|
617 |
+
thread_id=thread.id,
|
618 |
+
assistant_id=assistant.id
|
619 |
+
)
|
620 |
+
print(f"Run created. Run ID: {run.id}")
|
621 |
+
|
622 |
+
# Wait for the run to complete
|
623 |
+
while run.status != 'completed':
|
624 |
+
run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)
|
625 |
+
print(f"Run status: {run.status}")
|
626 |
+
time.sleep(1)
|
627 |
+
|
628 |
+
# Retrieve the messages
|
629 |
+
messages = client.beta.threads.messages.list(thread_id=thread.id)
|
630 |
+
|
631 |
+
# Extract the assistant's response
|
632 |
+
analysis_result = next((msg.content[0].text.value for msg in messages if msg.role == 'assistant'), None)
|
633 |
+
|
634 |
+
print(f"{file_type} analysis completed successfully")
|
635 |
+
return analysis_result
|
636 |
+
|
637 |
+
except Exception as e:
|
638 |
+
print(f"Error in process_file_with_assistant: {str(e)}")
|
639 |
+
print(traceback.format_exc())
|
640 |
+
return None
|
641 |
+
|
642 |
+
# Streamlit UI
|
643 |
+
st.set_page_config(layout="wide")
|
644 |
+
st.title("Document Processing, Chat, Excel Filling, and Analysis")
|
645 |
+
|
646 |
+
# Add login/signup system
|
647 |
+
if "user" not in st.session_state:
|
648 |
+
st.session_state.user = None
|
649 |
+
|
650 |
+
def login(username, password):
|
651 |
+
user = users_collection.find_one({"username": username})
|
652 |
+
if user and user.get("password") == password:
|
653 |
+
return user
|
654 |
+
return None
|
655 |
+
|
656 |
+
def signup(username, password):
|
657 |
+
existing_user = users_collection.find_one({"username": username})
|
658 |
+
if existing_user:
|
659 |
+
return False
|
660 |
+
users_collection.insert_one({
|
661 |
+
"username": username,
|
662 |
+
"password": password,
|
663 |
+
"feedback": [] # Initialize an empty list for feedback
|
664 |
+
})
|
665 |
+
return True
|
666 |
+
|
667 |
+
def store_feedback(username, feedback):
|
668 |
+
users_collection.update_one(
|
669 |
+
{"username": username},
|
670 |
+
{"$push": {"feedback": feedback}},
|
671 |
+
upsert=True
|
672 |
+
)
|
673 |
+
|
674 |
+
# Login/Signup form
|
675 |
+
if not st.session_state.user:
|
676 |
+
tab1, tab2 = st.tabs(["Login", "Sign Up"])
|
677 |
+
|
678 |
+
with tab1:
|
679 |
+
st.subheader("Login")
|
680 |
+
login_username = st.text_input("Username", key="login_username")
|
681 |
+
login_password = st.text_input("Password", type="password", key="login_password")
|
682 |
+
if st.button("Login"):
|
683 |
+
user = login(login_username, login_password)
|
684 |
+
if user:
|
685 |
+
st.session_state.user = user
|
686 |
+
st.success("Logged in successfully!")
|
687 |
+
st.rerun()
|
688 |
+
else:
|
689 |
+
st.error("Invalid username or password")
|
690 |
+
|
691 |
+
with tab2:
|
692 |
+
st.subheader("Sign Up")
|
693 |
+
signup_username = st.text_input("Username", key="signup_username")
|
694 |
+
signup_password = st.text_input("Password", type="password", key="signup_password")
|
695 |
+
if st.button("Sign Up"):
|
696 |
+
if signup(signup_username, signup_password):
|
697 |
+
st.success("Account created successfully! Please log in.")
|
698 |
+
else:
|
699 |
+
st.error("Username already exists")
|
700 |
+
|
701 |
+
if st.session_state.user:
|
702 |
+
st.write(f"Welcome, {st.session_state.user['username']}!")
|
703 |
+
|
704 |
+
# Create four tabs
|
705 |
+
tab1, tab2, tab3, tab4 = st.tabs(["Upload, Chat, and Source", "Excel Processing", "Excel Analysis and Chat", "Challan Processing"])
|
706 |
+
|
707 |
+
with tab1:
|
708 |
+
st.subheader("Upload")
|
709 |
+
|
710 |
+
# PDF upload
|
711 |
+
uploaded_files = st.file_uploader("Choose one or more PDF files", type="pdf", accept_multiple_files=True)
|
712 |
+
|
713 |
+
# Web Link input
|
714 |
+
web_link = st.text_input("Enter a Web Link")
|
715 |
+
|
716 |
+
# YouTube Links input
|
717 |
+
youtube_links = st.text_area("Enter YouTube Links (one per line)")
|
718 |
+
|
719 |
+
if st.button("Process All"):
|
720 |
+
st.session_state.upload_progress = st.progress(0)
|
721 |
+
with st.spinner("Processing uploads..."):
|
722 |
+
results = []
|
723 |
+
if uploaded_files:
|
724 |
+
for file in uploaded_files:
|
725 |
+
pdf_result = process_upload("PDF", file, file.name)
|
726 |
+
results.append(pdf_result)
|
727 |
+
if web_link:
|
728 |
+
web_result = process_upload("Web Link", web_link)
|
729 |
+
results.append(web_result)
|
730 |
+
if youtube_links:
|
731 |
+
youtube_links_list = re.split(r'[\n\r]+', youtube_links.strip())
|
732 |
+
youtube_results = process_youtube_links(youtube_links_list)
|
733 |
+
results.extend(youtube_results)
|
734 |
+
|
735 |
+
if results:
|
736 |
+
for result in results:
|
737 |
+
st.success(result)
|
738 |
+
else:
|
739 |
+
st.warning("No content uploaded. Please provide at least one input.")
|
740 |
+
st.session_state.upload_progress.empty()
|
741 |
+
|
742 |
+
st.subheader("Chat")
|
743 |
+
user_input = st.text_input("Ask a question about the uploaded content:")
|
744 |
+
if st.button("Send"):
|
745 |
+
if user_input:
|
746 |
+
print(f"Sending user input: {user_input}")
|
747 |
+
st.session_state.chat_progress = st.progress(0)
|
748 |
+
response, sources = chat_with_ai(user_input)
|
749 |
+
st.session_state.chat_progress.progress(1.0)
|
750 |
+
st.markdown("**You:** " + user_input)
|
751 |
+
st.markdown("**AI:** " + response)
|
752 |
+
|
753 |
+
# Store sources in session state for display in the Source Chunks section
|
754 |
+
st.session_state.sources = sources
|
755 |
+
st.session_state.chat_progress.empty()
|
756 |
+
else:
|
757 |
+
print("Empty user input")
|
758 |
+
st.warning("Please enter a question.")
|
759 |
+
|
760 |
+
st.subheader("Source Chunks")
|
761 |
+
if 'sources' in st.session_state and st.session_state.sources:
|
762 |
+
for i, source in enumerate(st.session_state.sources, 1):
|
763 |
+
with st.expander(f"Source {i} - {source['type']} ({source['doc_name']}) - Score: {source['score']}"):
|
764 |
+
st.markdown(f"**Chunk Index:** {source['chunk_index']}")
|
765 |
+
st.text(source['text'])
|
766 |
+
else:
|
767 |
+
st.info("Ask a question to see source chunks here.")
|
768 |
+
|
769 |
+
with tab2:
|
770 |
+
st.subheader("Excel Processing")
|
771 |
+
uploaded_excel = st.file_uploader("Choose an Excel file", type=["xlsx", "xls"])
|
772 |
+
|
773 |
+
if uploaded_excel is not None:
|
774 |
+
dfs = process_excel(uploaded_excel)
|
775 |
+
|
776 |
+
# Display preview of each sheet
|
777 |
+
for sheet_name, df in dfs.items():
|
778 |
+
if not df.empty:
|
779 |
+
st.write(f"Preview of {sheet_name}:")
|
780 |
+
st.dataframe(df.head())
|
781 |
+
|
782 |
+
# Select the main sheet for processing
|
783 |
+
main_sheet = st.selectbox("Select the main sheet to fill", list(dfs.keys()))
|
784 |
+
main_df = dfs[main_sheet]
|
785 |
+
|
786 |
+
if st.button("Fill Missing Data"):
|
787 |
+
with st.spinner("Analyzing data and generating formulas..."):
|
788 |
+
other_dfs = {name: df for name, df in dfs.items() if name != main_sheet}
|
789 |
+
formulas = analyze_and_generate_formulas(main_df, other_dfs)
|
790 |
+
|
791 |
+
if formulas:
|
792 |
+
st.write("Generated Formulas:")
|
793 |
+
for column, formula in formulas.items():
|
794 |
+
st.code(f"{column}: {formula}")
|
795 |
+
|
796 |
+
filled_df = apply_formulas(main_df, other_dfs, formulas)
|
797 |
+
|
798 |
+
st.write("Filled Excel Data:")
|
799 |
+
st.dataframe(filled_df)
|
800 |
+
|
801 |
+
# Provide download link for the filled Excel file
|
802 |
+
buffer = io.BytesIO()
|
803 |
+
with pd.ExcelWriter(buffer, engine='xlsxwriter') as writer:
|
804 |
+
filled_df.to_excel(writer, index=False, sheet_name=main_sheet)
|
805 |
+
# Also save other sheets
|
806 |
+
for sheet_name, df in dfs.items():
|
807 |
+
if sheet_name != main_sheet:
|
808 |
+
df.to_excel(writer, index=False, sheet_name=sheet_name)
|
809 |
+
buffer.seek(0)
|
810 |
+
st.download_button(
|
811 |
+
label="Download Filled Excel",
|
812 |
+
data=buffer,
|
813 |
+
file_name="filled_excel.xlsx",
|
814 |
+
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
815 |
+
)
|
816 |
+
else:
|
817 |
+
st.warning("No formulas were generated. The data might not have clear patterns for filling missing values.")
|
818 |
+
|
819 |
+
with tab3:
|
820 |
+
st.subheader("Excel and Document Analysis")
|
821 |
+
uploaded_file = st.file_uploader(
|
822 |
+
"Choose an Excel file, PDF, or Word document for analysis",
|
823 |
+
type=["xlsx", "xls", "pdf", "docx", "doc"],
|
824 |
+
key="excel_analysis_uploader"
|
825 |
+
)
|
826 |
+
|
827 |
+
if uploaded_file is not None:
|
828 |
+
file_type, content = process_uploaded_file(uploaded_file)
|
829 |
+
|
830 |
+
if file_type is not None and content is not None:
|
831 |
+
if file_type == "excel":
|
832 |
+
dfs = content
|
833 |
+
sheet_names = list(dfs.keys())
|
834 |
+
selected_sheet = st.selectbox("Select a sheet for analysis", sheet_names)
|
835 |
+
|
836 |
+
df_to_analyze = dfs[selected_sheet]
|
837 |
+
st.write(f"Preview of {selected_sheet}:")
|
838 |
+
st.dataframe(df_to_analyze.head())
|
839 |
+
|
840 |
+
st.session_state.analyzed_data = df_to_analyze
|
841 |
+
analysis_method = "OCR" # Default to OCR for Excel files
|
842 |
+
elif file_type == "text":
|
843 |
+
st.write("Document content preview:")
|
844 |
+
preview_text = content[:500] + "..."
|
845 |
+
st.text(preview_text) # Show first 500 characters
|
846 |
+
|
847 |
+
# Add option to choose between OCR and Assistants API for PDF/Word
|
848 |
+
analysis_method = st.radio("Choose analysis method:", ("OCR", "OpenAI Assistants API"))
|
849 |
+
|
850 |
+
st.session_state.analyzed_data = content
|
851 |
+
|
852 |
+
# Add category selection with "Default" option
|
853 |
+
categories = list(get_category_reports().keys())
|
854 |
+
if "Default" in categories:
|
855 |
+
categories.remove("Default")
|
856 |
+
categories = ["Default"] + categories
|
857 |
+
selected_category = st.selectbox("Select analysis category", categories)
|
858 |
+
|
859 |
+
if st.button("Analyze with GPT"):
|
860 |
+
with st.spinner("Analyzing data... This may take a while for large datasets."):
|
861 |
+
user_feedback = get_user_feedback(st.session_state.user["_id"])
|
862 |
+
reports_needed = get_category_reports().get(selected_category, [])
|
863 |
+
|
864 |
+
if file_type == "excel":
|
865 |
+
analysis_result = analyze_excel_with_gpt(st.session_state.analyzed_data, selected_sheet, user_feedback, selected_category, reports_needed)
|
866 |
+
else: # PDF or Word document
|
867 |
+
if analysis_method == "OpenAI Assistants API":
|
868 |
+
analysis_result = process_file_with_assistant(uploaded_file, "PDF", selected_category, reports_needed, user_feedback)
|
869 |
+
else:
|
870 |
+
analysis_result = analyze_document_with_gpt(st.session_state.analyzed_data, user_feedback, selected_category, reports_needed)
|
871 |
+
|
872 |
+
st.markdown("## Analysis Results")
|
873 |
+
st.markdown(analysis_result)
|
874 |
+
|
875 |
+
st.session_state.analysis_result = analysis_result
|
876 |
+
|
877 |
+
if file_type == "excel":
|
878 |
+
pdf_path = excel_to_pdf(st.session_state.analyzed_data)
|
879 |
+
with open(pdf_path, "rb") as pdf_file:
|
880 |
+
pdf_bytes = pdf_file.read()
|
881 |
+
st.download_button(
|
882 |
+
label="Download Excel PDF version",
|
883 |
+
data=pdf_bytes,
|
884 |
+
file_name="excel_data.pdf",
|
885 |
+
mime="application/pdf"
|
886 |
+
)
|
887 |
+
|
888 |
+
# Feedback section
|
889 |
+
st.markdown("## Feedback")
|
890 |
+
new_feedback = st.text_area("Provide feedback or additional insights about the analysis:")
|
891 |
+
if st.button("Submit Feedback"):
|
892 |
+
if new_feedback:
|
893 |
+
user = users_collection.find_one({"_id": st.session_state.user["_id"]})
|
894 |
+
existing_feedback = user.get("feedback", "")
|
895 |
+
|
896 |
+
updated_feedback = f"{existing_feedback}\n{new_feedback}" if existing_feedback else new_feedback
|
897 |
+
|
898 |
+
users_collection.update_one(
|
899 |
+
{"_id": st.session_state.user["_id"]},
|
900 |
+
{"$set": {"feedback": updated_feedback}}
|
901 |
+
)
|
902 |
+
st.success("Feedback submitted successfully!")
|
903 |
+
else:
|
904 |
+
st.warning("Please enter some feedback before submitting.")
|
905 |
+
|
906 |
+
# Chat with Data section
|
907 |
+
st.markdown("## Chat with Data")
|
908 |
+
with st.form(key='chat_form'):
|
909 |
+
user_question = st.text_input("Ask a question about the data:")
|
910 |
+
chat_submit_button = st.form_submit_button(label='Get Answer')
|
911 |
+
|
912 |
+
if chat_submit_button:
|
913 |
+
if user_question:
|
914 |
+
with st.spinner("Analyzing your question..."):
|
915 |
+
answer = chat_with_data(st.session_state.analyzed_data, user_question, file_type)
|
916 |
+
st.markdown("### Answer")
|
917 |
+
st.markdown(answer)
|
918 |
+
else:
|
919 |
+
st.warning("Please enter a question about the data.")
|
920 |
+
else:
|
921 |
+
st.error("Unable to process the uploaded file. Please check the file format and try again.")
|
922 |
+
|
923 |
+
with tab4:
|
924 |
+
st.subheader("Challan Processing")
|
925 |
+
|
926 |
+
challan_pdfs = st.file_uploader(
|
927 |
+
"Choose Challan PDF files",
|
928 |
+
type="pdf",
|
929 |
+
accept_multiple_files=True,
|
930 |
+
key="challan_processing_uploader"
|
931 |
+
)
|
932 |
+
|
933 |
+
if st.button("Process Challan PDFs"):
|
934 |
+
if challan_pdfs:
|
935 |
+
with st.spinner("Processing Challan PDFs..."):
|
936 |
+
challan_df = process_challan_pdfs(challan_pdfs)
|
937 |
+
st.write("Challan Data:")
|
938 |
+
st.dataframe(challan_df)
|
939 |
+
|
940 |
+
buffer = io.BytesIO()
|
941 |
+
with pd.ExcelWriter(buffer, engine='xlsxwriter') as writer:
|
942 |
+
challan_df.to_excel(writer, index=False, sheet_name='Challan Data')
|
943 |
+
buffer.seek(0)
|
944 |
+
st.download_button(
|
945 |
+
label="Download Challan Excel",
|
946 |
+
data=buffer,
|
947 |
+
file_name="challan_data.xlsx",
|
948 |
+
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
949 |
+
)
|
950 |
+
|
951 |
+
st.success("Challan PDFs processed successfully")
|
952 |
+
else:
|
953 |
+
st.warning("No Challan PDFs uploaded. Please choose at least one PDF file.")
|