Skip to content

Commit 1991bb3

Browse files
committed
Merge branch 'Develop'
2 parents 4380325 + 2acad46 commit 1991bb3

23 files changed

+2986
-10069
lines changed

Axiora.py

Lines changed: 110 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# Import Qt modules first
77
from PySide6.QtWidgets import (
88
QApplication, QMainWindow, QHeaderView, QLabel,
9-
QVBoxLayout, QSizePolicy, QPushButton
9+
QVBoxLayout, QSizePolicy, QPushButton, QGridLayout, QWidget, QFrame
1010
)
1111
from PySide6.QtGui import QIcon, QFont, QPixmap, QCursor
1212
from PySide6.QtCore import Qt, QSize
@@ -19,6 +19,7 @@
1919
from langchain_core.messages import HumanMessage, AIMessage
2020
from OprFuncs import read_file
2121
from modules.ui_main import Ui_MainWindow
22+
from uiEXT.ColDialog import ColDialog
2223

2324

2425
def resizeEvent(self, event):
@@ -47,7 +48,7 @@ def __init__(self, user_id):
4748

4849
# Initialize app functions after UI setup
4950
self.app_functions = GuiFunctions(self, self.user_id)
50-
self.load_reports()
51+
self.load_oldreports()
5152

5253
# Fix path separators for Windows - use forward slashes
5354
self.report_logo = "images/icons/cil-report-colored-1.png"
@@ -156,30 +157,107 @@ def openCloseRightBox():
156157
widgets.home_2.layout().addWidget(welcome_label)
157158
widgets.btn_home.setStyleSheet(UIFunctions.selectMenu(widgets.btn_home.styleSheet()))
158159

159-
def load_reports(self):
160-
#self.report_list.clear()
161-
reports = self.app_functions.db.get_user_reports(self.user_id)
162-
for report in reports:
163-
report_btn = QPushButton(self.ui.topMenus)
164-
report_btn.setObjectName(report['name'])
165-
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
166-
sizePolicy1.setHeightForWidth(report_btn.sizePolicy().hasHeightForWidth())
167-
report_btn.setSizePolicy(sizePolicy1)
168-
report_btn.setMinimumSize(QSize(0, 45))
169-
# report_btn.setFont(Qfont)
170-
report_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
171-
report_btn.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
172-
report_btn.setText(report['name'])
173-
report_btn.setProperty("report_id", report['id'])
174-
report_btn.setProperty("report_name", report['name'])
175-
176-
report_logo = "images/icons/cil-report-colored-1.png"
177-
pixmap_report_logo = QPixmap(report_logo)
178-
logo_icon = QIcon(pixmap_report_logo)
179-
report_btn.setIcon(logo_icon)
180-
181-
report_btn.clicked.connect(self.report_button_clicked)
182-
self.ui.verticalLayout_14.addWidget(report_btn)
160+
# Connect column header click event
161+
widgets.tableData.horizontalHeader().sectionClicked.connect(self.show_column_dialog)
162+
163+
def load_oldreports(self):
164+
165+
# Create a grid layout for the home page
166+
if hasattr(self.ui, 'home_2'):
167+
# Clear existing layout if any
168+
if self.ui.home_2.layout():
169+
QWidget().setLayout(self.ui.home_2.layout())
170+
171+
# Create new grid layout
172+
grid_layout = QGridLayout(self.ui.home_2)
173+
grid_layout.setSpacing(10)
174+
grid_layout.setContentsMargins(20, 20, 20, 20)
175+
176+
# Create welcome message widget for top left
177+
welcome_widget = QWidget()
178+
welcome_layout = QVBoxLayout(welcome_widget)
179+
welcome_layout.addStretch()
180+
181+
# Add welcome widget to top left
182+
grid_layout.addWidget(welcome_widget, 0, 0)
183+
184+
# Create reports container for top right
185+
reports_container = QWidget()
186+
reports_layout = QVBoxLayout(reports_container)
187+
reports_layout.setSpacing(5)
188+
reports_layout.setContentsMargins(0, 0, 0, 0)
189+
190+
# Add title
191+
title_label = QLabel("Your Reports")
192+
title_label.setStyleSheet("""
193+
QLabel {
194+
font-size: 16px;
195+
font-weight: bold;
196+
color: white;
197+
padding: 10px;
198+
background-color: rgb(196, 7, 105);
199+
border-radius: 4px;
200+
}
201+
""")
202+
title_label.setAlignment(Qt.AlignCenter)
203+
reports_layout.addWidget(title_label)
204+
reports_layout.addSpacing(10)
205+
206+
reports = self.app_functions.db.get_user_reports(self.user_id)
207+
for report in reports:
208+
report_btn = QPushButton()
209+
report_btn.setObjectName(report['name'])
210+
report_btn.setMinimumHeight(40)
211+
report_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
212+
report_btn.setText(report['name'])
213+
report_btn.setProperty("report_id", report['id'])
214+
report_btn.setProperty("report_name", report['name'])
215+
216+
# Set button style
217+
report_btn.setStyleSheet("""
218+
QPushButton {
219+
background-color: rgb(24, 196, 199);
220+
border: 1px solid #ddd;
221+
border-radius: 4px;
222+
padding: 5px 10px;
223+
text-align: left;
224+
}
225+
QPushButton:hover {
226+
background-color: rgb(7, 60, 196);
227+
}
228+
""")
229+
230+
# Add icon
231+
report_logo = "images/icons/cil-report-colored-1.png"
232+
pixmap_report_logo = QPixmap(report_logo)
233+
if not pixmap_report_logo.isNull():
234+
scaled_pixmap = pixmap_report_logo.scaled(24, 24, Qt.KeepAspectRatio, Qt.SmoothTransformation)
235+
report_btn.setIcon(QIcon(scaled_pixmap))
236+
report_btn.setIconSize(QSize(24, 24))
237+
238+
report_btn.clicked.connect(self.report_button_clicked)
239+
reports_layout.addWidget(report_btn)
240+
241+
# Add reports container to top right
242+
grid_layout.addWidget(reports_container, 0, 1)
243+
244+
# Add empty widgets for bottom left and right
245+
bottom_left = QWidget()
246+
bottom_right = QWidget()
247+
grid_layout.addWidget(bottom_left, 1, 0)
248+
grid_layout.addWidget(bottom_right, 1, 1)
249+
250+
# Add horizontal line
251+
horizontal_line = QFrame()
252+
horizontal_line.setFrameShape(QFrame.Shape.HLine)
253+
horizontal_line.setStyleSheet("background-color: #ddd;")
254+
grid_layout.addWidget(horizontal_line, 1, 0, 1, 2)
255+
256+
# Add vertical line
257+
vertical_line = QFrame()
258+
vertical_line.setFrameShape(QFrame.Shape.VLine)
259+
vertical_line.setStyleSheet("background-color: #ddd;")
260+
grid_layout.addWidget(vertical_line, 0, 1, 2, 1)
183261

184262
def report_button_clicked(self):
185263
btn = self.sender()
@@ -313,6 +391,12 @@ def mousePressEvent(self, event):
313391
if event.buttons() == Qt.RightButton:
314392
print('Mouse click: RIGHT CLICK')
315393

394+
def show_column_dialog(self, column_index):
395+
"""Show the column dialog when a column header is clicked"""
396+
column_name = widgets.tableData.horizontalHeaderItem(column_index).text()
397+
dialog = ColDialog(self, self.app_functions.df, column_name)
398+
dialog.setWindowTitle(f"Column Options - {column_name}")
399+
dialog.exec_()
316400

317401
if __name__ == "__main__":
318402
# Create QApplication instance

Axioradb.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
from passlib.hash import bcrypt
2-
from sqlalchemy.orm import sessionmaker
3-
import sqlalchemy as sa
1+
import bcrypt
42
from sqlalchemy import (
5-
PrimaryKeyConstraint, create_engine, ForeignKey,
6-
Column, String, Integer, CHAR, SmallInteger,
3+
create_engine, ForeignKey,
4+
Column, String, Integer, SmallInteger,
75
Text, DateTime, Boolean
86
)
97
from sqlalchemy import func
@@ -19,20 +17,22 @@ class User(Base):
1917
password_hash = Column(String, nullable=False)
2018
email = Column(String)
2119
preferred_llm = Column(Integer, ForeignKey('llm.llm_id'), nullable=True)
20+
user_context = Column(Text)
2221

2322
reports = relationship("Report", back_populates="user")
2423
llm = relationship("LLM", back_populates="users")
2524

26-
def __init__(self, username, email, password):
25+
def __init__(self, username, email, password, user_context=None):
2726
self.username = username
2827
self.email = email
28+
self.user_context = user_context
2929
self.set_password(password)
3030

3131
def set_password(self, password):
32-
self.password_hash = bcrypt.hash(password)
32+
self.password_hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
3333

3434
def check_password(self, password):
35-
return bcrypt.verify(password, self.password_hash)
35+
return bcrypt.checkpw(password.encode('utf-8'), self.password_hash.encode('utf-8'))
3636

3737
def __repr__(self):
3838
return f"<User(user_id={self.user_id}, username='{self.username}', email='{self.email}')>"
@@ -313,4 +313,4 @@ def __repr__(self):
313313
session.add(cheif)
314314
315315
session.commit()
316-
session.close()"""
316+
session.close()"""

DataAnalyzer.py

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from langchain_experimental.agents import create_pandas_dataframe_agent
1616

1717
class DataAnalyzer:
18-
def __init__(self,dataframe,llm):
18+
def __init__(self,dataframe,llm,user_id=None):
1919
self.dataframe = dataframe
2020
self.llm = llm
2121
self.data_info = data_infer(dataframe)
@@ -25,6 +25,14 @@ def __init__(self,dataframe,llm):
2525
self.db = DatabaseManager()
2626
self.report_id = None
2727
self.memory = []
28+
29+
if user_id:
30+
self.user_id = user_id
31+
self.user_context = self.db.get_user_context(user_id)
32+
if self.user_context:
33+
self.memory.append(HumanMessage(content=f"User Context: {self.user_context}"))
34+
else:
35+
self.user_context = None
2836

2937
def analysis_data(self):
3038
data_info = self.data_info
@@ -35,34 +43,39 @@ def analysis_data(self):
3543
You are a data analyst. You are provided with:
3644
1. Dataset metadata: {data_info}
3745
2. Dataset sample: {data_sample}
38-
3. Dataset summary: {data_description}
46+
3. Dataset summary: {data_description}
47+
4. User_context: {user_context}
3948
4049
Please analyze the data and provide insights about:
4150
1. Key trends and patterns.
4251
3. Recommendations or actionable insights based on the analyzed data.
4352
'''
4453
analysis_prompt = PromptTemplate(
45-
input_variables=["data_info", "data_sample", "data_description"],
54+
input_variables=["data_info", "data_sample", "data_description", "user_context"],
4655
template=analysis_template
4756
)
4857

4958
analysis_chain = analysis_prompt | self.llm
5059

51-
analysis = analysis_chain.invoke({
60+
self.analysis = analysis_chain.invoke({
5261
"data_info": data_info,
5362
"data_sample": data_sample,
54-
"data_description": data_description
63+
"data_description": data_description,
64+
"user_context":self.user_context or "No prior context available"
5565
})
5666

57-
formatted_analysis_prompt = analysis_template.format(data_info=data_info,data_sample=data_sample,data_description=data_description)
67+
formatted_analysis_prompt = analysis_template.format(data_info=data_info,data_sample=data_sample,
68+
data_description=data_description,
69+
user_context=self.user_context)
5870
self.memory.append(HumanMessage(content=formatted_analysis_prompt))
59-
self.memory.append(AIMessage(content=analysis))
71+
self.memory.append(AIMessage(content=self.analysis))
6072
self.db.saveMemory(reportID=self.report_id,
6173
llm=self.db.llm_id_by_name(self.llm.model),
6274
prompet=formatted_analysis_prompt,
63-
response=analysis,
75+
response=self.analysis,
6476
chat=False)
65-
return analysis
77+
self.generate_user_context()
78+
return self.analysis
6679

6780
# Drop Nulls
6881
def drop_nulls(self):
@@ -130,22 +143,26 @@ def questions_gen(self, num):
130143
"data_description": data_description
131144
})
132145

133-
print("🔹 Raw LLM Output:", repr(generated_questions))
146+
# Ensure the response is properly encoded
147+
if isinstance(generated_questions, str):
148+
generated_questions = generated_questions.encode('utf-8', 'replace').decode('utf-8')
149+
150+
print("Raw LLM Output:", repr(generated_questions))
134151

135152
if not generated_questions.strip():
136-
print("⚠️ LLM did not generate any questions.")
153+
print("Warning: LLM did not generate any questions.")
137154
return []
138155

139156
# Use the improved extraction function
140157
questions_list = extract_questions(generated_questions)
141158

142-
print("🟢 Extracted Questions List:", questions_list)
159+
print("Extracted Questions List:", questions_list)
143160

144161
# Trim or handle missing questions
145162
if len(questions_list) > num:
146163
questions_list = questions_list[:num]
147164
elif len(questions_list) < num:
148-
print(f"⚠️ Warning: Expected {num} questions, but got {len(questions_list)}")
165+
print(f"Warning: Expected {num} questions, but got {len(questions_list)}")
149166

150167
# Store in memory
151168
formatted_question_prompt = question_template.format(
@@ -165,7 +182,7 @@ def questions_gen(self, num):
165182
return questions_list
166183

167184
except Exception as e:
168-
print(f"Error generating questions: {e}")
185+
print(f"Error generating questions: {str(e)}")
169186
return []
170187

171188

@@ -289,3 +306,37 @@ def get_chart_recommendation(self, question: str) -> Tuple[str, List[str]]:
289306
chart_type = self.select_chart_type(question)
290307
columns = self.select_columns(question)
291308
return chart_type, columns
309+
310+
def generate_user_context(self):
311+
if not self.user_id:
312+
return "No user ID provided"
313+
314+
context_template = """
315+
Generate a concise user profile context based on:
316+
317+
User's existing context: {existing_context}
318+
Current analysis: {current_analysis}
319+
Conversation history: {conversation_summary}
320+
Focus on:
321+
- Key analysis interests
322+
- Frequently asked about metrics
323+
- Data domains of interest
324+
325+
Format as bullet points, max 5 items.
326+
"""
327+
328+
conversation = "\n".join([msg.content for msg in self.memory[-4:]])
329+
330+
context_prompt = PromptTemplate(
331+
template=context_template,
332+
input_variables=["existing_context", "current_analysis", "conversation_summary"]
333+
)
334+
335+
new_context = (context_prompt | self.llm).invoke({
336+
"existing_context": self.user_context or "No prior context available",
337+
"current_analysis": self.analysis,
338+
"conversation_summary": conversation
339+
})
340+
341+
self.db.update_user_context(userID=self.user_id, new_context=new_context)
342+
return new_context

DatabaseManager.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,18 @@ def get_user_name(self,userID):
156156
user_name = self.session.query(User.username).filter(User.user_id == userID).first()
157157
return user_name if user_name else None
158158

159+
def get_user_context(self, userID):
160+
user = self.session.query(User).filter(User.user_id == userID).first()
161+
return user.user_context if user else None
162+
163+
def update_user_context(self, userID, new_context):
164+
user = self.session.query(User).filter(User.user_id == userID).first()
165+
if user:
166+
user.user_context = new_context
167+
self.session.commit()
168+
return True
169+
return False
170+
159171
def get_report_charts(self, reportID):
160172
charts = self.session.query(Charts.chart_path)\
161173
.join(Dashboards, Charts.dashboard_id == Dashboards.dashboard_id)\

0 commit comments

Comments
 (0)