Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
# app.py
|
2 |
import os, base64
|
3 |
import gradio as gr
|
4 |
import pandas as pd
|
@@ -10,7 +9,7 @@ LOGO_PATH = Path("rowsquared-logo-large.png")
|
|
10 |
with open(LOGO_PATH, "rb") as f:
|
11 |
logo_b64 = base64.b64encode(f.read()).decode()
|
12 |
|
13 |
-
# --- load your ISCO hierarchy and build nested dict
|
14 |
PROCESSED_DATA_DIR = Path(".")
|
15 |
df_isco = (
|
16 |
pd.read_excel(
|
@@ -25,34 +24,33 @@ for _, r in df_isco.iterrows():
|
|
25 |
.setdefault(r.sub_major_label, {})\
|
26 |
.setdefault(r.minor_label, [])\
|
27 |
.append(r.unit_label)
|
28 |
-
# dedupe & sort units
|
29 |
for maj in hierarchy:
|
30 |
for sub in hierarchy[maj]:
|
31 |
for mn in hierarchy[maj][sub]:
|
32 |
hierarchy[maj][sub][mn] = sorted(dict.fromkeys(hierarchy[maj][sub][mn]))
|
33 |
|
34 |
-
def majors():
|
35 |
-
def submajors(m):return sorted(hierarchy.get(m,{}).keys())
|
36 |
-
def minors(m,s):
|
37 |
-
def units(m,s,n):return hierarchy.get(m,{}).get(s,{}).get(n,[])
|
38 |
|
39 |
-
# --- load your records
|
40 |
records = pd.read_excel(PROCESSED_DATA_DIR/"isco_predictions.xlsx").copy()
|
41 |
for c in ["major_label","sub_major_label","minor_label","unit_label"]:
|
42 |
-
if c not in records: records[c]=""
|
43 |
-
if "annotated" not in records: records["annotated"]=False
|
44 |
records.reset_index(drop=True,inplace=True)
|
45 |
|
46 |
-
# --- clamp + save + load + handlers
|
47 |
def clamp_path(maj,sub,mn,un):
|
48 |
maj_c = majors()
|
49 |
-
if maj
|
50 |
sub_c = submajors(maj)
|
51 |
-
if sub
|
52 |
mn_c = minors(maj,sub)
|
53 |
-
if mn
|
54 |
un_c = units(maj,sub,mn)
|
55 |
-
if un
|
56 |
return maj,sub,mn,un, maj_c,sub_c,mn_c,un_c
|
57 |
|
58 |
def save_record(i,maj,sub,mn,un):
|
@@ -64,10 +62,8 @@ def status_text(i):
|
|
64 |
|
65 |
def load_record(i):
|
66 |
r = records.loc[i]
|
67 |
-
maj,sub,mn,un,maj_c,sub_c,mn_c,un_c = clamp_path(
|
68 |
-
|
69 |
-
)
|
70 |
-
save_record(i, maj,sub,mn,un)
|
71 |
md = f"## Occupation: {r.occupation_title_main}\n## Industry: {r.industry_title_main}"
|
72 |
return (
|
73 |
md,
|
@@ -79,15 +75,15 @@ def load_record(i):
|
|
79 |
)
|
80 |
|
81 |
def on_major_change(new_maj,i):
|
82 |
-
sub_c = submajors(new_maj);
|
83 |
-
mn_c = minors(new_maj,sub);
|
84 |
un_c = units(new_maj,sub,mn); un = un_c[0] if un_c else ""
|
85 |
save_record(i,new_maj,sub,mn,un)
|
86 |
return (
|
87 |
-
gr.update(choices=majors(),value=new_maj),
|
88 |
-
gr.update(choices=sub_c,value=sub),
|
89 |
-
gr.update(choices=mn_c,value=mn),
|
90 |
-
gr.update(choices=un_c,value=un),
|
91 |
status_text(i),
|
92 |
)
|
93 |
|
@@ -98,8 +94,8 @@ def on_sub_change(new_sub,i,maj):
|
|
98 |
records.loc[i,"annotated"]=True
|
99 |
return (
|
100 |
gr.update(choices=submajors(maj),value=new_sub),
|
101 |
-
gr.update(choices=mn_c,value=mn),
|
102 |
-
gr.update(choices=un_c,value=un),
|
103 |
status_text(i),
|
104 |
)
|
105 |
|
@@ -109,13 +105,13 @@ def on_minor_change(new_mn,i,maj,sub):
|
|
109 |
records.loc[i,"annotated"]=True
|
110 |
return (
|
111 |
gr.update(choices=minors(maj,sub),value=new_mn),
|
112 |
-
gr.update(choices=un_c,value=un),
|
113 |
status_text(i),
|
114 |
)
|
115 |
|
116 |
def on_unit_change(new_un,i,maj,sub,mn):
|
117 |
un_c=units(maj,sub,mn)
|
118 |
-
if new_un
|
119 |
records.loc[i,"unit_label"]=new_un
|
120 |
records.loc[i,"annotated"]=True
|
121 |
return gr.update(choices=un_c,value=new_un), status_text(i)
|
@@ -123,73 +119,76 @@ def on_unit_change(new_un,i,maj,sub,mn):
|
|
123 |
def go_next(i): return (i+1)%len(records)
|
124 |
def go_prev(i): return (i-1)%len(records)
|
125 |
|
126 |
-
def save_and_jump(i,
|
127 |
-
# clamp and save first
|
128 |
r=records.loc[i]
|
129 |
-
maj,sub,mn,un,*_ = clamp_path(r.major_label,
|
130 |
save_record(i,maj,sub,mn,un)
|
131 |
-
j = go_next(i) if
|
132 |
return (j,)+load_record(j)
|
133 |
|
134 |
def download_csv():
|
135 |
-
|
136 |
-
records.to_csv(
|
137 |
-
return str(
|
138 |
|
139 |
-
# ---
|
140 |
def build_gradio_app():
|
141 |
USER = os.getenv("APP_USER","")
|
142 |
PWD = os.getenv("APP_PASS","")
|
143 |
|
144 |
with gr.Blocks() as demo:
|
145 |
-
gr.HTML(
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
</style>""")
|
150 |
|
151 |
-
#
|
152 |
-
with gr.Column(elem_id="login_panel"):
|
153 |
-
gr.HTML(f'<img
|
154 |
-
'style="pointer-events:none;user-select:none;display:block;margin:auto;"
|
155 |
-
user_in
|
156 |
-
pass_in
|
157 |
-
login_btn= gr.Button("🔒 Log in")
|
158 |
-
login_msg= gr.Markdown("", visible=False)
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
'style="pointer-events:none;user-select:none;display:block;margin:auto;" />')
|
164 |
gr.Markdown("# ISCO Annotation", elem_id="title")
|
165 |
idx_state = gr.State(0)
|
166 |
record_md = gr.Markdown()
|
167 |
status_md = gr.Markdown()
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
minor_radio = gr.Radio("Level 3: Minor", choices=[], interactive=True)
|
175 |
-
unit_radio = gr.Radio("Level 4: Unit", choices=[], interactive=True)
|
176 |
download_btn = gr.Button("📥 Download CSV")
|
177 |
download_file = gr.File(visible=False)
|
178 |
|
179 |
-
# login
|
180 |
def check_creds(u,p):
|
181 |
if u==USER and p==PWD:
|
182 |
-
return
|
|
|
|
|
|
|
|
|
|
|
183 |
else:
|
184 |
-
return None, None,
|
|
|
|
|
185 |
|
186 |
login_btn.click(
|
187 |
check_creds,
|
188 |
inputs=[user_in,pass_in],
|
189 |
-
outputs=[login_panel,
|
190 |
)
|
191 |
|
192 |
-
# app
|
193 |
demo.load(lambda: (0,)+load_record(0),
|
194 |
outputs=[idx_state,record_md,status_md,
|
195 |
major_radio,sub_radio,minor_radio,unit_radio])
|
@@ -224,4 +223,6 @@ def build_gradio_app():
|
|
224 |
|
225 |
if __name__=="__main__":
|
226 |
demo = build_gradio_app()
|
227 |
-
demo.queue().launch(show_api=False, share=True
|
|
|
|
|
|
|
|
1 |
import os, base64
|
2 |
import gradio as gr
|
3 |
import pandas as pd
|
|
|
9 |
with open(LOGO_PATH, "rb") as f:
|
10 |
logo_b64 = base64.b64encode(f.read()).decode()
|
11 |
|
12 |
+
# --- load your ISCO hierarchy and build nested dict ---
|
13 |
PROCESSED_DATA_DIR = Path(".")
|
14 |
df_isco = (
|
15 |
pd.read_excel(
|
|
|
24 |
.setdefault(r.sub_major_label, {})\
|
25 |
.setdefault(r.minor_label, [])\
|
26 |
.append(r.unit_label)
|
|
|
27 |
for maj in hierarchy:
|
28 |
for sub in hierarchy[maj]:
|
29 |
for mn in hierarchy[maj][sub]:
|
30 |
hierarchy[maj][sub][mn] = sorted(dict.fromkeys(hierarchy[maj][sub][mn]))
|
31 |
|
32 |
+
def majors(): return sorted(hierarchy.keys())
|
33 |
+
def submajors(m): return sorted(hierarchy.get(m,{}).keys())
|
34 |
+
def minors(m,s): return sorted(hierarchy.get(m,{}).get(s,{}).keys())
|
35 |
+
def units(m,s,n): return hierarchy.get(m,{}).get(s,{}).get(n,[])
|
36 |
|
37 |
+
# --- load your records ---
|
38 |
records = pd.read_excel(PROCESSED_DATA_DIR/"isco_predictions.xlsx").copy()
|
39 |
for c in ["major_label","sub_major_label","minor_label","unit_label"]:
|
40 |
+
if c not in records: records[c] = ""
|
41 |
+
if "annotated" not in records: records["annotated"] = False
|
42 |
records.reset_index(drop=True,inplace=True)
|
43 |
|
44 |
+
# --- clamp + save + load + handlers ---
|
45 |
def clamp_path(maj,sub,mn,un):
|
46 |
maj_c = majors()
|
47 |
+
maj = maj if maj in maj_c else (maj_c[0] if maj_c else "")
|
48 |
sub_c = submajors(maj)
|
49 |
+
sub = sub if sub in sub_c else (sub_c[0] if sub_c else "")
|
50 |
mn_c = minors(maj,sub)
|
51 |
+
mn = mn if mn in mn_c else (mn_c[0] if mn_c else "")
|
52 |
un_c = units(maj,sub,mn)
|
53 |
+
un = un if un in un_c else (un_c[0] if un_c else "")
|
54 |
return maj,sub,mn,un, maj_c,sub_c,mn_c,un_c
|
55 |
|
56 |
def save_record(i,maj,sub,mn,un):
|
|
|
62 |
|
63 |
def load_record(i):
|
64 |
r = records.loc[i]
|
65 |
+
maj,sub,mn,un,maj_c,sub_c,mn_c,un_c = clamp_path(r.major_label, r.sub_major_label, r.minor_label, r.unit_label)
|
66 |
+
save_record(i,maj,sub,mn,un)
|
|
|
|
|
67 |
md = f"## Occupation: {r.occupation_title_main}\n## Industry: {r.industry_title_main}"
|
68 |
return (
|
69 |
md,
|
|
|
75 |
)
|
76 |
|
77 |
def on_major_change(new_maj,i):
|
78 |
+
sub_c = submajors(new_maj); sub = sub_c[0] if sub_c else ""
|
79 |
+
mn_c = minors(new_maj,sub); mn = mn_c[0] if mn_c else ""
|
80 |
un_c = units(new_maj,sub,mn); un = un_c[0] if un_c else ""
|
81 |
save_record(i,new_maj,sub,mn,un)
|
82 |
return (
|
83 |
+
gr.update(choices=majors(), value=new_maj),
|
84 |
+
gr.update(choices=sub_c, value=sub),
|
85 |
+
gr.update(choices=mn_c, value=mn),
|
86 |
+
gr.update(choices=un_c, value=un),
|
87 |
status_text(i),
|
88 |
)
|
89 |
|
|
|
94 |
records.loc[i,"annotated"]=True
|
95 |
return (
|
96 |
gr.update(choices=submajors(maj),value=new_sub),
|
97 |
+
gr.update(choices=mn_c, value=mn),
|
98 |
+
gr.update(choices=un_c, value=un),
|
99 |
status_text(i),
|
100 |
)
|
101 |
|
|
|
105 |
records.loc[i,"annotated"]=True
|
106 |
return (
|
107 |
gr.update(choices=minors(maj,sub),value=new_mn),
|
108 |
+
gr.update(choices=un_c, value=un),
|
109 |
status_text(i),
|
110 |
)
|
111 |
|
112 |
def on_unit_change(new_un,i,maj,sub,mn):
|
113 |
un_c=units(maj,sub,mn)
|
114 |
+
new_un = new_un if new_un in un_c else (un_c[0] if un_c else "")
|
115 |
records.loc[i,"unit_label"]=new_un
|
116 |
records.loc[i,"annotated"]=True
|
117 |
return gr.update(choices=un_c,value=new_un), status_text(i)
|
|
|
119 |
def go_next(i): return (i+1)%len(records)
|
120 |
def go_prev(i): return (i-1)%len(records)
|
121 |
|
122 |
+
def save_and_jump(i,direction):
|
|
|
123 |
r=records.loc[i]
|
124 |
+
maj,sub,mn,un,*_ = clamp_path(r.major_label,r.sub_major_label,r.minor_label,r.unit_label)
|
125 |
save_record(i,maj,sub,mn,un)
|
126 |
+
j = go_next(i) if direction=="next" else go_prev(i)
|
127 |
return (j,)+load_record(j)
|
128 |
|
129 |
def download_csv():
|
130 |
+
out = PROCESSED_DATA_DIR/"annotated_output.csv"
|
131 |
+
records.to_csv(out,index=False)
|
132 |
+
return str(out)
|
133 |
|
134 |
+
# --- build with login gate ---
|
135 |
def build_gradio_app():
|
136 |
USER = os.getenv("APP_USER","")
|
137 |
PWD = os.getenv("APP_PASS","")
|
138 |
|
139 |
with gr.Blocks() as demo:
|
140 |
+
gr.HTML("""
|
141 |
+
<style>
|
142 |
+
footer, .share-link, .api-link {display:none!important}
|
143 |
+
#title{text-align:center;margin:0.5em}
|
144 |
</style>""")
|
145 |
|
146 |
+
# === capture the two panels ===
|
147 |
+
with gr.Column(elem_id="login_panel") as login_panel:
|
148 |
+
gr.HTML(f'<img src="data:image/png;base64,{logo_b64}" width="160" '
|
149 |
+
'style="pointer-events:none;user-select:none;display:block;margin:auto;"/>')
|
150 |
+
user_in = gr.Textbox(label="Username")
|
151 |
+
pass_in = gr.Textbox(label="Password", type="password")
|
152 |
+
login_btn = gr.Button("🔒 Log in")
|
153 |
+
login_msg = gr.Markdown("", visible=False)
|
154 |
+
|
155 |
+
with gr.Column(elem_id="app_panel", visible=False) as app_panel:
|
156 |
+
gr.HTML(f'<img src="data:image/png;base64,{logo_b64}" width="160" '
|
157 |
+
'style="pointer-events:none;user-select:none;display:block;margin:auto;"/>')
|
|
|
158 |
gr.Markdown("# ISCO Annotation", elem_id="title")
|
159 |
idx_state = gr.State(0)
|
160 |
record_md = gr.Markdown()
|
161 |
status_md = gr.Markdown()
|
162 |
+
prev_btn = gr.Button("⬅ Previous")
|
163 |
+
next_btn = gr.Button("✅ Next")
|
164 |
+
major_radio = gr.Radio("Level 1 Major", choices=[], interactive=True)
|
165 |
+
sub_radio = gr.Radio("Level 2 Sub-major",choices=[], interactive=True)
|
166 |
+
minor_radio = gr.Radio("Level 3 Minor", choices=[], interactive=True)
|
167 |
+
unit_radio = gr.Radio("Level 4 Unit", choices=[], interactive=True)
|
|
|
|
|
168 |
download_btn = gr.Button("📥 Download CSV")
|
169 |
download_file = gr.File(visible=False)
|
170 |
|
171 |
+
# --- login handler ---
|
172 |
def check_creds(u,p):
|
173 |
if u==USER and p==PWD:
|
174 |
+
return (
|
175 |
+
gr.update(visible=False), # hide login
|
176 |
+
gr.update(visible=True), # show app
|
177 |
+
gr.update(visible=False), # hide error
|
178 |
+
"" # clear user_in
|
179 |
+
)
|
180 |
else:
|
181 |
+
return (None, None,
|
182 |
+
gr.update(visible=True,value="❌ Bad credentials"),
|
183 |
+
"")
|
184 |
|
185 |
login_btn.click(
|
186 |
check_creds,
|
187 |
inputs=[user_in,pass_in],
|
188 |
+
outputs=[login_panel,app_panel,login_msg,user_in]
|
189 |
)
|
190 |
|
191 |
+
# --- main app wiring ---
|
192 |
demo.load(lambda: (0,)+load_record(0),
|
193 |
outputs=[idx_state,record_md,status_md,
|
194 |
major_radio,sub_radio,minor_radio,unit_radio])
|
|
|
223 |
|
224 |
if __name__=="__main__":
|
225 |
demo = build_gradio_app()
|
226 |
+
demo.queue().launch(show_api=False, share=True,
|
227 |
+
# require Gradio‐level HTTP auth
|
228 |
+
auth=(os.getenv("APP_USER",""), os.getenv("APP_PASS","")))
|