standard app name
This commit is contained in:
		
							
								
								
									
										214
									
								
								app.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								app.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | ||||
| import pandas as pd  | ||||
| import vobject | ||||
| import requests | ||||
| from datetime import datetime, date | ||||
| import matplotlib.pyplot as plt | ||||
| import calmap | ||||
| import streamlit as st  | ||||
| import plotly.express as px | ||||
| import plotly.graph_objects as go | ||||
| import json | ||||
| import os | ||||
|  | ||||
|  | ||||
|  | ||||
| # Function to load data from a JSON file | ||||
| def load_data(file_path): | ||||
|     try: | ||||
|         with open(file_path, 'r') as file: | ||||
|             if os.path.getsize(file_path) == 0: return {} | ||||
|             return json.load(file) | ||||
|     except FileNotFoundError: | ||||
|         print("settings file not found") | ||||
|         return {} | ||||
|     except : | ||||
|         print("settings file invalid") | ||||
|         return {} | ||||
|  | ||||
|  | ||||
| # Function to save data to a JSON file | ||||
| def save_data(file_path, data): | ||||
|     with open(file_path, 'w') as file: | ||||
|         json.dump(data, file) | ||||
|  | ||||
|  | ||||
| # Function to create a calendar heatmap for a specific year | ||||
| def create_calendar_heatmap(event_series, year): | ||||
|     # Filter the series to include only the specific year | ||||
|     event_series_year = event_series[event_series.index.year == year] | ||||
|  | ||||
|     # Create a complete date range for the year | ||||
|     all_dates_year = pd.date_range(start=f'{year}-01-01', end=f'{year}-12-31') | ||||
|  | ||||
|     # Reindex the Series to include all dates in the year and fill missing dates with zero | ||||
|     event_series_year = event_series_year.reindex(all_dates_year, fill_value=0) | ||||
|  | ||||
|     # Create a DataFrame for Plotly | ||||
|     df_year = pd.DataFrame({'Date': event_series_year.index, 'Events': event_series_year.values}) | ||||
|  | ||||
|     # Generate day of the week and week of the year | ||||
|     df_year['DayOfWeek'] = df_year['Date'].dt.dayofweek | ||||
|     df_year['WeekOfYear'] = df_year['Date'].dt.isocalendar().week | ||||
|     df_year['Month'] = df_year['Date'].dt.strftime('%b') | ||||
|  | ||||
|     # Create the Plotly figure | ||||
|     fig = go.Figure(data=go.Heatmap( | ||||
|         z=df_year['Events'], | ||||
|         x=df_year['WeekOfYear'], | ||||
|         y=df_year['DayOfWeek'], | ||||
|         colorscale='YlGn', | ||||
|         showscale=True, | ||||
|         colorbar=dict( | ||||
|             title='# of Events', | ||||
|             orientation='h',  # Set the color scale to horizontal | ||||
|             len=0.3,  # Adjust the length of the color scale | ||||
|             thickness=10, | ||||
|             x=0.98, | ||||
|             y=1, | ||||
|             xanchor="right", | ||||
|             yanchor="bottom",  # Position the color scale at the bottom | ||||
|         ), | ||||
|         hoverinfo='z+x+y' | ||||
|     )) | ||||
|  | ||||
|     # Set the aspect ratio to ensure square boxes | ||||
|     fig.update_yaxes(scaleanchor="x", scaleratio=1, constrain="domain") | ||||
|  | ||||
|     # Update the layout with the axes | ||||
|     fig.update_layout( | ||||
|         title=f'Calendar Heatmap for {year}', | ||||
|         yaxis=dict( | ||||
|             tickmode='array', | ||||
|             tickvals=[0, 1, 2, 3, 4, 5, 6], | ||||
|             ticktext=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], | ||||
|             range=[-1.2, 6.5], # Adjust the range to eliminate space | ||||
|             automargin=True, # Adjust margins automatically  | ||||
|             domain=[0.1,0.95] # Use available space effectively | ||||
|         ), | ||||
|         xaxis=dict( | ||||
|             tickmode='array', | ||||
|             tickvals=list(range(1, 54)), | ||||
|             ticktext=list(range(1, 54)), | ||||
|             title=dict( | ||||
|                 text='Weeks of the Year', | ||||
|                 standoff=0 | ||||
|             ), | ||||
|             title_standoff=0,  # Adjust the distance of the title from the axis | ||||
|             tickangle=-90,  # Adjust the angle of the week numbers | ||||
|             ticklen=2, | ||||
|             side='top', # Display x-axis ticks above the heatmap | ||||
|             automargin=True, # Adjust margins automatically | ||||
|         ), | ||||
|         autosize=True, | ||||
|         margin=dict( | ||||
|             l=50, | ||||
|             r=50, | ||||
|             b=30,  # Increase bottom margin to fit month names | ||||
|             t=30,  # Increase top margin to fit week numbers | ||||
|             pad=0 | ||||
|         ), | ||||
|         height=450, # Fixed height to avoid excessive space | ||||
|     ) | ||||
|  | ||||
|     # Add month annotations with arrows at the bottom | ||||
|     month_starts = df_year.iloc[:-10].groupby('Month')['WeekOfYear'].min().to_dict() | ||||
|     for month, week in month_starts.items(): | ||||
|         fig.add_annotation( | ||||
|             x=week, | ||||
|             y=-0.5,  # Position below the x-axis | ||||
|             text=month, | ||||
|             showarrow=False, | ||||
|             arrowhead=2, | ||||
|             ax=week, | ||||
|             ay=0,  # Position the arrow closer | ||||
|             xref="x", | ||||
|             yref="y", | ||||
|             #font=dict(size=12, color="black"), | ||||
|             xanchor='left', | ||||
|             yanchor='top' | ||||
|         ) | ||||
|      | ||||
|     return fig | ||||
|  | ||||
|  | ||||
| def save_load(): | ||||
|     st.session_state['options'].pop("New") | ||||
|     save_data(settings_file, st.session_state['options']) | ||||
|     st.session_state['options'] = load_data(settings_file) | ||||
|      | ||||
| def update_params(): | ||||
|     st.session_state['cal_url'] = st.session_state['options'][st.session_state['setup']]['cal_url'] | ||||
|     st.session_state['number_of_years'] = st.session_state['options'][st.session_state['setup']]['number_of_years'] | ||||
|  | ||||
|  | ||||
| # set webpage title | ||||
| st.title("Yearly Activity Heatmaps") | ||||
|  | ||||
| # File path for data storage | ||||
| settings_file = 'settings.json' | ||||
| # Load persisted data if it exists; set default values otherwise | ||||
| st.session_state['options'] = load_data(settings_file) | ||||
| st.session_state['options']["New"] = { | ||||
|     'cal_url':"https://canada-holidays.ca/ics?cd=true", | ||||
|     'number_of_years': 3 | ||||
|     } | ||||
|  | ||||
| # Get the Calendar Export link and number of years from sidebar menu | ||||
| st.sidebar.header("Settings") | ||||
| st.session_state['setup'] = st.sidebar.selectbox("Choose a Setup:", st.session_state['options'].keys()) | ||||
| st.session_state['cal_url'] = st.sidebar.text_input("Enter The Calendar Export Link:", value=st.session_state['options'][st.session_state['setup']]['cal_url']) | ||||
| st.session_state['number_of_years'] = st.sidebar.number_input("Enter the number of years:", min_value=1, max_value=10, value=st.session_state['options'][st.session_state['setup']]['number_of_years']) | ||||
| update_params() | ||||
| if(st.session_state['setup'] == "New"): | ||||
|     setup_save_name = st.sidebar.text_input("Setup Save Name:", value='yahoo') | ||||
|     st.session_state['options'][setup_save_name] = { | ||||
|         'cal_url': st.session_state['cal_url'], | ||||
|         'number_of_years': st.session_state['number_of_years'] | ||||
|     } | ||||
|     st.sidebar.button("Save",on_click=save_load) | ||||
|  | ||||
|  | ||||
| response = requests.get(st.session_state['cal_url']) | ||||
| if response.status_code == 200: | ||||
|     # Parse the .ics file | ||||
|     cal = vobject.readOne(response.text) | ||||
| else: | ||||
|     print("Failed to download the file") | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| # Create a dictionary to store event counts by date | ||||
| event_counts = {} | ||||
| for component in cal.components(): | ||||
|     if component.name == "VEVENT": | ||||
|         event_date = component.dtstart.value | ||||
|         if isinstance(event_date, datetime): | ||||
|             event_date = event_date.date() | ||||
|         if event_date in event_counts: | ||||
|             event_counts[event_date] += 1 | ||||
|         else: | ||||
|             event_counts[event_date] = 1 | ||||
|  | ||||
|  | ||||
|  | ||||
| # Convert the dictionary to a pandas Series | ||||
| event_series = pd.Series(event_counts) | ||||
| # Ensure the index is in datetime format | ||||
| event_series.index = pd.to_datetime(event_series.index) | ||||
|  | ||||
|  | ||||
| # Get the current year | ||||
| current_year = datetime.now().year | ||||
| # Create a list of years to plot | ||||
| years_to_plot = [current_year - i for i in range(st.session_state['number_of_years'])] | ||||
|  | ||||
|  | ||||
|  | ||||
| # Plot each year's data | ||||
| for year in years_to_plot: | ||||
|     fig = create_calendar_heatmap(event_series, year) | ||||
|     st.plotly_chart(fig) | ||||
|  | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user