mirror of
https://github.com/primedigitaltech/market-assistant.git
synced 2026-07-24 17:21:40 +08:00
fix(report): restore jd_competitor_report after mistaken chart revert
9cc8de9 accidentally rewrote jd_competitor_report.py and merged an oversized report_charts.py. Reset both to f19d112 baseline; keep only obsolete cleanup for removed global bar chart filenames. Made-with: Cursor
This commit is contained in:
parent
9cc8de9006
commit
708f28f40e
File diff suppressed because it is too large
Load Diff
@ -63,52 +63,6 @@ def _merge_labeled_counts_tail(
|
|||||||
return head
|
return head
|
||||||
|
|
||||||
|
|
||||||
def _matrix_block_chart_slug(group: str, index: int) -> str:
|
|
||||||
"""与 ``jd_competitor_report._scenario_group_asset_slug`` 规则一致(文件名对齐)。"""
|
|
||||||
raw = (group or "").strip()
|
|
||||||
core = re.sub(r"[^\w\u4e00-\u9fff-]", "", raw)[:20]
|
|
||||||
if not core:
|
|
||||||
core = "group"
|
|
||||||
return f"i{index:02d}_{core}"
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_price_from_text(s: str) -> float | None:
|
|
||||||
t = (s or "").strip().replace(",", "")
|
|
||||||
if not t:
|
|
||||||
return None
|
|
||||||
m = re.search(r"(\d+(?:\.\d+)?)", t)
|
|
||||||
if not m:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
v = float(m.group(1))
|
|
||||||
return v if 0 < v < 1_000_000 else None
|
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_comment_fuzzy_sortable(s: str) -> float | None:
|
|
||||||
"""评价量/声量展示文案粗转可排序正数(启发式,非精确条数)。"""
|
|
||||||
t = (s or "").strip().replace("+", "+").replace(" ", "")
|
|
||||||
if not t:
|
|
||||||
return None
|
|
||||||
m = re.search(r"(\d+(?:\.\d+)?)\s*万\+?", t)
|
|
||||||
if m:
|
|
||||||
return float(m.group(1)) * 10_000
|
|
||||||
m = re.search(r"(\d+(?:\.\d+)?)\s*亿\+?", t)
|
|
||||||
if m:
|
|
||||||
return float(m.group(1)) * 100_000_000
|
|
||||||
m = re.search(r"(\d+(?:\.\d+)?)\s*万", t)
|
|
||||||
if m:
|
|
||||||
return float(m.group(1)) * 10_000
|
|
||||||
m = re.search(r"(\d{2,})\+", t)
|
|
||||||
if m:
|
|
||||||
return float(m.group(1))
|
|
||||||
m = re.search(r"(\d+)", t)
|
|
||||||
if m:
|
|
||||||
return float(m.group(1))
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _merge_tail_as_other(
|
def _merge_tail_as_other(
|
||||||
labels: list[str], values: list[float], *, max_slices: int
|
labels: list[str], values: list[float], *, max_slices: int
|
||||||
) -> tuple[list[str], list[float]]:
|
) -> tuple[list[str], list[float]]:
|
||||||
@ -305,17 +259,6 @@ def generate_report_charts(run_dir: Path, brief: dict[str, Any]) -> list[str]:
|
|||||||
"chart_shop_rows_pie.png",
|
"chart_shop_rows_pie.png",
|
||||||
)
|
)
|
||||||
|
|
||||||
sf = brief.get("sales_floor_analysis") or {}
|
|
||||||
sf_chart = sf.get("bucket_chart") if isinstance(sf, dict) else None
|
|
||||||
labs_sf, vals_sf = _label_count_pairs(sf_chart or [])
|
|
||||||
save_bar_h(
|
|
||||||
labs_sf,
|
|
||||||
vals_sf,
|
|
||||||
"销量楼层档位分布(结构行数)",
|
|
||||||
"chart_sales_floor_buckets_bar.png",
|
|
||||||
"行数",
|
|
||||||
)
|
|
||||||
|
|
||||||
def scenario_group_asset_slug(group: str, index: int) -> str:
|
def scenario_group_asset_slug(group: str, index: int) -> str:
|
||||||
"""与 ``jd_competitor_report._scenario_group_asset_slug`` 保持一致。"""
|
"""与 ``jd_competitor_report._scenario_group_asset_slug`` 保持一致。"""
|
||||||
raw = (group or "").strip()
|
raw = (group or "").strip()
|
||||||
@ -391,58 +334,6 @@ def generate_report_charts(run_dir: Path, brief: dict[str, Any]) -> list[str]:
|
|||||||
tkw = f"「{gname}」· 关注词命中次数" if gname else "细类 · 关注词命中次数"
|
tkw = f"「{gname}」· 关注词命中次数" if gname else "细类 · 关注词命中次数"
|
||||||
save_bar_h(wl, vl, tkw, f"chart_focus_keywords_bar__{slug}.png", "命中次数")
|
save_bar_h(wl, vl, tkw, f"chart_focus_keywords_bar__{slug}.png", "命中次数")
|
||||||
|
|
||||||
matrix_compact = bool(brief.get("matrix_compact_section", True))
|
|
||||||
mg = brief.get("matrix_by_group") or []
|
|
||||||
if matrix_compact and isinstance(mg, list):
|
|
||||||
for gi, block in enumerate(mg):
|
|
||||||
if not isinstance(block, dict):
|
|
||||||
continue
|
|
||||||
gname = str(block.get("group") or "").strip()
|
|
||||||
skus = block.get("skus")
|
|
||||||
if not isinstance(skus, list) or not skus:
|
|
||||||
continue
|
|
||||||
slug = _matrix_block_chart_slug(gname, gi)
|
|
||||||
triples: list[tuple[str, float, float]] = []
|
|
||||||
for s in skus:
|
|
||||||
if not isinstance(s, dict):
|
|
||||||
continue
|
|
||||||
title = (s.get("title") or "").strip()[:30] or str(
|
|
||||||
s.get("sku_id") or ""
|
|
||||||
).strip()[:14] or "—"
|
|
||||||
p = _parse_price_from_text(str(s.get("detail_price_final") or ""))
|
|
||||||
if p is None:
|
|
||||||
p = _parse_price_from_text(
|
|
||||||
str(s.get("coupon_or_detail_price") or "")
|
|
||||||
)
|
|
||||||
if p is None:
|
|
||||||
p = _parse_price_from_text(str(s.get("list_price_show") or ""))
|
|
||||||
v = _parse_comment_fuzzy_sortable(str(s.get("comment_fuzzy") or ""))
|
|
||||||
triples.append((title, p or 0.0, v or 0.0))
|
|
||||||
if not triples:
|
|
||||||
continue
|
|
||||||
triples.sort(key=lambda x: x[1], reverse=True)
|
|
||||||
triples = triples[:36]
|
|
||||||
labs = [t[0] for t in triples]
|
|
||||||
prs = [t[1] for t in triples]
|
|
||||||
cms = [t[2] for t in triples]
|
|
||||||
tbase = f"「{gname}」" if gname else "细类"
|
|
||||||
if max(prs) > 0:
|
|
||||||
save_bar_h(
|
|
||||||
labs,
|
|
||||||
prs,
|
|
||||||
f"{tbase}· 详情/券后价(元)",
|
|
||||||
f"chart_matrix_price__{slug}.png",
|
|
||||||
"元",
|
|
||||||
)
|
|
||||||
if max(cms) > 0:
|
|
||||||
save_bar_h(
|
|
||||||
labs,
|
|
||||||
cms,
|
|
||||||
f"{tbase}· 评价量展示(粗算排序,非精确条数)",
|
|
||||||
f"chart_matrix_comments__{slug}.png",
|
|
||||||
"粗算值",
|
|
||||||
)
|
|
||||||
|
|
||||||
sent = brief.get("comment_sentiment_lexicon") or {}
|
sent = brief.get("comment_sentiment_lexicon") or {}
|
||||||
if isinstance(sent, dict):
|
if isinstance(sent, dict):
|
||||||
pie_labs = ["偏正向", "偏负向", "正负混合", "中性/空"]
|
pie_labs = ["偏正向", "偏负向", "正负混合", "中性/空"]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user