Spaces:
Sleeping
Sleeping
Pointf5ive commited on
Commit Β·
df7d6cf
1
Parent(s): 309e8ca
Fix Smoke Signal hang: remove wizard.load, disable SSR
Browse files- app.py +72 -7
- smoke_signal_tab.py +10 -10
app.py
CHANGED
|
@@ -71,16 +71,36 @@ CSS = """
|
|
| 71 |
|
| 72 |
footer { display: none !important; }
|
| 73 |
|
| 74 |
-
/* Keep native Gradio tab behavior, but force tab bar above rich dashboard overlays. */
|
| 75 |
.gradio-container [role="tablist"] {
|
| 76 |
position: sticky !important;
|
| 77 |
top: 0 !important;
|
| 78 |
-
z-index:
|
| 79 |
background: #fffaf0 !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
}
|
| 81 |
|
| 82 |
.gradio-container [role="tab"] {
|
| 83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
}
|
| 85 |
|
| 86 |
#hidden-export, #hidden-status {
|
|
@@ -240,7 +260,52 @@ footer { display: none !important; }
|
|
| 240 |
}
|
| 241 |
"""
|
| 242 |
|
| 243 |
-
HEAD = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
|
| 245 |
|
| 246 |
REVISION_ACTIONS = {
|
|
@@ -794,7 +859,7 @@ def dashboard_html(path: Path, notice: str = "") -> str:
|
|
| 794 |
<div class="nav-item">β₯ TOTEM Analytics</div>
|
| 795 |
<div class="nav-item">β· Revision Queue</div>
|
| 796 |
<div class="nav-item">⬑ Risk Clusters</div>
|
| 797 |
-
<div class="nav-item"
|
| 798 |
<div class="nav-item">β© Export</div>
|
| 799 |
<div class="sidebar-foot">Knowledge. Structure.<br>Story. Performance.</div>
|
| 800 |
</aside>
|
|
@@ -1111,7 +1176,7 @@ def single_score(active_path, sequence, stanza_id, draft_pass, clarity, rhythm,
|
|
| 1111 |
|
| 1112 |
# ββ GRADIO INTERFACE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1113 |
|
| 1114 |
-
with gr.Blocks(title="TOTEM Studio") as demo:
|
| 1115 |
active_path = gr.State(str(DEFAULT_WORKBOOK))
|
| 1116 |
log_state = gr.State(pd.DataFrame(columns=LOG_COLUMNS))
|
| 1117 |
|
|
@@ -1324,4 +1389,4 @@ with gr.Blocks(title="TOTEM Studio") as demo:
|
|
| 1324 |
|
| 1325 |
|
| 1326 |
if __name__ == "__main__":
|
| 1327 |
-
demo.launch(
|
|
|
|
| 71 |
|
| 72 |
footer { display: none !important; }
|
| 73 |
|
|
|
|
| 74 |
.gradio-container [role="tablist"] {
|
| 75 |
position: sticky !important;
|
| 76 |
top: 0 !important;
|
| 77 |
+
z-index: 60 !important;
|
| 78 |
background: #fffaf0 !important;
|
| 79 |
+
border-bottom: 1px solid var(--studio-line);
|
| 80 |
+
padding: 8px 10px;
|
| 81 |
+
gap: 8px;
|
| 82 |
+
overflow: visible !important;
|
| 83 |
}
|
| 84 |
|
| 85 |
.gradio-container [role="tab"] {
|
| 86 |
+
opacity: 1 !important;
|
| 87 |
+
visibility: visible !important;
|
| 88 |
+
color: var(--studio-ink) !important;
|
| 89 |
+
background: #f4ecd6 !important;
|
| 90 |
+
border: 1px solid var(--studio-line) !important;
|
| 91 |
+
border-radius: 8px !important;
|
| 92 |
+
padding: 8px 14px !important;
|
| 93 |
+
font-weight: 700 !important;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
.gradio-container [role="tab"][aria-selected="true"] {
|
| 97 |
+
background: linear-gradient(90deg, #f5c93c, #e5a721) !important;
|
| 98 |
+
color: white !important;
|
| 99 |
+
border-color: #d99e1b !important;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
.nav-item[role="button"] {
|
| 103 |
+
cursor: pointer;
|
| 104 |
}
|
| 105 |
|
| 106 |
#hidden-export, #hidden-status {
|
|
|
|
| 260 |
}
|
| 261 |
"""
|
| 262 |
|
| 263 |
+
HEAD = """
|
| 264 |
+
<script>
|
| 265 |
+
(() => {
|
| 266 |
+
const switchToCodexTab = () => {
|
| 267 |
+
const tabs = Array.from(document.querySelectorAll('[role="tab"]'));
|
| 268 |
+
if (!tabs.length) return false;
|
| 269 |
+
const target = tabs.find((tab) => /codex extractor/i.test((tab.textContent || "").trim()));
|
| 270 |
+
if (target) {
|
| 271 |
+
target.click();
|
| 272 |
+
return true;
|
| 273 |
+
}
|
| 274 |
+
if (tabs.length > 1) {
|
| 275 |
+
tabs[1].click();
|
| 276 |
+
return true;
|
| 277 |
+
}
|
| 278 |
+
return false;
|
| 279 |
+
};
|
| 280 |
+
|
| 281 |
+
const bindSidebarCodexLinks = () => {
|
| 282 |
+
document.querySelectorAll('.js-open-codex').forEach((el) => {
|
| 283 |
+
if (el.dataset.boundCodexNav === "1") return;
|
| 284 |
+
el.dataset.boundCodexNav = "1";
|
| 285 |
+
el.addEventListener('click', (e) => {
|
| 286 |
+
e.preventDefault();
|
| 287 |
+
switchToCodexTab();
|
| 288 |
+
});
|
| 289 |
+
el.addEventListener('keydown', (e) => {
|
| 290 |
+
if (e.key === 'Enter' || e.key === ' ') {
|
| 291 |
+
e.preventDefault();
|
| 292 |
+
switchToCodexTab();
|
| 293 |
+
}
|
| 294 |
+
});
|
| 295 |
+
});
|
| 296 |
+
};
|
| 297 |
+
|
| 298 |
+
const observer = new MutationObserver(() => bindSidebarCodexLinks());
|
| 299 |
+
observer.observe(document.documentElement, { childList: true, subtree: true });
|
| 300 |
+
|
| 301 |
+
if (document.readyState === 'loading') {
|
| 302 |
+
document.addEventListener('DOMContentLoaded', bindSidebarCodexLinks);
|
| 303 |
+
} else {
|
| 304 |
+
bindSidebarCodexLinks();
|
| 305 |
+
}
|
| 306 |
+
})();
|
| 307 |
+
</script>
|
| 308 |
+
"""
|
| 309 |
|
| 310 |
|
| 311 |
REVISION_ACTIONS = {
|
|
|
|
| 859 |
<div class="nav-item">β₯ TOTEM Analytics</div>
|
| 860 |
<div class="nav-item">β· Revision Queue</div>
|
| 861 |
<div class="nav-item">⬑ Risk Clusters</div>
|
| 862 |
+
<div class="nav-item js-open-codex" role="button" tabindex="0" aria-label="Open Codex Extractor tab">β Codex Extractor</div>
|
| 863 |
<div class="nav-item">β© Export</div>
|
| 864 |
<div class="sidebar-foot">Knowledge. Structure.<br>Story. Performance.</div>
|
| 865 |
</aside>
|
|
|
|
| 1176 |
|
| 1177 |
# ββ GRADIO INTERFACE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1178 |
|
| 1179 |
+
with gr.Blocks(title="TOTEM Studio", css=CSS + SS_CSS, head=HEAD) as demo:
|
| 1180 |
active_path = gr.State(str(DEFAULT_WORKBOOK))
|
| 1181 |
log_state = gr.State(pd.DataFrame(columns=LOG_COLUMNS))
|
| 1182 |
|
|
|
|
| 1389 |
|
| 1390 |
|
| 1391 |
if __name__ == "__main__":
|
| 1392 |
+
demo.launch()
|
smoke_signal_tab.py
CHANGED
|
@@ -824,9 +824,7 @@ def run_ocr() -> tuple:
|
|
| 824 |
"book_id": book_id, "filename": row["filename"],
|
| 825 |
"page": page_num, "region_id": region_id,
|
| 826 |
"region_class": "narration",
|
| 827 |
-
"rights_class": row.get("rights_class", "unknown"),
|
| 828 |
"crop_path": page_data.get("render_path",""),
|
| 829 |
-
"page_image_path": page_data.get("render_path",""),
|
| 830 |
"raw_ocr": raw_text,
|
| 831 |
"confidence": conf, "confidence_class": conf_class,
|
| 832 |
"status": "quarantine" if conf_class == "quarantine" else "pending",
|
|
@@ -969,10 +967,8 @@ def save_review_decision(idx: int, final_text: str, action: str, reviewer: str,
|
|
| 969 |
f.write(json.dumps({
|
| 970 |
**decision,
|
| 971 |
"region_class": region_class,
|
| 972 |
-
"rights_class": item.get("rights_class", "unknown"),
|
| 973 |
"confidence": float(item.get("confidence", 0)),
|
| 974 |
"conf_class": item.get("confidence_class",""),
|
| 975 |
-
"page_image_path": item.get("page_image_path", item.get("crop_path", "")),
|
| 976 |
}) + "\n")
|
| 977 |
|
| 978 |
cal = load_calibration()
|
|
@@ -1143,7 +1139,6 @@ def smoke_signal_tab():
|
|
| 1143 |
label="Source Manifest",
|
| 1144 |
interactive=False,
|
| 1145 |
wrap=True,
|
| 1146 |
-
value=load_manifest_df(),
|
| 1147 |
)
|
| 1148 |
ingest_log = gr.Textbox(label="Log", lines=6, interactive=False, elem_classes=["ss-log"])
|
| 1149 |
|
|
@@ -1153,6 +1148,7 @@ def smoke_signal_tab():
|
|
| 1153 |
outputs=[ingest_status, manifest_table, ingest_log],
|
| 1154 |
)
|
| 1155 |
gr.HTML('<div style="height:16px"></div>')
|
|
|
|
| 1156 |
|
| 1157 |
# ββ STEP 2: PROFILE βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1158 |
with gr.TabItem("β‘ Profile", id="ss-profile"):
|
|
@@ -1202,7 +1198,6 @@ def smoke_signal_tab():
|
|
| 1202 |
|
| 1203 |
review_status = gr.HTML(_review_status_html())
|
| 1204 |
training_feedback = gr.HTML()
|
| 1205 |
-
init_img, init_raw, init_info, _, _ = get_review_item(0)
|
| 1206 |
|
| 1207 |
with gr.Row():
|
| 1208 |
with gr.Column(scale=2):
|
|
@@ -1210,22 +1205,20 @@ def smoke_signal_tab():
|
|
| 1210 |
label="Page Render",
|
| 1211 |
type="filepath",
|
| 1212 |
height=420,
|
| 1213 |
-
|
| 1214 |
)
|
| 1215 |
-
item_info = gr.HTML(
|
| 1216 |
|
| 1217 |
with gr.Column(scale=2):
|
| 1218 |
raw_text_box = gr.Textbox(
|
| 1219 |
label="Raw OCR",
|
| 1220 |
lines=6,
|
| 1221 |
interactive=False,
|
| 1222 |
-
value=init_raw,
|
| 1223 |
)
|
| 1224 |
final_text_box = gr.Textbox(
|
| 1225 |
label="Final Text (edit to correct)",
|
| 1226 |
lines=8,
|
| 1227 |
interactive=True,
|
| 1228 |
-
value=init_raw,
|
| 1229 |
)
|
| 1230 |
reviewer_name = gr.Textbox(label="Your name", placeholder="e.g. jamal", scale=1)
|
| 1231 |
reason_code = gr.Dropdown(
|
|
@@ -1249,6 +1242,11 @@ def smoke_signal_tab():
|
|
| 1249 |
review_outputs = [training_feedback, review_image, raw_text_box, item_info,
|
| 1250 |
gr.State(), gr.State()]
|
| 1251 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1252 |
def next_item(idx):
|
| 1253 |
new_idx = idx + 1
|
| 1254 |
img, raw, info, done, total = get_review_item(new_idx)
|
|
@@ -1292,6 +1290,8 @@ def smoke_signal_tab():
|
|
| 1292 |
next_btn.click(_next, inputs=[current_idx],
|
| 1293 |
outputs=[current_idx, review_image, raw_text_box, final_text_box, item_info])
|
| 1294 |
|
|
|
|
|
|
|
| 1295 |
# ββ STEP 5: EXPORT ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1296 |
with gr.TabItem("β€ Export", id="ss-export"):
|
| 1297 |
gr.HTML("""<div class="ss-panel-header" style="padding:20px 0 0 0">
|
|
|
|
| 824 |
"book_id": book_id, "filename": row["filename"],
|
| 825 |
"page": page_num, "region_id": region_id,
|
| 826 |
"region_class": "narration",
|
|
|
|
| 827 |
"crop_path": page_data.get("render_path",""),
|
|
|
|
| 828 |
"raw_ocr": raw_text,
|
| 829 |
"confidence": conf, "confidence_class": conf_class,
|
| 830 |
"status": "quarantine" if conf_class == "quarantine" else "pending",
|
|
|
|
| 967 |
f.write(json.dumps({
|
| 968 |
**decision,
|
| 969 |
"region_class": region_class,
|
|
|
|
| 970 |
"confidence": float(item.get("confidence", 0)),
|
| 971 |
"conf_class": item.get("confidence_class",""),
|
|
|
|
| 972 |
}) + "\n")
|
| 973 |
|
| 974 |
cal = load_calibration()
|
|
|
|
| 1139 |
label="Source Manifest",
|
| 1140 |
interactive=False,
|
| 1141 |
wrap=True,
|
|
|
|
| 1142 |
)
|
| 1143 |
ingest_log = gr.Textbox(label="Log", lines=6, interactive=False, elem_classes=["ss-log"])
|
| 1144 |
|
|
|
|
| 1148 |
outputs=[ingest_status, manifest_table, ingest_log],
|
| 1149 |
)
|
| 1150 |
gr.HTML('<div style="height:16px"></div>')
|
| 1151 |
+
wizard.load(lambda: (load_manifest_df(),), outputs=[manifest_table])
|
| 1152 |
|
| 1153 |
# ββ STEP 2: PROFILE βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1154 |
with gr.TabItem("β‘ Profile", id="ss-profile"):
|
|
|
|
| 1198 |
|
| 1199 |
review_status = gr.HTML(_review_status_html())
|
| 1200 |
training_feedback = gr.HTML()
|
|
|
|
| 1201 |
|
| 1202 |
with gr.Row():
|
| 1203 |
with gr.Column(scale=2):
|
|
|
|
| 1205 |
label="Page Render",
|
| 1206 |
type="filepath",
|
| 1207 |
height=420,
|
| 1208 |
+
show_download_button=False,
|
| 1209 |
)
|
| 1210 |
+
item_info = gr.HTML()
|
| 1211 |
|
| 1212 |
with gr.Column(scale=2):
|
| 1213 |
raw_text_box = gr.Textbox(
|
| 1214 |
label="Raw OCR",
|
| 1215 |
lines=6,
|
| 1216 |
interactive=False,
|
|
|
|
| 1217 |
)
|
| 1218 |
final_text_box = gr.Textbox(
|
| 1219 |
label="Final Text (edit to correct)",
|
| 1220 |
lines=8,
|
| 1221 |
interactive=True,
|
|
|
|
| 1222 |
)
|
| 1223 |
reviewer_name = gr.Textbox(label="Your name", placeholder="e.g. jamal", scale=1)
|
| 1224 |
reason_code = gr.Dropdown(
|
|
|
|
| 1242 |
review_outputs = [training_feedback, review_image, raw_text_box, item_info,
|
| 1243 |
gr.State(), gr.State()]
|
| 1244 |
|
| 1245 |
+
def load_review():
|
| 1246 |
+
img, raw, info, done, total = get_review_item(0)
|
| 1247 |
+
status = _review_status_html()
|
| 1248 |
+
return status, img, raw, raw, info
|
| 1249 |
+
|
| 1250 |
def next_item(idx):
|
| 1251 |
new_idx = idx + 1
|
| 1252 |
img, raw, info, done, total = get_review_item(new_idx)
|
|
|
|
| 1290 |
next_btn.click(_next, inputs=[current_idx],
|
| 1291 |
outputs=[current_idx, review_image, raw_text_box, final_text_box, item_info])
|
| 1292 |
|
| 1293 |
+
wizard.load(load_review, outputs=[review_status, review_image, raw_text_box, final_text_box, item_info])
|
| 1294 |
+
|
| 1295 |
# ββ STEP 5: EXPORT ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1296 |
with gr.TabItem("β€ Export", id="ss-export"):
|
| 1297 |
gr.HTML("""<div class="ss-panel-header" style="padding:20px 0 0 0">
|