Update app.py
Browse files
app.py
CHANGED
|
@@ -451,9 +451,9 @@ def create_empty_df():
|
|
| 451 |
"""Create an empty DataFrame with the required columns"""
|
| 452 |
return pd.DataFrame(columns=[
|
| 453 |
'ID', 'Title', 'Description', 'Assignee', 'Status',
|
| 454 |
-
'Date Started', 'Date to Finish', 'Due Date' # Added
|
| 455 |
])
|
| 456 |
-
|
| 457 |
def load_tasks():
|
| 458 |
"""Load tasks from JSON file with better error handling"""
|
| 459 |
# First check if we have in-memory tasks from a failed save
|
|
@@ -873,19 +873,119 @@ if selected_task_id:
|
|
| 873 |
</div>
|
| 874 |
""", unsafe_allow_html=True)
|
| 875 |
|
| 876 |
-
# Description section
|
| 877 |
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
| 878 |
st.markdown('<div class="task-details-section">', unsafe_allow_html=True)
|
| 879 |
st.markdown('<h3>Description</h3>', unsafe_allow_html=True)
|
| 880 |
st.markdown('<div class="task-description">', unsafe_allow_html=True)
|
| 881 |
-
|
|
|
|
|
|
|
| 882 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 883 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 884 |
|
| 885 |
-
#
|
| 886 |
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
| 887 |
st.markdown('<div class="task-details-section">', unsafe_allow_html=True)
|
| 888 |
-
st.markdown('<h3>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 889 |
|
| 890 |
# Create columns for actions
|
| 891 |
col1, col2 = st.columns(2)
|
|
|
|
| 451 |
"""Create an empty DataFrame with the required columns"""
|
| 452 |
return pd.DataFrame(columns=[
|
| 453 |
'ID', 'Title', 'Description', 'Assignee', 'Status',
|
| 454 |
+
'Date Started', 'Date to Finish', 'Due Date', 'Documents' # Added Documents field
|
| 455 |
])
|
| 456 |
+
|
| 457 |
def load_tasks():
|
| 458 |
"""Load tasks from JSON file with better error handling"""
|
| 459 |
# First check if we have in-memory tasks from a failed save
|
|
|
|
| 873 |
</div>
|
| 874 |
""", unsafe_allow_html=True)
|
| 875 |
|
| 876 |
+
# Description section - Fix formatting
|
| 877 |
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
| 878 |
st.markdown('<div class="task-details-section">', unsafe_allow_html=True)
|
| 879 |
st.markdown('<h3>Description</h3>', unsafe_allow_html=True)
|
| 880 |
st.markdown('<div class="task-description">', unsafe_allow_html=True)
|
| 881 |
+
# Ensure proper encoding and sanitization of description text
|
| 882 |
+
description_text = task['Description'] if task['Description'] else 'No description provided.'
|
| 883 |
+
st.markdown(f"<p>{description_text}</p>", unsafe_allow_html=True)
|
| 884 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 885 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 886 |
|
| 887 |
+
# Task actions
|
| 888 |
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
| 889 |
st.markdown('<div class="task-details-section">', unsafe_allow_html=True)
|
| 890 |
+
st.markdown('<h3>Edit Task</h3>', unsafe_allow_html=True)
|
| 891 |
+
|
| 892 |
+
# Edit description
|
| 893 |
+
new_description = st.text_area("Edit Description",
|
| 894 |
+
value=task['Description'] if task['Description'] else '',
|
| 895 |
+
height=150,
|
| 896 |
+
key=f"edit_desc_{selected_task_id}")
|
| 897 |
+
|
| 898 |
+
# File upload section
|
| 899 |
+
uploaded_files = st.file_uploader("Upload Documents",
|
| 900 |
+
accept_multiple_files=True,
|
| 901 |
+
key=f"upload_{selected_task_id}")
|
| 902 |
+
|
| 903 |
+
# Save changes button for description
|
| 904 |
+
if st.button("Save Description", key=f"save_desc_{selected_task_id}"):
|
| 905 |
+
task_idx = task_df.index[0]
|
| 906 |
+
|
| 907 |
+
# Only update if description changed
|
| 908 |
+
if new_description != task['Description']:
|
| 909 |
+
update_task(task_idx, 'Description', new_description)
|
| 910 |
+
st.success("Description updated successfully!")
|
| 911 |
+
st.rerun()
|
| 912 |
+
|
| 913 |
+
# Handle file uploads
|
| 914 |
+
if uploaded_files:
|
| 915 |
+
# Create a container to show upload status
|
| 916 |
+
upload_status = st.empty()
|
| 917 |
+
|
| 918 |
+
# Process each uploaded file
|
| 919 |
+
for uploaded_file in uploaded_files:
|
| 920 |
+
# Display upload progress
|
| 921 |
+
upload_status.info(f"Processing {uploaded_file.name}...")
|
| 922 |
+
|
| 923 |
+
try:
|
| 924 |
+
# Read file content
|
| 925 |
+
file_content = uploaded_file.read()
|
| 926 |
+
|
| 927 |
+
# Create documents directory if it doesn't exist
|
| 928 |
+
doc_dir = os.path.join(os.path.dirname(TASKS_FILE), "task_documents")
|
| 929 |
+
os.makedirs(doc_dir, exist_ok=True)
|
| 930 |
+
|
| 931 |
+
# Create task-specific directory
|
| 932 |
+
task_doc_dir = os.path.join(doc_dir, f"task_{selected_task_id.replace('#', '')}")
|
| 933 |
+
os.makedirs(task_doc_dir, exist_ok=True)
|
| 934 |
+
|
| 935 |
+
# Save file
|
| 936 |
+
file_path = os.path.join(task_doc_dir, uploaded_file.name)
|
| 937 |
+
with open(file_path, "wb") as f:
|
| 938 |
+
f.write(file_content)
|
| 939 |
+
|
| 940 |
+
# Update task with document reference (optional)
|
| 941 |
+
if 'Documents' not in task or not isinstance(task['Documents'], list):
|
| 942 |
+
task_df.at[task_df.index[0], 'Documents'] = [uploaded_file.name]
|
| 943 |
+
else:
|
| 944 |
+
docs = task['Documents']
|
| 945 |
+
if uploaded_file.name not in docs:
|
| 946 |
+
docs.append(uploaded_file.name)
|
| 947 |
+
task_df.at[task_df.index[0], 'Documents'] = docs
|
| 948 |
+
|
| 949 |
+
# Save the updated task data
|
| 950 |
+
st.session_state.tasks.loc[task_df.index[0]] = task_df.iloc[0]
|
| 951 |
+
save_tasks()
|
| 952 |
+
|
| 953 |
+
upload_status.success(f"Uploaded {uploaded_file.name} successfully!")
|
| 954 |
+
except Exception as e:
|
| 955 |
+
upload_status.error(f"Error uploading {uploaded_file.name}: {str(e)}")
|
| 956 |
+
|
| 957 |
+
# Clear status after all files processed
|
| 958 |
+
st.success("All files processed. Refresh to see attached documents.")
|
| 959 |
+
|
| 960 |
+
# Display attached documents if any
|
| 961 |
+
if 'Documents' in task and isinstance(task['Documents'], list) and len(task['Documents']) > 0:
|
| 962 |
+
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
| 963 |
+
st.markdown('<div class="task-details-section">', unsafe_allow_html=True)
|
| 964 |
+
st.markdown('<h3>Attached Documents</h3>', unsafe_allow_html=True)
|
| 965 |
+
|
| 966 |
+
for doc in task['Documents']:
|
| 967 |
+
# Create the file path
|
| 968 |
+
doc_dir = os.path.join(os.path.dirname(TASKS_FILE), "task_documents")
|
| 969 |
+
task_doc_dir = os.path.join(doc_dir, f"task_{selected_task_id.replace('#', '')}")
|
| 970 |
+
file_path = os.path.join(task_doc_dir, doc)
|
| 971 |
+
|
| 972 |
+
# Display document with download option
|
| 973 |
+
if os.path.exists(file_path):
|
| 974 |
+
col1, col2 = st.columns([3, 1])
|
| 975 |
+
with col1:
|
| 976 |
+
st.markdown(f"📄 **{doc}**")
|
| 977 |
+
with col2:
|
| 978 |
+
with open(file_path, "rb") as file:
|
| 979 |
+
st.download_button(
|
| 980 |
+
label="Download",
|
| 981 |
+
data=file,
|
| 982 |
+
file_name=doc,
|
| 983 |
+
key=f"download_{doc}_{selected_task_id}"
|
| 984 |
+
)
|
| 985 |
+
|
| 986 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 987 |
+
|
| 988 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 989 |
|
| 990 |
# Create columns for actions
|
| 991 |
col1, col2 = st.columns(2)
|