Compare commits
	
		
			6 Commits
		
	
	
		
			594e7c3cce
			...
			new_featur
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e1b817252c | |||
| 2d73ae8d63 | |||
| ab1d541e2c | |||
| cc8de33780 | |||
| 1c41f666dc | |||
| d76db631fd | 
| @ -59,3 +59,17 @@ hr { | ||||
|         grid-template-columns: 1fr; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .feedback-button { | ||||
|     background-color: #007bff; | ||||
|     color: white; | ||||
|     border: none; | ||||
|     padding: 5px 10px; | ||||
|     text-align: center; | ||||
|     text-decoration: none; | ||||
|     display: inline-block; | ||||
|     font-size: 12px; | ||||
|     margin: 2px 2px; | ||||
|     cursor: pointer; | ||||
|     border-radius: 4px; | ||||
| } | ||||
|  | ||||
							
								
								
									
										2
									
								
								data/feedback.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								data/feedback.csv
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| timestamp,category,comment | ||||
| 2025-09-07 20:02:38,groceries,"ABZT please change this air to P1231313" | ||||
| 
 | 
							
								
								
									
										5
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								main.py
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| # initial commit | ||||
| from dash import Dash | ||||
| from dash_bootstrap_components.themes import BOOTSTRAP | ||||
| import dash_bootstrap_components as dbc | ||||
|  | ||||
| from src.components.layout import create_layout | ||||
| from src.data.loader_gz import load_spc_data | ||||
| @ -17,10 +17,11 @@ with open(config_file) as config_f: | ||||
|  | ||||
| def main() -> None: | ||||
|     print(os.getenv("MY_ENV_VAR")) | ||||
|     print(config["Startup"]) | ||||
|     # load the data and create the data manager | ||||
|     data = load_spc_data(config["DATA_PATH"]) | ||||
|  | ||||
|     app = Dash(external_stylesheets=[BOOTSTRAP]) | ||||
|     app = Dash(external_stylesheets=[dbc.themes.LUX]) | ||||
|     app.title = "Reliability Dashboard" | ||||
|     app.layout = create_layout(app, data) | ||||
|     app.run() | ||||
|  | ||||
| @ -7,4 +7,5 @@ requires-python = ">=3.11" | ||||
| dependencies = [ | ||||
|     "dash>=3.2.0", | ||||
|     "dash-bootstrap-components>=2.0.4", | ||||
|     "dotenv>=0.9.9", | ||||
| ] | ||||
|  | ||||
| @ -12,15 +12,15 @@ def render(app: Dash, data: pd.DataFrame) -> html.Div: | ||||
|         Output(ids.BAR_CHART, "children"), | ||||
|         [ | ||||
|             Input(ids.YEAR_DROPDOWN, "value"), | ||||
|             Input(ids.MONTH_DROPDOWN, "value"), | ||||
|             Input(ids.WEEK_DROPDOWN, "value"), | ||||
|             Input(ids.CATEGORY_DROPDOWN, "value"), | ||||
|         ], | ||||
|     ) | ||||
|     def update_bar_chart( | ||||
|         years: list[str], months: list[str], categories: list[str] | ||||
|         years: list[str], weeks: list[str], categories: list[str] | ||||
|     ) -> html.Div: | ||||
|         filtered_data = data.query( | ||||
|             "year in @years and month in @months and category in @categories" | ||||
|             "year in @years and week in @weeks and category in @categories" | ||||
|         ) | ||||
|  | ||||
|         if filtered_data.shape[0] == 0: | ||||
|  | ||||
| @ -14,12 +14,12 @@ def render(app: Dash, data: pd.DataFrame) -> html.Div: | ||||
|         Output(ids.CATEGORY_DROPDOWN, "value"), | ||||
|         [ | ||||
|             Input(ids.YEAR_DROPDOWN, "value"), | ||||
|             Input(ids.MONTH_DROPDOWN, "value"), | ||||
|             Input(ids.WEEK_DROPDOWN, "value"), | ||||
|             Input(ids.SELECT_ALL_CATEGORIES_BUTTON, "n_clicks"), | ||||
|         ], | ||||
|     ) | ||||
|     def select_all_categories(years: list[str], months: list[str], _: int) -> list[str]: | ||||
|         filtered_data = data.query("year in @years and month in @months") | ||||
|     def select_all_categories(years: list[str], weeks: list[str], _: int) -> list[str]: | ||||
|         filtered_data = data.query("year in @years and week in @weeks") | ||||
|         return sorted(set(filtered_data[DataSchema.CATEGORY].tolist())) | ||||
|  | ||||
|     return html.Div( | ||||
|  | ||||
| @ -1,48 +1,123 @@ | ||||
| import pandas as pd | ||||
| import plotly.express as px | ||||
| from dash import Dash, dcc, html | ||||
| from dash.dependencies import Input, Output | ||||
| from dash import Dash, dcc, html, dash_table, Input, Output, State, callback_context | ||||
| from datetime import datetime | ||||
| import os | ||||
| import dash_bootstrap_components as dbc | ||||
|  | ||||
| from ..data.loader import DataSchema | ||||
| from . import ids | ||||
|  | ||||
|  | ||||
| def render(app: Dash, data: pd.DataFrame) -> html.Div: | ||||
|     @app.callback( | ||||
|         Output(ids.BAR_CHART, "children"), | ||||
|         Output(ids.DATA_TABLE, "children"), | ||||
|         [ | ||||
|             Input(ids.YEAR_DROPDOWN, "value"), | ||||
|             Input(ids.MONTH_DROPDOWN, "value"), | ||||
|             Input(ids.WEEK_DROPDOWN, "value"), | ||||
|             Input(ids.CATEGORY_DROPDOWN, "value"), | ||||
|         ], | ||||
|     ) | ||||
|     def update_bar_chart( | ||||
|         years: list[str], months: list[str], categories: list[str] | ||||
|     def update_data_table( | ||||
|         years: list[str], weeks: list[str], categories: list[str] | ||||
|     ) -> html.Div: | ||||
|         filtered_data = data.query( | ||||
|             "year in @years and month in @months and category in @categories" | ||||
|             "year in @years and week in @weeks and category in @categories" | ||||
|         ) | ||||
|  | ||||
|         if filtered_data.shape[0] == 0: | ||||
|             return html.Div("No data selected.", id=ids.BAR_CHART) | ||||
|             return html.Div("No data selected.") | ||||
|  | ||||
|         def create_pivot_table() -> pd.DataFrame: | ||||
|         pt = filtered_data.pivot_table( | ||||
|             values=DataSchema.AMOUNT, | ||||
|             index=[DataSchema.CATEGORY], | ||||
|             aggfunc="sum", | ||||
|             fill_value=0, | ||||
|             dropna=False, | ||||
|             ) | ||||
|             return pt.reset_index().sort_values(DataSchema.AMOUNT, ascending=False) | ||||
|         ).reset_index().sort_values(DataSchema.AMOUNT, ascending=False) | ||||
|  | ||||
|         fig = px.bar( | ||||
|             create_pivot_table(), | ||||
|             x=DataSchema.CATEGORY, | ||||
|             y=DataSchema.AMOUNT, | ||||
|             color=DataSchema.CATEGORY, | ||||
|         columns = [{"name": i.capitalize(), "id": i} for i in pt.columns] | ||||
|  | ||||
|         return dash_table.DataTable( | ||||
|             id=ids.CATEGORY_TABLE, | ||||
|             data=pt.to_dict("records"), | ||||
|             columns=columns, | ||||
|             row_selectable='single', | ||||
|             selected_rows=[] | ||||
|         ) | ||||
|  | ||||
|         return html.Div(dcc.Graph(figure=fig), id=ids.BAR_CHART) | ||||
|     @app.callback( | ||||
|         Output(ids.FEEDBACK_MODAL, "is_open"), | ||||
|         Output(ids.FEEDBACK_MESSAGE, "children"), | ||||
|         Input(ids.CATEGORY_TABLE, "selected_rows"), | ||||
|         Input(ids.SAVE_FEEDBACK_BUTTON_POPUP, "n_clicks"), | ||||
|         Input(ids.CLOSE_FEEDBACK_BUTTON_POPUP, "n_clicks"), | ||||
|         State(ids.FEEDBACK_MODAL, "is_open"), | ||||
|         State(ids.CATEGORY_TABLE, "data"), | ||||
|         State(ids.FEEDBACK_INPUT, "value"), | ||||
|     ) | ||||
|     def handle_feedback_modal(selected_rows, save_clicks, close_clicks, is_open, data, comment): | ||||
|         ctx = callback_context | ||||
|         if not ctx.triggered: | ||||
|             return False, "" | ||||
|  | ||||
|     return html.Div(id=ids.BAR_CHART) | ||||
|         triggered_id = ctx.triggered[0]['prop_id'].split('.')[0] | ||||
|          | ||||
|         if triggered_id == ids.CATEGORY_TABLE and selected_rows: | ||||
|             return True, "" | ||||
|  | ||||
|         if triggered_id == ids.SAVE_FEEDBACK_BUTTON_POPUP and selected_rows: | ||||
|             selected_row_data = data[selected_rows[0]] | ||||
|             category = selected_row_data[DataSchema.CATEGORY] | ||||
|             timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') | ||||
|             if not comment: | ||||
|                 comment = "" | ||||
|             feedback_data = f'{timestamp},{category},"{comment}"\n' | ||||
|             file_path = "data/feedback.csv" | ||||
|             try: | ||||
|                 is_new_file = not os.path.exists(file_path) or os.path.getsize(file_path) == 0 | ||||
|                 with open(file_path, "a") as f: | ||||
|                     if is_new_file: | ||||
|                         f.write("timestamp,category,comment\n") | ||||
|                     f.write(feedback_data) | ||||
|                 message = f"Feedback for category '{category}' has been saved successfully at {timestamp}." | ||||
|                 return False, message | ||||
|             except Exception as e: | ||||
|                 return True, "An error occurred while saving feedback." | ||||
|  | ||||
|         if triggered_id == ids.CLOSE_FEEDBACK_BUTTON_POPUP: | ||||
|             return False, "" | ||||
|  | ||||
|         return is_open, "" | ||||
|  | ||||
|     @app.callback( | ||||
|         Output("feedback-category-label", "children"), | ||||
|         Input(ids.CATEGORY_TABLE, "selected_rows"), | ||||
|         State(ids.CATEGORY_TABLE, "data"), | ||||
|         prevent_initial_call=True | ||||
|     ) | ||||
|     def update_feedback_category_label(selected_rows, data): | ||||
|         if selected_rows: | ||||
|             category = data[selected_rows[0]][DataSchema.CATEGORY] | ||||
|             return f"Category: {category}" | ||||
|         return "Category: " | ||||
|  | ||||
|     modal = dbc.Modal( | ||||
|         [ | ||||
|             dbc.ModalHeader(dbc.ModalTitle("Submit Feedback")), | ||||
|             dbc.ModalBody([ | ||||
|                 html.H6("Category: ", id="feedback-category-label"), | ||||
|                 dcc.Input(id=ids.FEEDBACK_INPUT, type='text', placeholder='Enter feedback...', style={'width': '100%'}), | ||||
|             ]), | ||||
|             dbc.ModalFooter([ | ||||
|                 dbc.Button("Save", id=ids.SAVE_FEEDBACK_BUTTON_POPUP, className="ms-auto", n_clicks=0), | ||||
|                 dbc.Button("Close", id=ids.CLOSE_FEEDBACK_BUTTON_POPUP, className="ms-auto", n_clicks=0) | ||||
|             ]), | ||||
|         ], | ||||
|         id=ids.FEEDBACK_MODAL, | ||||
|         is_open=False, | ||||
|     ) | ||||
|  | ||||
|     return html.Div([ | ||||
|         html.Div(id=ids.DATA_TABLE), | ||||
|         html.Div(id=ids.FEEDBACK_MESSAGE), | ||||
|         modal | ||||
|     ]) | ||||
							
								
								
									
										22
									
								
								src/components/explanation_tab.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/components/explanation_tab.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| from dash import Dash, dcc, html | ||||
|  | ||||
| def render(_: Dash) -> html.Div: | ||||
|     return html.Div([ | ||||
|         dcc.Markdown(""" | ||||
|             ### How to use this Dashboard | ||||
|  | ||||
|             **Dashboard Tab:** | ||||
|             - Use the dropdowns at the top to filter the data by year, week, and category. | ||||
|             - The bar chart shows the total amount per category for the selected period. | ||||
|             - The data table shows the detailed data. | ||||
|  | ||||
|             **Feedback:** | ||||
|             - In the data table on the Dashboard tab, you can select a row to provide feedback. | ||||
|             - A popup will appear where you can enter your comments for the selected category. | ||||
|             - Click "Save" to store your feedback. | ||||
|  | ||||
|             **Feedback Tab:** | ||||
|             - This tab displays all the feedback that has been submitted. | ||||
|             - The table on this tab updates automatically every 5 seconds. | ||||
|         """) | ||||
|     ]) | ||||
							
								
								
									
										27
									
								
								src/components/feedback_tab.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/components/feedback_tab.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| from dash import Dash, dcc, html, dash_table | ||||
| from dash.dependencies import Input, Output | ||||
| import pandas as pd | ||||
|  | ||||
| def render(app: Dash) -> html.Div: | ||||
|     @app.callback( | ||||
|         Output("feedback-table-content", "children"), | ||||
|         Input("feedback-interval", "n_intervals") | ||||
|     ) | ||||
|     def update_feedback_table(_): | ||||
|         try: | ||||
|             feedback_df = pd.read_csv("data/feedback.csv") | ||||
|             return dash_table.DataTable( | ||||
|                 data=feedback_df.to_dict("records"), | ||||
|                 columns=[{"name": i, "id": i} for i in feedback_df.columns], | ||||
|                 page_size=10, | ||||
|             ) | ||||
|         except FileNotFoundError: | ||||
|             return html.P("No feedback submitted yet.") | ||||
|         except Exception as e: | ||||
|             return html.P(f"An error occurred: {e}") | ||||
|  | ||||
|     return html.Div([ | ||||
|         html.H4("Submitted Feedback"), | ||||
|         html.Div(id="feedback-table-content"), | ||||
|         dcc.Interval(id="feedback-interval", interval=5 * 1000, n_intervals=0) # 5 seconds | ||||
|     ]) | ||||
| @ -5,8 +5,16 @@ DATA_TABLE = "data-table" | ||||
| SELECT_ALL_CATEGORIES_BUTTON = "select-all-categories-button" | ||||
| CATEGORY_DROPDOWN = "category-dropdown" | ||||
|  | ||||
| SELECT_ALL_MONTHS_BUTTON = "select-all-months-button" | ||||
| MONTH_DROPDOWN = "month-dropdown" | ||||
|  | ||||
| YEAR_DROPDOWN = "year-dropdown" | ||||
| SELECT_ALL_YEARS_BUTTON = "select-all-years-button" | ||||
|  | ||||
| WEEK_DROPDOWN = "week-dropdown" | ||||
| SELECT_ALL_WEEKS_BUTTON = "select-all-weeks-button" | ||||
|  | ||||
| CATEGORY_TABLE = "category-table" | ||||
|  | ||||
| FEEDBACK_INPUT = "feedback-input" | ||||
| FEEDBACK_MESSAGE = "feedback-message" | ||||
| FEEDBACK_MODAL = "feedback-modal" | ||||
| SAVE_FEEDBACK_BUTTON_POPUP = "save-feedback-button-popup" | ||||
| CLOSE_FEEDBACK_BUTTON_POPUP = "close-feedback-button-popup" | ||||
| @ -1,29 +1,48 @@ | ||||
| import pandas as pd | ||||
| from dash import Dash, html | ||||
| from dash import Dash, dcc, html | ||||
| from src.components import ( | ||||
|     bar_chart, | ||||
|     category_dropdown, | ||||
|     month_dropdown, | ||||
|     pie_chart, | ||||
|     data_table, | ||||
|     year_dropdown, | ||||
|     week_dropdown, | ||||
|     category_dropdown, | ||||
|     feedback_tab, | ||||
|     explanation_tab, | ||||
| ) | ||||
|  | ||||
|  | ||||
| def create_layout(app: Dash, data: pd.DataFrame) -> html.Div: | ||||
|     tab_content_style = {'height': 'calc(100vh - 220px)', 'overflowY': 'auto', 'padding': '15px'} | ||||
|  | ||||
|     return html.Div( | ||||
|         className="app-div", | ||||
|         children=[ | ||||
|             html.H1(app.title), | ||||
|             html.Hr(), | ||||
|             dcc.Tabs(id="tabs", value='tab-dashboard', children=[ | ||||
|                 dcc.Tab(label='Dashboard', value='tab-dashboard', children=[ | ||||
|                     html.Div(style=tab_content_style, children=[ | ||||
|                         html.Div( | ||||
|                             className="dropdown-container", | ||||
|                             children=[ | ||||
|                                 year_dropdown.render(app, data), | ||||
|                     month_dropdown.render(app, data), | ||||
|                                 week_dropdown.render(app, data), | ||||
|                                 category_dropdown.render(app, data), | ||||
|                             ], | ||||
|                         ), | ||||
|                         bar_chart.render(app, data), | ||||
|             pie_chart.render(app, data), | ||||
|                         data_table.render(app, data), | ||||
|                     ]) | ||||
|                 ]), | ||||
|                 dcc.Tab(label='Feedback', value='tab-feedback', children=[ | ||||
|                     html.Div(style=tab_content_style, children=[ | ||||
|                         feedback_tab.render(app) | ||||
|                     ]) | ||||
|                 ]), | ||||
|                 dcc.Tab(label='Explanation', value='tab-explanation', children=[ | ||||
|                     html.Div(style=tab_content_style, children=[ | ||||
|                         explanation_tab.render(app) | ||||
|                     ]) | ||||
|                 ]), | ||||
|             ]), | ||||
|         ], | ||||
|     ) | ||||
| @ -1,40 +0,0 @@ | ||||
| import pandas as pd | ||||
| from dash import Dash, dcc, html | ||||
| from dash.dependencies import Input, Output | ||||
|  | ||||
| from ..data.loader import DataSchema | ||||
| from . import ids | ||||
|  | ||||
|  | ||||
| def render(app: Dash, data: pd.DataFrame) -> html.Div: | ||||
|     all_months: list[str] = data[DataSchema.MONTH].tolist() | ||||
|     unique_months = sorted(set(all_months)) | ||||
|  | ||||
|     @app.callback( | ||||
|         Output(ids.MONTH_DROPDOWN, "value"), | ||||
|         [ | ||||
|             Input(ids.YEAR_DROPDOWN, "value"), | ||||
|             Input(ids.SELECT_ALL_MONTHS_BUTTON, "n_clicks"), | ||||
|         ], | ||||
|     ) | ||||
|     def select_all_months(years: list[str], _: int) -> list[str]: | ||||
|         filtered_data = data.query("year in @years") | ||||
|         return sorted(set(filtered_data[DataSchema.MONTH].tolist())) | ||||
|  | ||||
|     return html.Div( | ||||
|         children=[ | ||||
|             html.H6("Month"), | ||||
|             dcc.Dropdown( | ||||
|                 id=ids.MONTH_DROPDOWN, | ||||
|                 options=[{"label": month, "value": month} for month in unique_months], | ||||
|                 value=unique_months, | ||||
|                 multi=True, | ||||
|             ), | ||||
|             html.Button( | ||||
|                 className="dropdown-button", | ||||
|                 children=["Select All"], | ||||
|                 id=ids.SELECT_ALL_MONTHS_BUTTON, | ||||
|                 n_clicks=0, | ||||
|             ), | ||||
|         ] | ||||
|     ) | ||||
							
								
								
									
										53
									
								
								src/components/week_dropdown.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/components/week_dropdown.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| import pandas as pd | ||||
| from dash import Dash, dcc, html | ||||
| from dash.dependencies import Input, Output | ||||
| from datetime import datetime, timedelta | ||||
|  | ||||
| from ..data.loader import DataSchema | ||||
| from . import ids | ||||
|  | ||||
| def render(app: Dash, data: pd.DataFrame) -> html.Div: | ||||
|     all_weeks: list[str] = data[DataSchema.WEEK].tolist() | ||||
|     unique_weeks = sorted(set(all_weeks), reverse=True) | ||||
|  | ||||
|     # determine default weeks (last 13) | ||||
|     today = datetime.now() | ||||
|     last_13_weeks = [] | ||||
|     for i in range(13): | ||||
|         date = today - timedelta(weeks=i) | ||||
|         year, week, _ = date.isocalendar() | ||||
|         last_13_weeks.append(f"{year}{week:02d}") | ||||
|  | ||||
|     default_weeks = [week for week in last_13_weeks if week in unique_weeks] | ||||
|     if not default_weeks and unique_weeks: | ||||
|         default_weeks = unique_weeks[:13] | ||||
|  | ||||
|  | ||||
|     @app.callback( | ||||
|         Output(ids.WEEK_DROPDOWN, "value"), | ||||
|         [ | ||||
|             Input(ids.YEAR_DROPDOWN, "value"), | ||||
|             Input(ids.SELECT_ALL_WEEKS_BUTTON, "n_clicks"), | ||||
|         ], | ||||
|     ) | ||||
|     def select_all_weeks(years: list[str], _: int) -> list[str]: | ||||
|         filtered_data = data.query("year in @years") | ||||
|         return sorted(set(filtered_data[DataSchema.WEEK].tolist())) | ||||
|  | ||||
|     return html.Div( | ||||
|         children=[ | ||||
|             html.H6("Week"), | ||||
|             dcc.Dropdown( | ||||
|                 id=ids.WEEK_DROPDOWN, | ||||
|                 options=[{"label": f"{week[:4]}-{week[4:]}", "value": week} for week in unique_weeks], | ||||
|                 value=default_weeks, | ||||
|                 multi=True, | ||||
|             ), | ||||
|             html.Button( | ||||
|                 className="dropdown-button", | ||||
|                 children=["Select All"], | ||||
|                 id=ids.SELECT_ALL_WEEKS_BUTTON, | ||||
|                 n_clicks=0, | ||||
|             ), | ||||
|         ] | ||||
|     ) | ||||
| @ -7,6 +7,7 @@ class DataSchema: | ||||
|     DATE = "date" | ||||
|     MONTH = "month" | ||||
|     YEAR = "year" | ||||
|     WEEK = "week" | ||||
|  | ||||
|  | ||||
| def load_transaction_data(path: str) -> pd.DataFrame: | ||||
| @ -22,4 +23,8 @@ def load_transaction_data(path: str) -> pd.DataFrame: | ||||
|     ) | ||||
|     data[DataSchema.YEAR] = data[DataSchema.DATE].dt.year.astype(str) | ||||
|     data[DataSchema.MONTH] = data[DataSchema.DATE].dt.month.astype(str) | ||||
|     data[DataSchema.WEEK] = ( | ||||
|         data[DataSchema.DATE].dt.isocalendar().year.astype(str) | ||||
|         + data[DataSchema.DATE].dt.isocalendar().week.astype(str).str.zfill(2) | ||||
|     ) | ||||
|     return data | ||||
| @ -6,6 +6,7 @@ class DataSchema: | ||||
|     DATE = "date" | ||||
|     MONTH = "month" | ||||
|     YEAR = "year" | ||||
|     WEEK = "week" | ||||
|  | ||||
| class SPC_Schema: | ||||
|     FC = "FC" | ||||
| @ -30,4 +31,8 @@ def load_spc_data(path: str) -> pd.DataFrame: | ||||
|     ) | ||||
|     data[DataSchema.YEAR] = data[DataSchema.DATE].dt.year.astype(str) | ||||
|     data[DataSchema.MONTH] = data[DataSchema.DATE].dt.month.astype(str) | ||||
|     data[DataSchema.WEEK] = ( | ||||
|         data[DataSchema.DATE].dt.isocalendar().year.astype(str) | ||||
|         + data[DataSchema.DATE].dt.isocalendar().week.astype(str).str.zfill(2) | ||||
|     ) | ||||
|     return data | ||||
							
								
								
									
										22
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										22
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							| @ -9,12 +9,14 @@ source = { virtual = "." } | ||||
| dependencies = [ | ||||
|     { name = "dash" }, | ||||
|     { name = "dash-bootstrap-components" }, | ||||
|     { name = "dotenv" }, | ||||
| ] | ||||
|  | ||||
| [package.metadata] | ||||
| requires-dist = [ | ||||
|     { name = "dash", specifier = ">=3.2.0" }, | ||||
|     { name = "dash-bootstrap-components", specifier = ">=2.0.4" }, | ||||
|     { name = "dotenv", specifier = ">=0.9.9" }, | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -141,6 +143,17 @@ wheels = [ | ||||
|     { url = "https://files.pythonhosted.org/packages/d6/38/1efeec8b4d741c09ccd169baf8a00c07a0176b58e418d4cd0c30dffedd22/dash_bootstrap_components-2.0.4-py3-none-any.whl", hash = "sha256:767cf0084586c1b2b614ccf50f79fe4525fdbbf8e3a161ed60016e584a14f5d1", size = 204044 }, | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "dotenv" | ||||
| version = "0.9.9" | ||||
| source = { registry = "https://pypi.org/simple" } | ||||
| dependencies = [ | ||||
|     { name = "python-dotenv" }, | ||||
| ] | ||||
| wheels = [ | ||||
|     { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892 }, | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "flask" | ||||
| version = "3.1.2" | ||||
| @ -288,6 +301,15 @@ wheels = [ | ||||
|     { url = "https://files.pythonhosted.org/packages/95/a9/12e2dc726ba1ba775a2c6922d5d5b4488ad60bdab0888c337c194c8e6de8/plotly-6.3.0-py3-none-any.whl", hash = "sha256:7ad806edce9d3cdd882eaebaf97c0c9e252043ed1ed3d382c3e3520ec07806d4", size = 9791257 }, | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "python-dotenv" | ||||
| version = "1.1.1" | ||||
| source = { registry = "https://pypi.org/simple" } | ||||
| sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 } | ||||
| wheels = [ | ||||
|     { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 }, | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "requests" | ||||
| version = "2.32.5" | ||||
|  | ||||
		Reference in New Issue
	
	Block a user