Kikulika commited on
Commit
36ec28d
·
verified ·
1 Parent(s): beaf8b0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +234 -65
app.py CHANGED
@@ -4,11 +4,185 @@ from datetime import datetime
4
  import os
5
  import sys
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  # Color mapping
8
  STATUS_COLORS = {
9
- "To Do": "#e6f3ff",
10
- "In Progress": "#fff7e6",
11
- "Done": "#e6ffe6"
12
  }
13
 
14
  # Determine the best place to store data based on environment
@@ -30,14 +204,12 @@ def get_data_path():
30
  with open(test_file, 'w') as f:
31
  f.write('test')
32
  os.remove(test_file)
33
- st.success(f"Using {location} for data storage")
34
  return location
35
  except (PermissionError, OSError):
36
  continue
37
 
38
  # If we get here, we couldn't find a writable location
39
  st.error("Could not find a writable location for data storage!")
40
- st.info("The application will run, but changes won't be saved")
41
  return None
42
 
43
  # Set up paths
@@ -97,10 +269,9 @@ def save_tasks():
97
 
98
  def load_assignees():
99
  """Load assignees from text file or use defaults"""
100
- default_assignees = ["User 1", "User 2", "User 3"]
101
 
102
  if not ASSIGNEE_FILE:
103
- st.info("Using default assignees because no assignee file could be loaded")
104
  return default_assignees
105
 
106
  try:
@@ -108,7 +279,7 @@ def load_assignees():
108
  # Create a sample file
109
  try:
110
  with open(ASSIGNEE_FILE, "w") as f:
111
- f.write("John Doe - Developer\nJane Smith - Designer\n")
112
  except:
113
  return default_assignees
114
 
@@ -123,38 +294,8 @@ def load_assignees():
123
 
124
  return assignees if assignees else default_assignees
125
  except Exception as e:
126
- st.info(f"Using default assignees due to error: {str(e)}")
127
  return default_assignees
128
 
129
- # Function to create styled task cards
130
- def task_card(title, description, assignee, status, due_date):
131
- color = STATUS_COLORS.get(status, "#ffffff")
132
- card = f"""
133
- <div style="
134
- background-color: {color};
135
- border-radius: 8px;
136
- padding: 15px;
137
- margin: 10px 0;
138
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
139
- ">
140
- <h3 style="margin-top: 0; color: #2b5876;">{title}</h3>
141
- <p><strong>Assignee:</strong> {assignee}</p>
142
- <p><strong>Due Date:</strong> {due_date}</p>
143
- <p><strong>Description:</strong> {description}</p>
144
- <div style="margin-top: 15px;"></div>
145
- </div>
146
- """
147
- return card
148
-
149
- # Display information about the environment (helpful for debugging)
150
- st.sidebar.expander("Environment Info").write(f"""
151
- - Python version: {sys.version}
152
- - Working directory: {os.getcwd()}
153
- - Data directory: {DATA_DIR}
154
- - Tasks file: {TASKS_FILE}
155
- - Assignee file: {ASSIGNEE_FILE}
156
- """)
157
-
158
  # Initialize session state ONLY ONCE
159
  if 'tasks' not in st.session_state:
160
  st.session_state.tasks = load_tasks()
@@ -162,8 +303,13 @@ if 'tasks' not in st.session_state:
162
  # Load assignees once and reuse
163
  assignee_list = load_assignees()
164
 
165
- # App UI starts here
166
- st.title("Company Task Board 🗒️")
 
 
 
 
 
167
 
168
  # Sidebar for new tasks
169
  with st.sidebar:
@@ -193,35 +339,58 @@ with st.sidebar:
193
  else:
194
  st.warning("Please enter a task title")
195
 
196
- st.divider()
197
-
198
- # Display tasks in cards
199
  if st.session_state.tasks.empty:
200
  st.info("No tasks yet. Add your first task using the sidebar.")
201
  else:
 
 
 
 
202
  for idx, task in st.session_state.tasks.iterrows():
203
- card = task_card(
204
- task['Title'],
205
- task['Description'],
206
- task['Assignee'],
207
- task['Status'],
208
- task['Due Date'].strftime('%Y-%m-%d') if isinstance(task['Due Date'], datetime) else task['Due Date']
209
- )
210
-
211
- # Display the card
212
- st.markdown(card, unsafe_allow_html=True)
213
 
214
- # Status update dropdown below the card
215
- new_status = st.selectbox(
216
- "Update Status",
217
- ["To Do", "In Progress", "Done"],
218
- index=["To Do", "In Progress", "Done"].index(task['Status']),
219
- key=f"status_{idx}"
220
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
- if new_status != task['Status']:
223
- st.session_state.tasks.at[idx, 'Status'] = new_status
224
- save_tasks() # Save after updating
225
- st.rerun()
226
 
227
- st.write("") # Add some spacing between cards
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import os
5
  import sys
6
 
7
+ # Set page configuration
8
+ st.set_page_config(
9
+ page_title="Broadcast Solutions Task Board",
10
+ page_icon="📺",
11
+ layout="wide"
12
+ )
13
+
14
+ # Apply custom CSS for a more professional broadcast look
15
+ st.markdown("""
16
+ <style>
17
+ /* Main theme colors for broadcast industry feel */
18
+ :root {
19
+ --primary-color: #0A2647;
20
+ --secondary-color: #144272;
21
+ --accent-color: #205295;
22
+ --highlight-color: #2C74B3;
23
+ --text-color: #0A2647;
24
+ --card-bg-todo: #f0f7ff;
25
+ --card-bg-progress: #fff7e6;
26
+ --card-bg-done: #efffef;
27
+ }
28
+
29
+ /* Header styling */
30
+ .main-header {
31
+ background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
32
+ color: white;
33
+ padding: 1rem 2rem;
34
+ border-radius: 5px;
35
+ margin-bottom: 2rem;
36
+ text-align: center;
37
+ }
38
+
39
+ .main-header h1 {
40
+ margin: 0;
41
+ font-size: 2.5rem;
42
+ }
43
+
44
+ .company-name {
45
+ font-weight: 300;
46
+ font-size: 1.2rem;
47
+ opacity: 0.9;
48
+ letter-spacing: 1px;
49
+ }
50
+
51
+ /* Task Cards */
52
+ .task-grid {
53
+ display: flex;
54
+ flex-wrap: wrap;
55
+ gap: 15px;
56
+ padding-bottom: 20px;
57
+ }
58
+
59
+ .task-card {
60
+ flex: 0 0 300px;
61
+ background-color: white;
62
+ border-radius: 12px;
63
+ padding: 15px;
64
+ box-shadow: 0 4px 8px rgba(0,0,0,0.08);
65
+ transition: transform 0.2s, box-shadow 0.2s;
66
+ position: relative;
67
+ overflow: hidden;
68
+ }
69
+
70
+ .task-card:hover {
71
+ transform: translateY(-5px);
72
+ box-shadow: 0 6px 12px rgba(0,0,0,0.12);
73
+ }
74
+
75
+ .task-card.todo {
76
+ border-top: 5px solid #3498db;
77
+ background-color: var(--card-bg-todo);
78
+ }
79
+
80
+ .task-card.in-progress {
81
+ border-top: 5px solid #f39c12;
82
+ background-color: var(--card-bg-progress);
83
+ }
84
+
85
+ .task-card.done {
86
+ border-top: 5px solid #2ecc71;
87
+ background-color: var(--card-bg-done);
88
+ }
89
+
90
+ .task-title {
91
+ font-size: 1.2rem;
92
+ font-weight: 600;
93
+ color: var(--text-color);
94
+ margin-bottom: 10px;
95
+ }
96
+
97
+ .task-meta {
98
+ display: flex;
99
+ justify-content: space-between;
100
+ color: #555;
101
+ font-size: 0.9rem;
102
+ margin-bottom: 12px;
103
+ }
104
+
105
+ .task-desc {
106
+ color: #444;
107
+ font-size: 0.95rem;
108
+ margin-bottom: 15px;
109
+ }
110
+
111
+ .task-footer {
112
+ display: flex;
113
+ justify-content: space-between;
114
+ align-items: center;
115
+ margin-top: 10px;
116
+ }
117
+
118
+ .status-pill {
119
+ padding: 4px 10px;
120
+ border-radius: 15px;
121
+ font-size: 0.8rem;
122
+ font-weight: 500;
123
+ }
124
+
125
+ .status-pill.todo {
126
+ background-color: #e1f0ff;
127
+ color: #3498db;
128
+ }
129
+
130
+ .status-pill.in-progress {
131
+ background-color: #fff4e0;
132
+ color: #f39c12;
133
+ }
134
+
135
+ .status-pill.done {
136
+ background-color: #e0ffe0;
137
+ color: #2ecc71;
138
+ }
139
+
140
+ /* Form styling */
141
+ .sidebar .block-container {
142
+ padding-top: 2rem;
143
+ }
144
+
145
+ /* Remove default Streamlit formatting */
146
+ #MainMenu {visibility: hidden;}
147
+ footer {visibility: hidden;}
148
+ .block-container {padding-top: 0rem; padding-bottom: 0rem;}
149
+
150
+ /* Button styling */
151
+ .stButton > button {
152
+ width: 100%;
153
+ background-color: var(--accent-color);
154
+ color: white;
155
+ border: none;
156
+ padding: 10px 15px;
157
+ border-radius: 5px;
158
+ font-weight: 500;
159
+ }
160
+
161
+ .stButton > button:hover {
162
+ background-color: var(--highlight-color);
163
+ }
164
+
165
+ /* Hide storage path message */
166
+ .storage-message {
167
+ display: none;
168
+ }
169
+
170
+ /* Status selector */
171
+ .status-select {
172
+ width: 100%;
173
+ padding: 5px;
174
+ border-radius: 5px;
175
+ border: 1px solid #ddd;
176
+ margin-top: 5px;
177
+ }
178
+ </style>
179
+ """, unsafe_allow_html=True)
180
+
181
  # Color mapping
182
  STATUS_COLORS = {
183
+ "To Do": "todo",
184
+ "In Progress": "in-progress",
185
+ "Done": "done"
186
  }
187
 
188
  # Determine the best place to store data based on environment
 
204
  with open(test_file, 'w') as f:
205
  f.write('test')
206
  os.remove(test_file)
 
207
  return location
208
  except (PermissionError, OSError):
209
  continue
210
 
211
  # If we get here, we couldn't find a writable location
212
  st.error("Could not find a writable location for data storage!")
 
213
  return None
214
 
215
  # Set up paths
 
269
 
270
  def load_assignees():
271
  """Load assignees from text file or use defaults"""
272
+ default_assignees = ["John Doe", "Jane Smith", "Alex Johnson", "Maria Garcia"]
273
 
274
  if not ASSIGNEE_FILE:
 
275
  return default_assignees
276
 
277
  try:
 
279
  # Create a sample file
280
  try:
281
  with open(ASSIGNEE_FILE, "w") as f:
282
+ f.write("John Doe - Producer\nJane Smith - Director\nAlex Johnson - Camera Operator\nMaria Garcia - Editor\n")
283
  except:
284
  return default_assignees
285
 
 
294
 
295
  return assignees if assignees else default_assignees
296
  except Exception as e:
 
297
  return default_assignees
298
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  # Initialize session state ONLY ONCE
300
  if 'tasks' not in st.session_state:
301
  st.session_state.tasks = load_tasks()
 
303
  # Load assignees once and reuse
304
  assignee_list = load_assignees()
305
 
306
+ # Custom header
307
+ st.markdown("""
308
+ <div class="main-header">
309
+ <span class="company-name">BROADCAST SOLUTIONS</span>
310
+ <h1>Task Board</h1>
311
+ </div>
312
+ """, unsafe_allow_html=True)
313
 
314
  # Sidebar for new tasks
315
  with st.sidebar:
 
339
  else:
340
  st.warning("Please enter a task title")
341
 
342
+ # Display tasks in a horizontal grid
 
 
343
  if st.session_state.tasks.empty:
344
  st.info("No tasks yet. Add your first task using the sidebar.")
345
  else:
346
+ # Start the grid container
347
+ st.markdown('<div class="task-grid">', unsafe_allow_html=True)
348
+
349
+ # Render each task as a card in the grid
350
  for idx, task in st.session_state.tasks.iterrows():
351
+ status_class = STATUS_COLORS.get(task['Status'], "todo")
 
 
 
 
 
 
 
 
 
352
 
353
+ # Format the date
354
+ due_date = task['Due Date']
355
+ if isinstance(due_date, datetime):
356
+ formatted_date = due_date.strftime('%Y-%m-%d')
357
+ else:
358
+ formatted_date = due_date
359
+
360
+ # Create the card HTML
361
+ card_html = f"""
362
+ <div class="task-card {status_class}">
363
+ <div class="task-title">{task['Title']}</div>
364
+ <div class="task-meta">
365
+ <span><strong>Assignee:</strong> {task['Assignee']}</span>
366
+ <span><strong>Due:</strong> {formatted_date}</span>
367
+ </div>
368
+ <div class="task-desc">{task['Description']}</div>
369
+ <div class="task-footer">
370
+ <span class="status-pill {status_class}">{task['Status']}</span>
371
+ </div>
372
+ </div>
373
+ """
374
 
375
+ # Render the card
376
+ st.markdown(card_html, unsafe_allow_html=True)
 
 
377
 
378
+ # Hidden status update below each card
379
+ # We'll use columns to make this less prominent
380
+ cols = st.columns([3, 1])
381
+ with cols[1]:
382
+ new_status = st.selectbox(
383
+ "", # Empty label
384
+ ["To Do", "In Progress", "Done"],
385
+ index=["To Do", "In Progress", "Done"].index(task['Status']),
386
+ key=f"status_{idx}",
387
+ label_visibility="collapsed"
388
+ )
389
+
390
+ if new_status != task['Status']:
391
+ st.session_state.tasks.at[idx, 'Status'] = new_status
392
+ save_tasks() # Save after updating
393
+ st.rerun()
394
+
395
+ # Close the grid container
396
+ st.markdown('</div>', unsafe_allow_html=True)