Spaces:
Sleeping
Sleeping
Commit ·
cddf904
1
Parent(s): 320d35a
Refine merchant interface workflow
Browse files
app.py
CHANGED
|
@@ -696,6 +696,29 @@ def merchant_product_scores(metric: str) -> List[List[Any]]:
|
|
| 696 |
return rows or [["No rows", "Try Refresh", metric, "-", 0.0, 0.0, 0.0]]
|
| 697 |
|
| 698 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 699 |
def merchant_score_filter(aspect: str, prediction: str, category: str) -> List[List[Any]]:
|
| 700 |
rows = []
|
| 701 |
for product in PRODUCTS:
|
|
@@ -823,6 +846,62 @@ def export_risk_rows(rows):
|
|
| 823 |
)
|
| 824 |
|
| 825 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 826 |
def external_review_predict(review: str, features: str, categories: str, price: Any, rating: Any, count: Any, selected_aspect: str) -> Tuple[str, str, List[List[Any]]]:
|
| 827 |
try:
|
| 828 |
result = _predict_custom(review, features, categories, price, rating, count)
|
|
@@ -862,6 +941,52 @@ def export_external_rows(rows):
|
|
| 862 |
)
|
| 863 |
|
| 864 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 865 |
def _status_html() -> str:
|
| 866 |
missing = []
|
| 867 |
if not CHECKPOINT_PATH.exists() or CHECKPOINT_PATH.stat().st_size < 1024 * 1024:
|
|
@@ -932,10 +1057,11 @@ mark { background:#fde68a; color:#111827; border-radius:4px; padding:1px 3px; }
|
|
| 932 |
.info-tip { position:relative; display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px; border-radius:999px; border:1px solid #bfdbfe; background:#eff6ff; color:#1e40af; font-weight:800; cursor:help; }
|
| 933 |
.info-tip .tip-content { display:none; position:absolute; right:0; top:30px; z-index:20; width:420px; max-width:80vw; background:white; border:1px solid var(--line); border-radius:8px; padding:10px; box-shadow:0 12px 30px rgba(15,23,42,.16); }
|
| 934 |
.info-tip:hover .tip-content { display:block; }
|
| 935 |
-
.negative-grid { display:grid; grid-template-columns:repeat(
|
| 936 |
.negative-card { display:grid; grid-template-columns:64px 1fr; gap:10px; border:1px solid #fecaca; border-left:4px solid #ef4444; border-radius:8px; padding:10px; background:#fffafa; }
|
| 937 |
.negative-card p { margin:5px 0 0; color:#334155; font-size:13px; }
|
| 938 |
.mini-product-img { width:64px; height:76px; object-fit:cover; border-radius:6px; border:1px solid var(--line); background:#f8fafc; }
|
|
|
|
| 939 |
@media (max-width:860px) { .aspect-grid, .metric-grid, .product-layout, .decision-layout, .negative-grid { grid-template-columns:1fr; } .product-img { width:100%; height:180px; } }
|
| 940 |
"""
|
| 941 |
|
|
@@ -981,6 +1107,7 @@ def build_app() -> gr.Blocks:
|
|
| 981 |
gr.HTML('<div class="module-head"><h3>Product Score Monitor</h3><span class="info-tip">i<span class="tip-content">' + model_info_html() + '</span></span></div>')
|
| 982 |
gr.HTML('<div class="small-label">Most negative products across the six aspects</div>')
|
| 983 |
negative_spotlight = gr.HTML('<div class="note-card">Loading most negative products...</div>')
|
|
|
|
| 984 |
with gr.Accordion("Score filters and export", open=True):
|
| 985 |
with gr.Row():
|
| 986 |
merchant_metric = gr.Dropdown(["Overall"] + ASPECTS, value="Overall", label="Aspect")
|
|
@@ -992,33 +1119,52 @@ def build_app() -> gr.Blocks:
|
|
| 992 |
export_scores = gr.Button("Export Score List")
|
| 993 |
merchant_scores_file = gr.File(label="Downloaded score CSV", interactive=False)
|
| 994 |
with gr.Accordion("New Product Metadata Risk Screening", open=False):
|
|
|
|
| 995 |
with gr.Row():
|
| 996 |
with gr.Column(scale=1):
|
| 997 |
-
|
| 998 |
-
|
| 999 |
-
|
| 1000 |
-
|
| 1001 |
-
|
| 1002 |
-
|
| 1003 |
-
|
| 1004 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1005 |
with gr.Column(scale=1):
|
| 1006 |
risk_summary = gr.HTML()
|
| 1007 |
risk_table = gr.Dataframe(headers=["Aspect", "Risk Level", "Model Signal", "Confidence", "Reason"], value=[["Click Predict Metadata Risk", "", "", 0.0, ""]], datatype=["str", "str", "str", "number", "str"], interactive=False)
|
| 1008 |
export_risk = gr.Button("Export Risk Result")
|
| 1009 |
risk_file = gr.File(label="Downloaded risk CSV", interactive=False)
|
| 1010 |
with gr.Accordion("External Review Prediction", open=False):
|
|
|
|
| 1011 |
with gr.Row():
|
| 1012 |
with gr.Column(scale=1):
|
| 1013 |
-
|
| 1014 |
-
|
| 1015 |
-
|
| 1016 |
-
|
| 1017 |
-
|
| 1018 |
-
|
| 1019 |
-
|
| 1020 |
-
|
| 1021 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1022 |
with gr.Column(scale=1):
|
| 1023 |
ext_overall = gr.HTML()
|
| 1024 |
ext_aspects = gr.HTML()
|
|
@@ -1030,11 +1176,15 @@ def build_app() -> gr.Blocks:
|
|
| 1030 |
merchant_metric.change(merchant_score_filter, [merchant_metric, merchant_prediction, merchant_category], merchant_scores)
|
| 1031 |
merchant_prediction.change(merchant_score_filter, [merchant_metric, merchant_prediction, merchant_category], merchant_scores)
|
| 1032 |
merchant_category.change(merchant_score_filter, [merchant_metric, merchant_prediction, merchant_category], merchant_scores)
|
| 1033 |
-
|
| 1034 |
screen_btn.click(screen_new_product, [new_features, new_categories, new_price, new_rating, new_count, new_focus], [risk_summary, risk_table])
|
|
|
|
|
|
|
| 1035 |
export_risk.click(export_risk_rows, risk_table, risk_file)
|
| 1036 |
-
|
| 1037 |
external_btn.click(external_review_predict, [external_review, ext_features, ext_categories, ext_price, ext_rating, ext_count, ext_aspect], [ext_overall, ext_aspects, ext_table])
|
|
|
|
|
|
|
| 1038 |
export_ext.click(export_external_rows, ext_table, ext_file)
|
| 1039 |
|
| 1040 |
with gr.Tab("Research Metrics"):
|
|
|
|
| 696 |
return rows or [["No rows", "Try Refresh", metric, "-", 0.0, 0.0, 0.0]]
|
| 697 |
|
| 698 |
|
| 699 |
+
def merchant_aspect_overview() -> List[List[Any]]:
|
| 700 |
+
rows = []
|
| 701 |
+
for aspect in ASPECTS:
|
| 702 |
+
neg_count, top_name, top_conf, top_reason = 0, "-", 0.0, "-"
|
| 703 |
+
for product in PRODUCTS:
|
| 704 |
+
try:
|
| 705 |
+
result = _predict_product(product["name"])
|
| 706 |
+
except Exception:
|
| 707 |
+
continue
|
| 708 |
+
d = result.get("aspect_details", {}).get(aspect, {})
|
| 709 |
+
if d.get("label") != "Negative":
|
| 710 |
+
continue
|
| 711 |
+
neg_count += 1
|
| 712 |
+
conf = _safe_float(d.get("confidence"))
|
| 713 |
+
if conf >= top_conf:
|
| 714 |
+
hits = _keyword_hits(product.get("review", ""), aspect)
|
| 715 |
+
top_name = product["name"]
|
| 716 |
+
top_conf = conf
|
| 717 |
+
top_reason = ", ".join(hits) if hits else _short_text(product.get("review") or product.get("features"), 90)
|
| 718 |
+
rows.append([aspect, neg_count, top_name, round(top_conf, 4), top_reason])
|
| 719 |
+
return rows
|
| 720 |
+
|
| 721 |
+
|
| 722 |
def merchant_score_filter(aspect: str, prediction: str, category: str) -> List[List[Any]]:
|
| 723 |
rows = []
|
| 724 |
for product in PRODUCTS:
|
|
|
|
| 846 |
)
|
| 847 |
|
| 848 |
|
| 849 |
+
def _read_uploaded_records(file_obj) -> List[Dict[str, Any]]:
|
| 850 |
+
if not file_obj:
|
| 851 |
+
return []
|
| 852 |
+
path = Path(getattr(file_obj, "name", file_obj))
|
| 853 |
+
try:
|
| 854 |
+
if path.suffix.lower() == ".json":
|
| 855 |
+
data = json.loads(path.read_text(encoding="utf-8"))
|
| 856 |
+
if isinstance(data, dict):
|
| 857 |
+
data = data.get("items") or data.get("data") or [data]
|
| 858 |
+
return data if isinstance(data, list) else []
|
| 859 |
+
with path.open("r", encoding="utf-8-sig", newline="") as fh:
|
| 860 |
+
return list(csv.DictReader(fh))
|
| 861 |
+
except Exception:
|
| 862 |
+
return []
|
| 863 |
+
|
| 864 |
+
|
| 865 |
+
def download_new_product_template():
|
| 866 |
+
rows = [[
|
| 867 |
+
"Cotton Polyester Blend, Slim Fit, Graphic Print, Machine Wash",
|
| 868 |
+
"Clothing > Women > Tops > T-Shirts",
|
| 869 |
+
29.99,
|
| 870 |
+
4.1,
|
| 871 |
+
35,
|
| 872 |
+
"All",
|
| 873 |
+
]]
|
| 874 |
+
return _write_csv_download(
|
| 875 |
+
"new_product_metadata_template.csv",
|
| 876 |
+
["features", "categories", "price", "average_rating", "rating_number", "focus_aspect"],
|
| 877 |
+
rows,
|
| 878 |
+
)
|
| 879 |
+
|
| 880 |
+
|
| 881 |
+
def batch_screen_new_products(file_obj) -> Tuple[str, List[List[Any]]]:
|
| 882 |
+
records = _read_uploaded_records(file_obj)
|
| 883 |
+
if not records:
|
| 884 |
+
return '<div class="note-card">Upload a CSV or JSON file first.</div>', []
|
| 885 |
+
rows = []
|
| 886 |
+
for i, data in enumerate(records, 1):
|
| 887 |
+
features = data.get("features") or data.get("features_text") or ""
|
| 888 |
+
categories = data.get("categories") or data.get("categories_text") or ""
|
| 889 |
+
price = _safe_float(data.get("price"), 0.0)
|
| 890 |
+
rating = _safe_float(data.get("average_rating") or data.get("rating"), 4.0)
|
| 891 |
+
count = _safe_float(data.get("rating_number") or data.get("rating_count"), 0.0)
|
| 892 |
+
focus = data.get("focus_aspect") or data.get("focus") or "All"
|
| 893 |
+
_, detail_rows = screen_new_product(features, categories, price, rating, count, focus)
|
| 894 |
+
high = [r[0] for r in detail_rows if r[1] == "High"]
|
| 895 |
+
rows.append([
|
| 896 |
+
f"Product {i}",
|
| 897 |
+
"High" if high else "Low",
|
| 898 |
+
focus,
|
| 899 |
+
len(high),
|
| 900 |
+
f"{_short_text(categories, 55)} | high-risk aspects: {', '.join(high) or 'None'}",
|
| 901 |
+
])
|
| 902 |
+
return f'<div class="note-card"><b>Batch screening completed:</b> {len(rows)} products analyzed.</div>', rows
|
| 903 |
+
|
| 904 |
+
|
| 905 |
def external_review_predict(review: str, features: str, categories: str, price: Any, rating: Any, count: Any, selected_aspect: str) -> Tuple[str, str, List[List[Any]]]:
|
| 906 |
try:
|
| 907 |
result = _predict_custom(review, features, categories, price, rating, count)
|
|
|
|
| 941 |
)
|
| 942 |
|
| 943 |
|
| 944 |
+
def download_external_review_template():
|
| 945 |
+
rows = [[
|
| 946 |
+
"The fabric is soft and the color looks good, but it runs small.",
|
| 947 |
+
"Cotton Blend, Slim Fit, Zipper Closure",
|
| 948 |
+
"Clothing > Women > Jackets",
|
| 949 |
+
39.99,
|
| 950 |
+
4.2,
|
| 951 |
+
312,
|
| 952 |
+
"All",
|
| 953 |
+
]]
|
| 954 |
+
return _write_csv_download(
|
| 955 |
+
"external_review_template.csv",
|
| 956 |
+
["review", "features", "categories", "price", "average_rating", "rating_number", "highlight_aspect"],
|
| 957 |
+
rows,
|
| 958 |
+
)
|
| 959 |
+
|
| 960 |
+
|
| 961 |
+
def batch_external_review_predict(file_obj) -> Tuple[str, str, List[List[Any]]]:
|
| 962 |
+
records = _read_uploaded_records(file_obj)
|
| 963 |
+
if not records:
|
| 964 |
+
return '<div class="note-card">Upload a CSV or JSON file first.</div>', "", []
|
| 965 |
+
rows = []
|
| 966 |
+
for i, data in enumerate(records, 1):
|
| 967 |
+
review = data.get("review") or data.get("review_text") or ""
|
| 968 |
+
features = data.get("features") or data.get("features_text") or ""
|
| 969 |
+
categories = data.get("categories") or data.get("categories_text") or ""
|
| 970 |
+
price = _safe_float(data.get("price"), 0.0)
|
| 971 |
+
rating = _safe_float(data.get("average_rating") or data.get("rating"), 4.0)
|
| 972 |
+
count = _safe_float(data.get("rating_number") or data.get("rating_count"), 0.0)
|
| 973 |
+
aspect = data.get("highlight_aspect") or data.get("aspect") or "All"
|
| 974 |
+
overall_html, _, detail_rows = external_review_predict(review, features, categories, price, rating, count, aspect)
|
| 975 |
+
negative = [r[0] for r in detail_rows if r[1] == "Negative"]
|
| 976 |
+
rows.append([
|
| 977 |
+
f"Review {i}",
|
| 978 |
+
f"Negative: {', '.join(negative)}" if negative else "No major negative",
|
| 979 |
+
len(negative),
|
| 980 |
+
f"{_short_text(review, 70)} | {_short_text(categories, 45)}",
|
| 981 |
+
])
|
| 982 |
+
return f'<div class="note-card"><b>Batch review prediction completed:</b> {len(rows)} reviews analyzed.</div>', "", rows
|
| 983 |
+
|
| 984 |
+
|
| 985 |
+
def toggle_analysis_mode(mode: str):
|
| 986 |
+
single = mode.startswith("Single")
|
| 987 |
+
return gr.update(visible=single), gr.update(visible=not single)
|
| 988 |
+
|
| 989 |
+
|
| 990 |
def _status_html() -> str:
|
| 991 |
missing = []
|
| 992 |
if not CHECKPOINT_PATH.exists() or CHECKPOINT_PATH.stat().st_size < 1024 * 1024:
|
|
|
|
| 1057 |
.info-tip { position:relative; display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px; border-radius:999px; border:1px solid #bfdbfe; background:#eff6ff; color:#1e40af; font-weight:800; cursor:help; }
|
| 1058 |
.info-tip .tip-content { display:none; position:absolute; right:0; top:30px; z-index:20; width:420px; max-width:80vw; background:white; border:1px solid var(--line); border-radius:8px; padding:10px; box-shadow:0 12px 30px rgba(15,23,42,.16); }
|
| 1059 |
.info-tip:hover .tip-content { display:block; }
|
| 1060 |
+
.negative-grid { display:grid; grid-template-columns:repeat(6, minmax(0, 1fr)); gap:10px; margin:8px 0 14px; }
|
| 1061 |
.negative-card { display:grid; grid-template-columns:64px 1fr; gap:10px; border:1px solid #fecaca; border-left:4px solid #ef4444; border-radius:8px; padding:10px; background:#fffafa; }
|
| 1062 |
.negative-card p { margin:5px 0 0; color:#334155; font-size:13px; }
|
| 1063 |
.mini-product-img { width:64px; height:76px; object-fit:cover; border-radius:6px; border:1px solid var(--line); background:#f8fafc; }
|
| 1064 |
+
@media (max-width:1100px) { .negative-grid { grid-template-columns:repeat(3, minmax(0, 1fr)); } }
|
| 1065 |
@media (max-width:860px) { .aspect-grid, .metric-grid, .product-layout, .decision-layout, .negative-grid { grid-template-columns:1fr; } .product-img { width:100%; height:180px; } }
|
| 1066 |
"""
|
| 1067 |
|
|
|
|
| 1107 |
gr.HTML('<div class="module-head"><h3>Product Score Monitor</h3><span class="info-tip">i<span class="tip-content">' + model_info_html() + '</span></span></div>')
|
| 1108 |
gr.HTML('<div class="small-label">Most negative products across the six aspects</div>')
|
| 1109 |
negative_spotlight = gr.HTML('<div class="note-card">Loading most negative products...</div>')
|
| 1110 |
+
aspect_overview = gr.Dataframe(headers=["Aspect", "Negative Count", "Most Negative Product", "Confidence", "Reason"], value=merchant_aspect_overview(), datatype=["str", "number", "str", "number", "str"], interactive=False, label="Six-aspect negative overview")
|
| 1111 |
with gr.Accordion("Score filters and export", open=True):
|
| 1112 |
with gr.Row():
|
| 1113 |
merchant_metric = gr.Dropdown(["Overall"] + ASPECTS, value="Overall", label="Aspect")
|
|
|
|
| 1119 |
export_scores = gr.Button("Export Score List")
|
| 1120 |
merchant_scores_file = gr.File(label="Downloaded score CSV", interactive=False)
|
| 1121 |
with gr.Accordion("New Product Metadata Risk Screening", open=False):
|
| 1122 |
+
risk_mode = gr.Radio(["Single product analysis", "Batch import analysis"], value="Single product analysis", label="Analysis mode")
|
| 1123 |
with gr.Row():
|
| 1124 |
with gr.Column(scale=1):
|
| 1125 |
+
with gr.Group(visible=True) as risk_single_group:
|
| 1126 |
+
new_features = gr.Textbox("Cotton Polyester Blend, Slim Fit, Graphic Print, Machine Wash", label="New product features", lines=4)
|
| 1127 |
+
new_categories = gr.Textbox("Clothing > Women > Tops > T-Shirts", label="New product categories")
|
| 1128 |
+
with gr.Row():
|
| 1129 |
+
new_price = gr.Number(29.99, label="Price")
|
| 1130 |
+
new_rating = gr.Number(4.1, label="Expected or early average rating")
|
| 1131 |
+
with gr.Row():
|
| 1132 |
+
new_count = gr.Number(35, label="Expected or early rating count")
|
| 1133 |
+
new_focus = gr.Dropdown(["All"] + ASPECTS, value="All", label="Focus aspect")
|
| 1134 |
+
screen_btn = gr.Button("Predict Metadata Risk", variant="primary")
|
| 1135 |
+
with gr.Group(visible=False) as risk_batch_group:
|
| 1136 |
+
new_import = gr.File(label="Import product metadata JSON/CSV")
|
| 1137 |
+
with gr.Row():
|
| 1138 |
+
new_template = gr.Button("Download Import Template")
|
| 1139 |
+
batch_screen_btn = gr.Button("Batch Analyze Metadata", variant="primary")
|
| 1140 |
+
new_template_file = gr.File(label="Template CSV", interactive=False)
|
| 1141 |
with gr.Column(scale=1):
|
| 1142 |
risk_summary = gr.HTML()
|
| 1143 |
risk_table = gr.Dataframe(headers=["Aspect", "Risk Level", "Model Signal", "Confidence", "Reason"], value=[["Click Predict Metadata Risk", "", "", 0.0, ""]], datatype=["str", "str", "str", "number", "str"], interactive=False)
|
| 1144 |
export_risk = gr.Button("Export Risk Result")
|
| 1145 |
risk_file = gr.File(label="Downloaded risk CSV", interactive=False)
|
| 1146 |
with gr.Accordion("External Review Prediction", open=False):
|
| 1147 |
+
ext_mode = gr.Radio(["Single product analysis", "Batch import analysis"], value="Single product analysis", label="Analysis mode")
|
| 1148 |
with gr.Row():
|
| 1149 |
with gr.Column(scale=1):
|
| 1150 |
+
with gr.Group(visible=True) as ext_single_group:
|
| 1151 |
+
external_review = gr.Textbox("The fabric is soft and the color looks good, but it runs small and the zipper feels weak.", label="External customer review", lines=5)
|
| 1152 |
+
with gr.Row():
|
| 1153 |
+
ext_features = gr.Textbox("Cotton Blend, Slim Fit, Zipper Closure", label="Product features", lines=3)
|
| 1154 |
+
ext_categories = gr.Textbox("Clothing > Women > Jackets", label="Product categories", lines=3)
|
| 1155 |
+
with gr.Row():
|
| 1156 |
+
ext_price = gr.Number(39.99, label="Price")
|
| 1157 |
+
ext_rating = gr.Number(4.2, label="Average rating")
|
| 1158 |
+
with gr.Row():
|
| 1159 |
+
ext_count = gr.Number(312, label="Rating count")
|
| 1160 |
+
ext_aspect = gr.Dropdown(["All"] + ASPECTS, value="All", label="Highlight aspect")
|
| 1161 |
+
external_btn = gr.Button("Analyze External Review", variant="primary")
|
| 1162 |
+
with gr.Group(visible=False) as ext_batch_group:
|
| 1163 |
+
ext_import = gr.File(label="Import review metadata JSON/CSV")
|
| 1164 |
+
with gr.Row():
|
| 1165 |
+
ext_template = gr.Button("Download Import Template")
|
| 1166 |
+
batch_external_btn = gr.Button("Batch Analyze Reviews", variant="primary")
|
| 1167 |
+
ext_template_file = gr.File(label="Template CSV", interactive=False)
|
| 1168 |
with gr.Column(scale=1):
|
| 1169 |
ext_overall = gr.HTML()
|
| 1170 |
ext_aspects = gr.HTML()
|
|
|
|
| 1176 |
merchant_metric.change(merchant_score_filter, [merchant_metric, merchant_prediction, merchant_category], merchant_scores)
|
| 1177 |
merchant_prediction.change(merchant_score_filter, [merchant_metric, merchant_prediction, merchant_category], merchant_scores)
|
| 1178 |
merchant_category.change(merchant_score_filter, [merchant_metric, merchant_prediction, merchant_category], merchant_scores)
|
| 1179 |
+
risk_mode.change(toggle_analysis_mode, risk_mode, [risk_single_group, risk_batch_group])
|
| 1180 |
screen_btn.click(screen_new_product, [new_features, new_categories, new_price, new_rating, new_count, new_focus], [risk_summary, risk_table])
|
| 1181 |
+
new_template.click(download_new_product_template, None, new_template_file)
|
| 1182 |
+
batch_screen_btn.click(batch_screen_new_products, new_import, [risk_summary, risk_table])
|
| 1183 |
export_risk.click(export_risk_rows, risk_table, risk_file)
|
| 1184 |
+
ext_mode.change(toggle_analysis_mode, ext_mode, [ext_single_group, ext_batch_group])
|
| 1185 |
external_btn.click(external_review_predict, [external_review, ext_features, ext_categories, ext_price, ext_rating, ext_count, ext_aspect], [ext_overall, ext_aspects, ext_table])
|
| 1186 |
+
ext_template.click(download_external_review_template, None, ext_template_file)
|
| 1187 |
+
batch_external_btn.click(batch_external_review_predict, ext_import, [ext_overall, ext_aspects, ext_table])
|
| 1188 |
export_ext.click(export_external_rows, ext_table, ext_file)
|
| 1189 |
|
| 1190 |
with gr.Tab("Research Metrics"):
|