task-tracker / app.py
Ezhumalai13's picture
Upload 2 files
9492f63 verified
import streamlit as st
import pandas as pd
from datetime import datetime
import time
import os
from io import BytesIO
from fpdf import FPDF
from apscheduler.schedulers.background import BackgroundScheduler
from plyer import notification
import plotly.express as px
# --- PAGE CONFIGURATION ---
st.set_page_config(page_title="The Task Tracker", page_icon="βœ…", layout="wide")
# --- THEME CSS (Navy, Black, Gold) ---
st.markdown("""
<style>
/* GLOBAL FONT */
html, body, [class*="css"] {
font-family: 'Times New Roman', Times, serif;
}
/* BACKGROUND */
.stApp {
background-color: #F8F9FA;
color: #000000;
}
/* --- HEADER CSS --- */
.header-container {
background: linear-gradient(135deg, #000000 0%, #001f3f 100%);
padding: 30px;
border-radius: 12px;
color: white;
display: flex;
align-items: center;
justify-content: flex-start;
border-bottom: 4px solid #D4AF37;
margin-bottom: 30px;
box-shadow: 0 10px 20px rgba(0,0,0,0.3);
}
.svg-logo-container {
margin-right: 30px;
filter: drop-shadow(0px 0px 8px rgba(212, 175, 55, 0.5));
}
.title-block h1 {
font-family: 'Times New Roman', serif;
font-weight: 800;
letter-spacing: 2px;
margin: 0;
font-size: 3rem;
text-transform: uppercase;
color: #FFFFFF;
}
.title-block p {
font-size: 1.2rem;
font-style: italic;
color: #D4AF37;
margin-top: 10px;
font-weight: 500;
}
/* BUTTONS */
.stButton>button {
background-color: #001f3f;
color: white;
border-radius: 2px;
border: 1px solid #000000;
font-weight: bold;
font-size: 16px;
transition: 0.3s;
}
.stButton>button:hover {
background-color: #003366;
border-color: #D4AF37;
}
/* TABS */
.stTabs [data-baseweb="tab-list"] { gap: 10px; }
.stTabs [data-baseweb="tab"] {
height: 50px;
background-color: #E0E0E0;
border-radius: 4px 4px 0px 0px;
color: #000000;
font-weight: bold;
}
.stTabs [aria-selected="true"] {
background-color: #001f3f;
color: white;
border-bottom: 3px solid #D4AF37;
}
/* METRICS */
div[data-testid="stMetricValue"] {
color: #001f3f;
}
</style>
""", unsafe_allow_html=True)
# --- BACKEND FUNCTIONS ---
DATA_FILE = "task_tracker_elite.csv"
def load_data():
if os.path.exists(DATA_FILE):
df = pd.read_csv(DATA_FILE)
if "Category" not in df.columns:
df["Category"] = "General"
return df
else:
return pd.DataFrame(columns=["Date", "Category", "Task", "Status"])
def save_data(df):
df.to_csv(DATA_FILE, index=False)
def trigger_notification():
try:
notification.notify(
title='The Task Tracker',
message='Execution is key. Update your progress.',
timeout=10
)
except:
pass
def to_excel(df):
output = BytesIO()
with pd.ExcelWriter(output, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name='Sheet1')
return output.getvalue()
def to_pdf(df, start_date, end_date):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", 'B', 20)
pdf.set_text_color(0, 31, 63)
pdf.cell(200, 15, txt="PERFORMANCE REPORT", ln=1, align='C')
pdf.set_font("Times", 'I', 12)
pdf.cell(200, 10, txt=f"Period: {start_date} to {end_date}", ln=1, align='C')
pdf.set_text_color(0, 0, 0)
pdf.set_font("Times", size=11)
pdf.ln(10)
cols = ["Date", "Category", "Task", "Status"]
pdf.set_fill_color(220, 220, 220)
for col in cols:
pdf.cell(45, 10, col, 1, 0, 'C', True)
pdf.ln()
for _, row in df.iterrows():
pdf.cell(45, 10, str(row['Date']), 1)
pdf.cell(45, 10, str(row['Category']), 1)
pdf.cell(45, 10, str(row['Task'])[:20], 1)
pdf.cell(45, 10, str(row['Status']), 1)
pdf.ln()
return pdf.output(dest='S').encode('latin-1')
# --- SCHEDULER ---
if 'scheduler_active' not in st.session_state:
scheduler = BackgroundScheduler()
scheduler.add_job(trigger_notification, 'cron', hour=21, minute=0)
scheduler.start()
st.session_state['scheduler_active'] = True
# --- UI LOGIC ---
# 1. UPDATED HEADER & TAGLINE
st.markdown("""
<div class="header-container">
<div class="svg-logo-container">
<svg width="100px" height="100px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 3L6 21" stroke="#001f3f" stroke-width="2" stroke-linecap="round"/>
<path d="M18 3L18 21" stroke="#001f3f" stroke-width="2" stroke-linecap="round"/>
<path d="M6 7H18" stroke="#001f3f" stroke-width="2" stroke-linecap="round"/>
<path d="M6 12H18" stroke="#001f3f" stroke-width="2" stroke-linecap="round"/>
<path d="M6 17H18" stroke="#001f3f" stroke-width="2" stroke-linecap="round"/>
<circle cx="12" cy="8" r="2" fill="#D4AF37"/>
<path d="M12 10V14L10 16.5" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 14L14 16.5" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 11L12 10L14 11" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 11L16 8" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="title-block">
<h1>THE TASK TRACKER</h1>
<p>"Planning, Focus, Discipline & Execution – these are the Mantras of the life."</p>
</div>
</div>
""", unsafe_allow_html=True)
df = load_data()
today_str = datetime.now().strftime("%Y-%m-%d")
# 2. UPDATED TAB NAMES
tab_planner, tab_analytics = st.tabs(["πŸ“ Daily Task", "πŸ“Š Performance Metrics"])
# --- TAB 1: DAILY TASK ---
with tab_planner:
st.markdown("### πŸ“‹ Plan Your Execution")
with st.container():
c1, c2, c3 = st.columns([2, 1, 1])
with c1:
new_task = st.text_input("Task Description", placeholder="What is the next objective?")
with c2:
category = st.selectbox("Category", ["Work πŸ’Ό", "Personal 🏠", "Finance πŸ’°", "Health πŸ’ͺ", "Priority ⚑"])
with c3:
st.write("")
st.write("")
add_btn = st.button("ADD TASK", use_container_width=True)
if add_btn and new_task:
clean_cat = category.split(" ")[0]
new_entry = pd.DataFrame([{"Date": today_str, "Category": clean_cat, "Task": new_task, "Status": "Pending"}])
df = pd.concat([df, new_entry], ignore_index=True)
save_data(df)
st.toast("Task successfully added.", icon="βœ…")
time.sleep(0.5)
st.rerun()
st.markdown("---")
today_tasks = df[df['Date'] == today_str]
if not today_tasks.empty:
total_today = len(today_tasks)
completed_count = len(today_tasks[today_tasks['Status'] == "Completed"])
progress_val = int((completed_count / total_today) * 100) if total_today > 0 else 0
st.write(f"### πŸ“‰ Daily Discipline: {progress_val}%")
st.progress(progress_val)
categories = today_tasks['Category'].unique()
with st.form("status_update_form"):
for cat in categories:
st.markdown(f"#### πŸ”Ή {cat}")
cat_tasks = today_tasks[today_tasks['Category'] == cat]
for index, row in cat_tasks.iterrows():
c_task, c_status = st.columns([3, 2])
with c_task:
if row['Status'] == "Completed":
st.markdown(f"βœ… ~~{row['Task']}~~")
elif row['Status'] == "Missed":
st.markdown(f"❌ **{row['Task']}**")
else:
st.markdown(f"**{row['Task']}**")
with c_status:
options = ["Pending", "Completed", "Missed"]
try:
default_ix = options.index(row['Status'])
except:
default_ix = 0
st.radio("Status", options, key=f"status_{index}", index=default_ix, horizontal=True, label_visibility="collapsed")
st.write("")
if st.form_submit_button("CONFIRM PROGRESS"):
# Save changes
for index, row in today_tasks.iterrows():
key = f"status_{index}"
if key in st.session_state:
df.at[index, 'Status'] = st.session_state[key]
save_data(df)
# 3. NEW YEAR EVE EFFECT LOGIC
# Trigger massive celebration on any update to simulate the "Sky at New Year"
st.balloons() # Rising crackers
st.snow() # Falling glitter/ash from crackers
st.toast("πŸŽ† πŸ’₯ 🧨 BOOM! Progress Recorded! 🧨 πŸ’₯ πŸŽ†", icon="✨")
current_completed = len(df[(df['Date'] == today_str) & (df['Status'] == "Completed")])
if current_completed == total_today and total_today > 0:
st.success("πŸ† EXECUTION COMPLETE! You mastered the day.")
else:
st.info("Progress updated. Keep pushing.")
time.sleep(2)
st.rerun()
else:
st.info("No tasks defined. Start by planning your day above.")
# --- TAB 2: PERFORMANCE METRICS ---
with tab_analytics:
st.header("πŸ“Š Performance Metrics")
st.markdown("##### πŸ—“οΈ Select Reporting Period")
c_date1, c_date2 = st.columns(2)
start_date = c_date1.date_input("Start Date")
end_date = c_date2.date_input("End Date")
st.markdown("---")
if not df.empty:
mask = (df['Date'] >= str(start_date)) & (df['Date'] <= str(end_date))
filtered_df = df.loc[mask]
if not filtered_df.empty:
total_all = len(filtered_df)
total_done = len(filtered_df[filtered_df['Status'] == "Completed"])
total_missed = len(filtered_df[filtered_df['Status'] == "Missed"])
c1, c2, c3 = st.columns(3)
c1.metric("Total Tasks", total_all)
c2.metric("Executed (Done)", total_done)
c3.metric("Missed (Failed)", total_missed)
st.write("")
g1, g2 = st.columns(2)
with g1:
st.subheader("Consistency Chart")
daily_stats = filtered_df.groupby(['Date', 'Status']).size().reset_index(name='Count')
color_map = {
"Completed": "#001f3f",
"Missed": "#000000",
"Pending": "#808080"
}
fig_bar = px.bar(
daily_stats, x="Date", y="Count", color="Status",
barmode="group", color_discrete_map=color_map,
title="Daily Execution"
)
fig_bar.update_layout(font_family="Times New Roman")
st.plotly_chart(fig_bar, use_container_width=True)
with g2:
st.subheader("Category Breakdown")
cat_counts = filtered_df['Category'].value_counts().reset_index()
cat_counts.columns = ['Category', 'Count']
fig_pie = px.pie(
cat_counts, values='Count', names='Category', hole=0.4,
color_discrete_sequence=px.colors.sequential.Blues_r,
title="Focus Distribution"
)
fig_pie.update_layout(font_family="Times New Roman")
st.plotly_chart(fig_pie, use_container_width=True)
st.write("### πŸ“₯ Download Reports")
d1, d2 = st.columns(2)
d1.download_button(
"Export Excel Log",
data=to_excel(filtered_df),
file_name=f"Task_Data_{start_date}_{end_date}.xlsx"
)
d2.download_button(
"Export PDF Report",
data=to_pdf(filtered_df, start_date, end_date),
file_name=f"Task_Report_{start_date}_{end_date}.pdf"
)
else:
st.warning(f"No data found between {start_date} and {end_date}.")
else:
st.info("No data available in database.")