kreemyyyy commited on
Commit
aa57af7
·
verified ·
1 Parent(s): ae5b4d4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -40
app.py CHANGED
@@ -1,22 +1,26 @@
1
  import pandas as pd
 
2
  import gradio as gr
3
- import os
 
4
  from datetime import datetime
5
 
6
- def convert_and_save_local(file_path, direction):
7
  if file_path is None:
8
- return pd.DataFrame({'Message': ['Please upload a file']}), "No file uploaded"
9
 
10
  try:
11
- # [Same conversion logic as before - truncated for brevity]
12
  raw = pd.read_excel(file_path, header=None)
13
  header1 = raw.iloc[0, 1:].astype(object)
14
  header2 = raw.iloc[1, 1:].astype(object)
15
 
 
16
  if header2.notna().all() and not header2.str.startswith('Unnamed').any():
17
  days = header2.tolist()
18
  data_start = 2
19
  else:
 
20
  days = []
21
  last = None
22
  for val in header1:
@@ -27,79 +31,163 @@ def convert_and_save_local(file_path, direction):
27
  days.append(last)
28
  data_start = 1
29
 
30
- df = pd.read_excel(file_path, header=data_start, index_col=0, usecols=[0] + list(range(1, len(days) + 1)))
 
 
 
 
 
 
31
  df.columns = [str(day) for day in days]
 
 
32
  day_cols = list(df.columns)
33
 
 
34
  assignments = {}
35
  if direction == 'A to B':
 
36
  for model in df.index.astype(str):
37
  for day in day_cols:
38
  cell = df.at[model, day]
39
- if pd.isna(cell): continue
 
40
  for texter in str(cell).split(','):
41
  texter = texter.strip()
42
- if not texter or texter.lower() in ['nan', 'none', '']: continue
 
43
  assignments.setdefault(texter, {d: [] for d in day_cols})
44
  assignments[texter][day].append(model)
45
- first_col_name = 'Texter'
 
 
 
 
 
 
 
 
 
 
 
46
  else:
 
47
  for texter in df.index.astype(str):
48
  for day in day_cols:
49
  cell = df.at[texter, day]
50
- if pd.isna(cell): continue
 
51
  for model in str(cell).split(','):
52
  model = model.strip()
53
- if not model or model.lower() in ['nan', 'none', '']: continue
 
54
  assignments.setdefault(model, {d: [] for d in day_cols})
55
  assignments[model][day].append(texter)
56
- first_col_name = 'Model'
57
-
58
- if not assignments:
59
- result = pd.DataFrame(columns=day_cols)
60
- else:
61
- index = sorted(assignments.keys())
62
- result = pd.DataFrame(index=index, columns=day_cols)
63
- for person, days_map in assignments.items():
64
- for day in day_cols:
65
- items = days_map.get(day, [])
66
- result.at[person, day] = ', '.join(items) if items else 'OFF'
 
67
 
 
68
  result.index.name = None
69
  result.columns.name = None
 
 
70
  display_df = result.reset_index().rename(columns={'index': first_col_name})
71
-
72
- # Save to local directory
73
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
74
- local_filename = f"converted_schedule_{timestamp}.csv"
75
- local_path = os.path.join(os.getcwd(), local_filename)
76
-
77
  result_clean = result.copy().fillna('OFF')
 
 
 
 
 
 
78
  download_df = result_clean.reset_index().rename(columns={'index': first_col_name})
79
- download_df.to_csv(local_path, index=False)
80
 
81
- success_msg = f"✅ File saved successfully!\nLocation: {local_path}"
 
 
 
82
 
83
- return display_df, success_msg
 
 
 
 
 
84
 
85
  except Exception as e:
86
- error_df = pd.DataFrame({'Error': [f"Error: {str(e)}"]})
87
- return error_df, f"❌ Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
 
89
  iface = gr.Interface(
90
- fn=convert_and_save_local,
91
  inputs=[
92
- gr.File(label='Upload Weekly Schedule (.xlsx)', file_types=['.xlsx', '.xls']),
93
- gr.Radio(['A to B', 'B to A'], label='Convert Direction', value='A to B')
 
 
 
 
 
 
 
 
 
94
  ],
95
  outputs=[
96
- gr.Dataframe(label='Converted Schedule (Preview)'),
97
- gr.Textbox(label='Save Status', lines=3)
98
  ],
99
- title='🔄 Schedule Converter - Local Save',
100
- description='Converts and saves the file directly to your current directory',
 
 
 
 
 
 
101
  flagging_mode='never'
102
  )
103
 
104
  if __name__ == "__main__":
105
- iface.launch(server_name='0.0.0.0', server_port=7860)
 
 
 
 
 
1
  import pandas as pd
2
+ import openpyxl
3
  import gradio as gr
4
+ import io
5
+ import base64
6
  from datetime import datetime
7
 
8
+ def convert_schedule(file_path, direction):
9
  if file_path is None:
10
+ return pd.DataFrame({'Error': ['Please upload a file']}), None
11
 
12
  try:
13
+ # 1. Load raw header rows to determine day labels
14
  raw = pd.read_excel(file_path, header=None)
15
  header1 = raw.iloc[0, 1:].astype(object)
16
  header2 = raw.iloc[1, 1:].astype(object)
17
 
18
+ # Decide which header row to use: prefer second if fully populated
19
  if header2.notna().all() and not header2.str.startswith('Unnamed').any():
20
  days = header2.tolist()
21
  data_start = 2
22
  else:
23
+ # Forward-fill merged first-row headers
24
  days = []
25
  last = None
26
  for val in header1:
 
31
  days.append(last)
32
  data_start = 1
33
 
34
+ # 2. Load actual data using resolved day columns
35
+ df = pd.read_excel(
36
+ file_path,
37
+ header=data_start,
38
+ index_col=0,
39
+ usecols=[0] + list(range(1, len(days) + 1))
40
+ )
41
  df.columns = [str(day) for day in days]
42
+
43
+ # 3. Retain original day column order
44
  day_cols = list(df.columns)
45
 
46
+ # 4. Build assignment mapping via explicit iteration
47
  assignments = {}
48
  if direction == 'A to B':
49
+ # Models in rows → Texters as rows
50
  for model in df.index.astype(str):
51
  for day in day_cols:
52
  cell = df.at[model, day]
53
+ if pd.isna(cell):
54
+ continue
55
  for texter in str(cell).split(','):
56
  texter = texter.strip()
57
+ if not texter or texter.lower() in ['nan', 'none', '']:
58
+ continue
59
  assignments.setdefault(texter, {d: [] for d in day_cols})
60
  assignments[texter][day].append(model)
61
+
62
+ if not assignments:
63
+ result = pd.DataFrame(columns=day_cols)
64
+ first_col_name = 'Texter'
65
+ else:
66
+ index = sorted(assignments.keys())
67
+ result = pd.DataFrame(index=index, columns=day_cols)
68
+ first_col_name = 'Texter'
69
+ for texter, days_map in assignments.items():
70
+ for day in day_cols:
71
+ models = days_map.get(day, [])
72
+ result.at[texter, day] = ', '.join(models) if models else 'OFF'
73
  else:
74
+ # Texters in rows → Models as rows
75
  for texter in df.index.astype(str):
76
  for day in day_cols:
77
  cell = df.at[texter, day]
78
+ if pd.isna(cell):
79
+ continue
80
  for model in str(cell).split(','):
81
  model = model.strip()
82
+ if not model or model.lower() in ['nan', 'none', '']:
83
+ continue
84
  assignments.setdefault(model, {d: [] for d in day_cols})
85
  assignments[model][day].append(texter)
86
+
87
+ if not assignments:
88
+ result = pd.DataFrame(columns=day_cols)
89
+ first_col_name = 'Model'
90
+ else:
91
+ index = sorted(assignments.keys())
92
+ result = pd.DataFrame(index=index, columns=day_cols)
93
+ first_col_name = 'Model'
94
+ for model, days_map in assignments.items():
95
+ for day in day_cols:
96
+ texters = days_map.get(day, [])
97
+ result.at[model, day] = ', '.join(texters) if texters else 'OFF'
98
 
99
+ # 5. Cleanup axis names
100
  result.index.name = None
101
  result.columns.name = None
102
+
103
+ # For display, include index as a column
104
  display_df = result.reset_index().rename(columns={'index': first_col_name})
105
+
106
+ # 6. Create downloadable file using in-memory approach
 
 
 
 
107
  result_clean = result.copy().fillna('OFF')
108
+
109
+ # Ensure all values are strings
110
+ for col in result_clean.columns:
111
+ result_clean[col] = result_clean[col].astype(str)
112
+
113
+ # Create CSV file in memory (more reliable than Excel)
114
  download_df = result_clean.reset_index().rename(columns={'index': first_col_name})
 
115
 
116
+ # Create CSV content
117
+ csv_buffer = io.StringIO()
118
+ download_df.to_csv(csv_buffer, index=False)
119
+ csv_content = csv_buffer.getvalue()
120
 
121
+ # Create filename with timestamp
122
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
123
+ filename = f"converted_schedule_{timestamp}.csv"
124
+
125
+ # Return the CSV content for download
126
+ return display_df, (csv_content.encode('utf-8'), filename)
127
 
128
  except Exception as e:
129
+ error_msg = f"Error processing file: {str(e)}"
130
+ print(f"DEBUG: {error_msg}")
131
+ error_df = pd.DataFrame({'Error': [error_msg]})
132
+ return error_df, None
133
+
134
+ # Wrapper function to handle the file download properly
135
+ def process_and_download(file_path, direction):
136
+ display_result, download_data = convert_schedule(file_path, direction)
137
+
138
+ if download_data is None:
139
+ return display_result, None
140
+
141
+ # Create a temporary file that Gradio can serve
142
+ import tempfile
143
+ import os
144
+
145
+ excel_content, filename = download_data
146
+
147
+ # Save to a temporary file in the system temp directory
148
+ temp_dir = tempfile.gettempdir()
149
+ temp_path = os.path.join(temp_dir, filename)
150
+
151
+ # Write CSV content
152
+ with open(temp_path, 'w', encoding='utf-8') as f:
153
+ f.write(excel_content.decode('utf-8'))
154
+
155
+ return display_result, temp_path
156
 
157
+ # Create the interface
158
  iface = gr.Interface(
159
+ fn=process_and_download,
160
  inputs=[
161
+ gr.File(
162
+ label='Upload Weekly Schedule (.xlsx)',
163
+ file_count='single',
164
+ file_types=['.xlsx', '.xls']
165
+ ),
166
+ gr.Radio(
167
+ ['A to B', 'B to A'],
168
+ label='Convert Direction',
169
+ value='A to B',
170
+ info='A to B: Models→Texters, B to A: Texters→Models'
171
+ )
172
  ],
173
  outputs=[
174
+ gr.Dataframe(label='Converted Schedule (Preview)', wrap=True),
175
+ gr.File(label='Download Converted Schedule (.csv)')
176
  ],
177
+ title='🔄 7-Day Schedule Converter',
178
+ description=(
179
+ '**How to use:**\n'
180
+ '1. Upload your Excel file with a 7-day schedule\n'
181
+ '2. Choose conversion direction\n'
182
+ '3. Preview the result and download the converted file\n\n'
183
+ '*Supports merged headers and handles Models ↔ Texters conversion*'
184
+ ),
185
  flagging_mode='never'
186
  )
187
 
188
  if __name__ == "__main__":
189
+ iface.launch(
190
+ server_name='0.0.0.0',
191
+ server_port=7860,
192
+ share=False
193
+ )