Spaces:
Sleeping
Sleeping
Upload 7 files
Browse files- app.py +120 -0
- llm_part.py +100 -0
- requirements.txt +6 -0
- secret_key.py +2 -0
- task_operations.py +112 -0
- task_visualization.py +54 -0
- tasks.db +0 -0
app.py
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import streamlit as st
|
3 |
+
from task_operations import TaskManager
|
4 |
+
from task_visualization import TaskVisualizer
|
5 |
+
from llm_part import get_task_advice # Import the LLM advice function
|
6 |
+
import pandas as pd
|
7 |
+
from datetime import datetime
|
8 |
+
|
9 |
+
def main():
|
10 |
+
st.title("Daily Task Tracker")
|
11 |
+
|
12 |
+
task_manager = TaskManager()
|
13 |
+
visualizer = TaskVisualizer()
|
14 |
+
|
15 |
+
# Ensure tasks are loaded from the database into session state
|
16 |
+
if 'tasks' not in st.session_state:
|
17 |
+
st.session_state.tasks = task_manager.load_tasks()
|
18 |
+
|
19 |
+
# Task input fields
|
20 |
+
task_name = st.text_input("Task Name")
|
21 |
+
task_time = st.time_input("Task Time")
|
22 |
+
task_duration_hours = st.number_input("Task Duration (Hours)", min_value=0, step=1, format="%d")
|
23 |
+
task_duration_minutes = st.number_input("Task Duration (Minutes)", min_value=0, max_value=59, step=1, format="%d")
|
24 |
+
task_category = st.selectbox("Task Category", TaskManager.CATEGORIES)
|
25 |
+
|
26 |
+
# Create a button to add the task
|
27 |
+
if st.button("Add Task"):
|
28 |
+
# Add the task to the task manager
|
29 |
+
task_manager.add_task(task_name, task_time, task_duration_hours, task_duration_minutes, task_category)
|
30 |
+
st.success(f"Task '{task_name}' added!")
|
31 |
+
|
32 |
+
# Get advice from both LLMs
|
33 |
+
advice_gemini, advice_llama = get_task_advice(task_name, task_category, task_duration_hours, task_duration_minutes)
|
34 |
+
|
35 |
+
# Store the responses in session state
|
36 |
+
st.session_state.advice_gemini = advice_gemini
|
37 |
+
st.session_state.advice_llama = advice_llama
|
38 |
+
|
39 |
+
# Create tabs for LLM advice display
|
40 |
+
tab1, tab2 = st.tabs(["Gemini LLM", "Llama LLM"])
|
41 |
+
|
42 |
+
with tab1:
|
43 |
+
st.header("Gemini LLM")
|
44 |
+
st.write("**Gemini Advice:**")
|
45 |
+
st.write(st.session_state.advice_gemini)
|
46 |
+
|
47 |
+
with tab2:
|
48 |
+
st.header("Llama LLM")
|
49 |
+
st.write("**Llama Advice:**")
|
50 |
+
st.write(st.session_state.advice_llama)
|
51 |
+
|
52 |
+
# Display tasks for today only
|
53 |
+
if st.session_state.tasks:
|
54 |
+
st.write("Today's Tasks:")
|
55 |
+
|
56 |
+
# Convert session tasks to DataFrame and filter tasks for today
|
57 |
+
df = pd.DataFrame(st.session_state.tasks)
|
58 |
+
df['Task Time'] = pd.to_datetime(df['Task Time'])
|
59 |
+
today_tasks = df[df['Task Time'].dt.date == datetime.today().date()]
|
60 |
+
|
61 |
+
if not today_tasks.empty:
|
62 |
+
st.table(today_tasks[['Task ID', 'Task Name', 'Task Time', 'Task Duration (hours)',
|
63 |
+
'Task Duration (minutes)', 'Category']])
|
64 |
+
else:
|
65 |
+
st.write("No tasks for today.")
|
66 |
+
|
67 |
+
# Task deletion option by ID
|
68 |
+
task_id_to_delete = st.number_input("Enter Task ID to Delete", min_value=0, step=1)
|
69 |
+
if st.button("Delete Task by ID"):
|
70 |
+
if task_manager.delete_task_by_id(task_id_to_delete):
|
71 |
+
st.success(f"Task with ID '{task_id_to_delete}' deleted!")
|
72 |
+
else:
|
73 |
+
st.error(f"Task with ID '{task_id_to_delete}' not found.")
|
74 |
+
|
75 |
+
# Report options and other visualizations remain unchanged
|
76 |
+
if st.button("Daily Report"):
|
77 |
+
report = task_manager.generate_report('daily')
|
78 |
+
if not report.empty:
|
79 |
+
st.write("Daily Report:")
|
80 |
+
st.dataframe(report)
|
81 |
+
visualizer.plot_category_performance('daily', task_manager)
|
82 |
+
else:
|
83 |
+
st.warning("No tasks for today.")
|
84 |
+
|
85 |
+
if st.button("Weekly Report"):
|
86 |
+
report = task_manager.generate_report('weekly')
|
87 |
+
if not report.empty:
|
88 |
+
st.write("Weekly Report:")
|
89 |
+
st.dataframe(report)
|
90 |
+
visualizer.plot_category_performance('weekly', task_manager)
|
91 |
+
else:
|
92 |
+
st.warning("No tasks for this week.")
|
93 |
+
|
94 |
+
if st.button("Monthly Report"):
|
95 |
+
report = task_manager.generate_report('monthly')
|
96 |
+
if not report.empty:
|
97 |
+
st.write("Monthly Report:")
|
98 |
+
st.dataframe(report)
|
99 |
+
visualizer.plot_category_performance('monthly', task_manager)
|
100 |
+
else:
|
101 |
+
st.warning("No tasks for this month.")
|
102 |
+
|
103 |
+
if st.button("Yearly Report"):
|
104 |
+
report = task_manager.generate_report('yearly')
|
105 |
+
if not report.empty:
|
106 |
+
st.write("Yearly Report:")
|
107 |
+
st.dataframe(report)
|
108 |
+
visualizer.plot_category_performance('yearly', task_manager)
|
109 |
+
else:
|
110 |
+
st.warning("No tasks for this year.")
|
111 |
+
|
112 |
+
# Performance visualizations
|
113 |
+
visualizer.plot_performance()
|
114 |
+
visualizer.plot_overall_category_performance()
|
115 |
+
|
116 |
+
# Option to download the report
|
117 |
+
visualizer.download_report()
|
118 |
+
|
119 |
+
if __name__ == "__main__":
|
120 |
+
main()
|
llm_part.py
ADDED
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# llm_part.py
|
2 |
+
import os
|
3 |
+
from langchain.prompts import PromptTemplate
|
4 |
+
from langchain.chains import LLMChain
|
5 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
6 |
+
from langchain_groq import ChatGroq
|
7 |
+
from secret_key import gemeni_key, llama_key
|
8 |
+
from datetime import datetime
|
9 |
+
|
10 |
+
# Initialize LLM models
|
11 |
+
llm_1 = ChatGoogleGenerativeAI(model="gemini-pro", api_key=gemeni_key)
|
12 |
+
llm_2 = ChatGroq(model="llama3-groq-70b-8192-tool-use-preview", groq_api_key=llama_key)
|
13 |
+
|
14 |
+
# Prayer times for the current day only (Fajr, Dhuhr, Asr, Maghrib, Isha)
|
15 |
+
prayer_times = {
|
16 |
+
"Fajr": "04:30",
|
17 |
+
"Dhuhr": "11:45",
|
18 |
+
"Asr": "15:30",
|
19 |
+
"Maghrib": "17:45",
|
20 |
+
"Isha": "19:00"
|
21 |
+
}
|
22 |
+
|
23 |
+
# Define the next prayer function
|
24 |
+
def get_next_prayer(current_time):
|
25 |
+
# Retrieve today's prayer times
|
26 |
+
times = list(prayer_times.values())
|
27 |
+
prayer_names = list(prayer_times.keys())
|
28 |
+
|
29 |
+
# Current time in comparable format
|
30 |
+
current_hour = current_time.hour
|
31 |
+
current_minute = current_time.minute
|
32 |
+
|
33 |
+
for prayer, time_str in zip(prayer_names, times):
|
34 |
+
prayer_hour, prayer_minute = map(int, time_str.split(':'))
|
35 |
+
if (prayer_hour > current_hour) or (prayer_hour == current_hour and prayer_minute > current_minute):
|
36 |
+
return prayer, time_str # Return both prayer name and time
|
37 |
+
|
38 |
+
return "Isha", times[-1]
|
39 |
+
|
40 |
+
|
41 |
+
templates = {
|
42 |
+
"Learning": "You just completed a learning task: {task_name}. You studied for {hours} hours and {minutes} minutes. "
|
43 |
+
"Can you suggest improvements to my learning process? What should I learn next?",
|
44 |
+
|
45 |
+
"Gym": "You completed a gym task: {task_name}. It lasted {hours} hours and {minutes} minutes. "
|
46 |
+
"How can I optimize my workout? What should I focus on next time in the gym?",
|
47 |
+
|
48 |
+
"Personal": "I just completed a personal task: {task_name}. It took me {hours} hours and {minutes} minutes. "
|
49 |
+
"What advice can you give for better time management or improvement?",
|
50 |
+
|
51 |
+
"Work": "I completed a work-related task: {task_name}, which lasted {hours} hours and {minutes} minutes. "
|
52 |
+
"Can you suggest ways to improve my work efficiency? What should I work on next?",
|
53 |
+
|
54 |
+
"Prayer": "You completed a prayer task: {task_name}. It took you {hours} hours and {minutes} minutes. "
|
55 |
+
"What practical steps can I take to deepen my spiritual connection during Salah (prayer)?"
|
56 |
+
"Please provide specific traditions and teachings for the next prayer, **{next_prayer}**, which is at **{next_time}**. "
|
57 |
+
"Format your response as bullet points for clarity."
|
58 |
+
}
|
59 |
+
|
60 |
+
# Function to select a template based on the task category
|
61 |
+
def get_template(category):
|
62 |
+
return templates.get(category, "You just completed the task: {task_name}. It took {hours} hours and {minutes} minutes. "
|
63 |
+
"What advice can you give? What is the next most productive task?")
|
64 |
+
|
65 |
+
# Template function to generate advice and next task recommendations for each task
|
66 |
+
def get_task_advice(task_name, task_category, hours, minutes):
|
67 |
+
# Get the template based on the category
|
68 |
+
template_str = get_template(task_category)
|
69 |
+
|
70 |
+
# Determine the next prayer and its time
|
71 |
+
current_time = datetime.now()
|
72 |
+
next_prayer, next_time = get_next_prayer(current_time)
|
73 |
+
|
74 |
+
# Create the prompt
|
75 |
+
prompt_template = PromptTemplate(
|
76 |
+
input_variables=["task_name", "hours", "minutes", "next_prayer", "next_time"],
|
77 |
+
template=template_str
|
78 |
+
)
|
79 |
+
|
80 |
+
prompt = prompt_template.format(task_name=task_name, hours=hours, minutes=minutes,
|
81 |
+
next_prayer=next_prayer, next_time=next_time)
|
82 |
+
|
83 |
+
# Set up LLMChain for both models
|
84 |
+
chain_1 = LLMChain(llm=llm_1, prompt=prompt_template)
|
85 |
+
chain_2 = LLMChain(llm=llm_2, prompt=prompt_template)
|
86 |
+
|
87 |
+
try:
|
88 |
+
# Run the LLMChain to get the responses
|
89 |
+
response_llm1 = chain_1.run({"task_name": task_name, "hours": hours, "minutes": minutes,
|
90 |
+
"next_prayer": next_prayer, "next_time": next_time})
|
91 |
+
except Exception as e:
|
92 |
+
response_llm1 = f"Error generating response from Gemini: {str(e)}"
|
93 |
+
|
94 |
+
try:
|
95 |
+
response_llm2 = chain_2.run({"task_name": task_name, "hours": hours, "minutes": minutes,
|
96 |
+
"next_prayer": next_prayer, "next_time": next_time})
|
97 |
+
except Exception as e:
|
98 |
+
response_llm2 = f"Error generating response from Llama: {str(e)}"
|
99 |
+
|
100 |
+
return response_llm1, response_llm2
|
requirements.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
pandas
|
2 |
+
matplotlib
|
3 |
+
langchain
|
4 |
+
langchain-google-genai
|
5 |
+
langchain-groq
|
6 |
+
langchain_community
|
secret_key.py
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
gemeni_key="AIzaSyCLyDgZNcE_v4wLMFF8SoimKga9bbLSun0"
|
2 |
+
llama_key="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"
|
task_operations.py
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# components/task_operations.py
|
2 |
+
|
3 |
+
import sqlite3
|
4 |
+
import pandas as pd
|
5 |
+
from datetime import datetime
|
6 |
+
import streamlit as st
|
7 |
+
|
8 |
+
class TaskManager:
|
9 |
+
DB_FILE = "tasks.db"
|
10 |
+
CATEGORIES = ['Learning', 'Gym', 'Personal', 'Family', 'Work', 'Prayer']
|
11 |
+
|
12 |
+
def __init__(self):
|
13 |
+
self.conn = sqlite3.connect(self.DB_FILE)
|
14 |
+
self.create_table()
|
15 |
+
|
16 |
+
def create_table(self):
|
17 |
+
# Create tasks table with essential fields
|
18 |
+
query = """
|
19 |
+
CREATE TABLE IF NOT EXISTS tasks (
|
20 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
21 |
+
task_name TEXT NOT NULL,
|
22 |
+
task_time TEXT NOT NULL,
|
23 |
+
task_duration_hours INTEGER NOT NULL,
|
24 |
+
task_duration_minutes INTEGER NOT NULL,
|
25 |
+
category TEXT NOT NULL
|
26 |
+
);
|
27 |
+
"""
|
28 |
+
self.conn.execute(query)
|
29 |
+
self.conn.commit()
|
30 |
+
|
31 |
+
def load_tasks(self):
|
32 |
+
# Load tasks from the database
|
33 |
+
query = "SELECT * FROM tasks;"
|
34 |
+
df = pd.read_sql_query(query, self.conn)
|
35 |
+
|
36 |
+
if not df.empty:
|
37 |
+
# Rename columns explicitly to avoid conflicts
|
38 |
+
column_mapping = {
|
39 |
+
'task_name': 'Task Name',
|
40 |
+
'task_time': 'Task Time',
|
41 |
+
'task_duration_hours': 'Task Duration (hours)',
|
42 |
+
'task_duration_minutes': 'Task Duration (minutes)',
|
43 |
+
'category': 'Category',
|
44 |
+
'id': 'Task ID'
|
45 |
+
}
|
46 |
+
|
47 |
+
# Check for any conflicts before renaming
|
48 |
+
existing_columns = set(df.columns)
|
49 |
+
for original, new_name in column_mapping.items():
|
50 |
+
if new_name in existing_columns and original != new_name:
|
51 |
+
raise ValueError(f"Conflict detected: Column '{new_name}' already exists in the DataFrame.")
|
52 |
+
|
53 |
+
# Rename columns in the DataFrame
|
54 |
+
df.rename(columns=column_mapping, inplace=True)
|
55 |
+
|
56 |
+
# Convert 'task_time' to datetime after renaming
|
57 |
+
df['Task Time'] = pd.to_datetime(df['Task Time'])
|
58 |
+
|
59 |
+
return df.to_dict(orient='records')
|
60 |
+
else:
|
61 |
+
return []
|
62 |
+
|
63 |
+
def save_task(self, task):
|
64 |
+
# Save task into the database
|
65 |
+
query = """
|
66 |
+
INSERT INTO tasks (task_name, task_time, task_duration_hours, task_duration_minutes, category)
|
67 |
+
VALUES (?, ?, ?, ?, ?);
|
68 |
+
"""
|
69 |
+
self.conn.execute(query, (task['Task Name'], task['Task Time'], task['Task Duration (hours)'],
|
70 |
+
task['Task Duration (minutes)'], task['Category']))
|
71 |
+
self.conn.commit()
|
72 |
+
|
73 |
+
def add_task(self, task_name, task_time, task_duration_hours, task_duration_minutes, task_category):
|
74 |
+
task_time_full = datetime.combine(datetime.today(), task_time)
|
75 |
+
task_entry = {
|
76 |
+
"Task Name": task_name,
|
77 |
+
"Task Time": task_time_full,
|
78 |
+
"Task Duration (hours)": int(task_duration_hours),
|
79 |
+
"Task Duration (minutes)": int(task_duration_minutes),
|
80 |
+
"Category": task_category
|
81 |
+
}
|
82 |
+
self.save_task(task_entry)
|
83 |
+
st.session_state.tasks.append(task_entry)
|
84 |
+
|
85 |
+
def delete_task_by_id(self, task_id):
|
86 |
+
# Delete task by its ID
|
87 |
+
query = "DELETE FROM tasks WHERE id = ?;"
|
88 |
+
self.conn.execute(query, (task_id,))
|
89 |
+
self.conn.commit()
|
90 |
+
return True
|
91 |
+
|
92 |
+
def generate_report(self, timeframe):
|
93 |
+
df = pd.DataFrame(self.load_tasks())
|
94 |
+
if df.empty:
|
95 |
+
return pd.DataFrame()
|
96 |
+
|
97 |
+
df['Task Time'] = pd.to_datetime(df['Task Time'])
|
98 |
+
|
99 |
+
if timeframe == 'daily':
|
100 |
+
report = df[df['Task Time'].dt.date == pd.Timestamp.today().date()]
|
101 |
+
elif timeframe == 'weekly':
|
102 |
+
week_start = pd.Timestamp.today() - pd.DateOffset(days=pd.Timestamp.today().dayofweek)
|
103 |
+
report = df[(df['Task Time'] >= week_start) & (df['Task Time'] < pd.Timestamp.today() + pd.DateOffset(days=1))]
|
104 |
+
elif timeframe == 'monthly':
|
105 |
+
report = df[df['Task Time'].dt.month == pd.Timestamp.today().month]
|
106 |
+
elif timeframe == 'yearly':
|
107 |
+
report = df[df['Task Time'].dt.year == pd.Timestamp.today().year]
|
108 |
+
else:
|
109 |
+
report = pd.DataFrame()
|
110 |
+
|
111 |
+
report['Total Duration'] = report['Task Duration (hours)'] + report['Task Duration (minutes)'] / 60.0
|
112 |
+
return report
|
task_visualization.py
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# components/task_visualization.py
|
2 |
+
|
3 |
+
import matplotlib.pyplot as plt
|
4 |
+
import pandas as pd
|
5 |
+
import streamlit as st
|
6 |
+
|
7 |
+
class TaskVisualizer:
|
8 |
+
def plot_performance(self):
|
9 |
+
df = pd.DataFrame(st.session_state.tasks)
|
10 |
+
df['Total Duration'] = df['Task Duration (hours)'] + df['Task Duration (minutes)'] / 60.0
|
11 |
+
|
12 |
+
plt.figure(figsize=(10, 5))
|
13 |
+
task_times = df.groupby('Task Name')['Total Duration'].sum()
|
14 |
+
task_times.plot(kind='bar')
|
15 |
+
plt.xlabel('Task')
|
16 |
+
plt.ylabel('Hours Spent')
|
17 |
+
plt.title('Overall Task Performance')
|
18 |
+
plt.xticks(rotation=45)
|
19 |
+
plt.tight_layout()
|
20 |
+
st.pyplot(plt)
|
21 |
+
|
22 |
+
def plot_category_performance(self, timeframe, task_manager):
|
23 |
+
report = task_manager.generate_report(timeframe)
|
24 |
+
if not report.empty:
|
25 |
+
category_times = report.groupby('Category')['Total Duration'].sum()
|
26 |
+
|
27 |
+
plt.figure(figsize=(10, 5))
|
28 |
+
category_times.plot(kind='bar', color='skyblue')
|
29 |
+
plt.xlabel('Category')
|
30 |
+
plt.ylabel('Total Hours Spent')
|
31 |
+
plt.title(f'Task Performance by Category - {timeframe.capitalize()} Report')
|
32 |
+
plt.xticks(rotation=45)
|
33 |
+
plt.tight_layout()
|
34 |
+
st.pyplot(plt)
|
35 |
+
|
36 |
+
def plot_overall_category_performance(self):
|
37 |
+
df = pd.DataFrame(st.session_state.tasks)
|
38 |
+
df['Total Duration'] = df['Task Duration (hours)'] + df['Task Duration (minutes)'] / 60.0
|
39 |
+
|
40 |
+
category_times = df.groupby('Category')['Total Duration'].sum()
|
41 |
+
|
42 |
+
plt.figure(figsize=(10, 5))
|
43 |
+
category_times.plot(kind='bar', color='lightgreen')
|
44 |
+
plt.xlabel('Category')
|
45 |
+
plt.ylabel('Total Hours Spent')
|
46 |
+
plt.title('Overall Task Performance by Category')
|
47 |
+
plt.xticks(rotation=45)
|
48 |
+
plt.tight_layout()
|
49 |
+
st.pyplot(plt)
|
50 |
+
|
51 |
+
def download_report(self):
|
52 |
+
df = pd.DataFrame(st.session_state.tasks)
|
53 |
+
csv = df.to_csv(index=False)
|
54 |
+
st.download_button("Download CSV", data=csv, file_name="task_report.csv", mime='text/csv')
|
tasks.db
ADDED
Binary file (8.19 kB). View file
|
|