File size: 4,407 Bytes
cdf2807
41a9969
cdf2807
bc36177
 
cdf2807
41a9969
aa3525b
e22be08
4814b0a
40ca158
4814b0a
 
a3de3da
4814b0a
40ca158
 
 
 
41a9969
40ca158
 
 
 
 
 
 
 
 
cdf2807
4814b0a
40ca158
 
 
 
41a9969
40ca158
 
 
4814b0a
aa3525b
cdf2807
41a9969
aa3525b
 
4814b0a
40ca158
aa3525b
40ca158
 
aa3525b
 
 
40ca158
 
aa3525b
 
e27b63a
aa3525b
 
 
 
40ca158
4814b0a
40ca158
aa3525b
40ca158
 
aa3525b
 
 
40ca158
 
aa3525b
 
e27b63a
aa3525b
 
 
 
af26c90
e27b63a
af26c90
 
bc36177
e27b63a
 
 
fa525d3
0d205a7
fa525d3
41a9969
d7769fa
fa525d3
b42ec56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa525d3
b42ec56
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import pandas as pd
import openpyxl  # ensure XLSX engine is available
import gradio as gr
import uuid
import os

# 7-Day Schedule Converter with explicit iteration and header fill and download support

def convert_schedule(file_path, direction):
    # 1. Load raw header rows to determine day labels
    raw = pd.read_excel(file_path, header=None)
    header1 = raw.iloc[0, 1:].astype(object)
    header2 = raw.iloc[1, 1:].astype(object)

    # Decide which header row to use: prefer second if fully populated
    if header2.notna().all() and not header2.str.startswith('Unnamed').any():
        days = header2.tolist()
        data_start = 2
    else:
        # Forward-fill merged first-row headers
        days = []
        last = None
        for val in header1:
            if pd.isna(val) or str(val).startswith('Unnamed'):
                days.append(last)
            else:
                last = str(val)
                days.append(last)
        data_start = 1

    # 2. Load actual data using resolved day columns
    df = pd.read_excel(
        file_path,
        header=data_start,
        index_col=0,
        usecols=[0] + list(range(1, len(days) + 1))
    )
    df.columns = [str(day) for day in days]

    # 3. Retain original day column order
    day_cols = list(df.columns)

    # 4. Build assignment mapping via explicit iteration
    assignments = {}
    if direction == 'A to B':
        # Models in rows → Texters as rows
        for model in df.index.astype(str):
            for day in day_cols:
                cell = df.at[model, day]
                for texter in str(cell).split(','):
                    texter = texter.strip()
                    if not texter or texter.lower() in ['nan', 'none']:
                        continue
                    assignments.setdefault(texter, {d: [] for d in day_cols})
                    assignments[texter][day].append(model)
        index = sorted(assignments.keys())
        result = pd.DataFrame(index=index, columns=day_cols)
        first_col_name = 'Texter'
        for texter, days_map in assignments.items():
            for day in day_cols:
                models = days_map.get(day, [])
                result.at[texter, day] = ', '.join(models) if models else 'OFF'
    else:
        # Texters in rows → Models as rows
        for texter in df.index.astype(str):
            for day in day_cols:
                cell = df.at[texter, day]
                for model in str(cell).split(','):
                    model = model.strip()
                    if not model or model.lower() in ['nan', 'none']:
                        continue
                    assignments.setdefault(model, {d: [] for d in day_cols})
                    assignments[model][day].append(texter)
        index = sorted(assignments.keys())
        result = pd.DataFrame(index=index, columns=day_cols)
        first_col_name = 'Model'
        for model, days_map in assignments.items():
            for day in day_cols:
                texters = days_map.get(day, [])
                result.at[model, day] = ', '.join(texters) if texters else 'OFF'

    # 5. Cleanup axis names
    result.index.name = None
    result.columns.name = None

    # For display, include index as a column
    display_df = result.reset_index().rename(columns={'index': first_col_name})

    # 6. Save to Excel for download (relative path so Gradio can serve it)
    output_filename = f"converted_{uuid.uuid4().hex}.xlsx"
    result.to_excel(output_filename, engine='openpyxl', index=True)

    # Return both DataFrame and download path
    return display_df, output_filename

# Build Gradio interface and launch immediately for Hugging Face Spaces
iface = gr.Interface(
    fn=convert_schedule,
    inputs=[
        gr.File(label='Upload Weekly Schedule (.xlsx)', file_count='single', type='filepath'),
        gr.Radio(['A to B', 'B to A'], label='Convert Direction')
    ],
    outputs=[
        gr.Dataframe(label='Converted Schedule'),
        gr.File(label='Download Converted Excel')
    ],
    title='7-Day Schedule Converter',
    description=(
        'Upload a 7-column weekly schedule (Models vs Days) with merged or single headers, '
        'then flip between Models→Texters or Texters→Models. Download the result as .xlsx.'
    ),
    flagging_mode='never'
)
# Launch on 0.0.0.0:7860 for Spaces
iface.launch(server_name='0.0.0.0', server_port=7860)