Spaces:
Configuration error
Configuration error
| 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.") |