market-assistant/backend/pipeline/dataset_nonempty.py
hub-gif d95189723c refactor(dataset): 类目筛选改为报告矩阵细类名(report_group)
新增 matrix_group_label 与 jd_competitor_report 同源路径解析;API 使用 report_group 字符串;摘要返回 report_group_options;搜索行按 SKU 从合并表回填细类;导出含报告细类列;前端下拉展示饼干/米等名称。

Made-with: Cursor
2026-04-16 09:58:22 +08:00

146 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""按任务扫描库内行,得到「全表至少一格非空」的列,供摘要 / 浏览 / 导出一致裁剪。"""
from __future__ import annotations
from .csv_schema import (
COMMENT_CSV_COLUMNS,
COMMENT_CSV_TO_FIELD,
DETAIL_CSV_COLUMNS,
DETAIL_CSV_TO_FIELD,
JD_SEARCH_CSV_HEADERS,
JD_SEARCH_INTERNAL_KEYS,
MERGED_FIELD_TO_CSV_HEADER,
MERGED_INTERNAL_KEYS,
)
from .models import JdJobCommentRow, JdJobDetailRow, JdJobMergedRow, JdJobSearchRow, PipelineJob
from .row_serialize import COMMENT_FIELDS_ORDER, DETAIL_FIELDS_ORDER
MATRIX_GROUP_COLUMN = {"key": "matrix_group_label", "label": "报告细类§5矩阵"}
def _is_nonempty(val) -> bool:
if val is None:
return False
return str(val).strip() != ""
def nonempty_search_keys_for_job(job: PipelineJob) -> list[str]:
qs = JdJobSearchRow.objects.filter(job=job)
present = {k: False for k in JD_SEARCH_INTERNAL_KEYS}
for row in qs.iterator(chunk_size=400):
for k in JD_SEARCH_INTERNAL_KEYS:
if not present[k] and _is_nonempty(getattr(row, k, None)):
present[k] = True
return [k for k in JD_SEARCH_INTERNAL_KEYS if present[k]]
def nonempty_detail_fields_for_job(job: PipelineJob) -> list[str]:
qs = JdJobDetailRow.objects.filter(job=job)
present = {k: False for k in DETAIL_FIELDS_ORDER}
for row in qs.iterator(chunk_size=400):
for k in DETAIL_FIELDS_ORDER:
if not present[k] and _is_nonempty(getattr(row, k, None)):
present[k] = True
return [k for k in DETAIL_FIELDS_ORDER if present[k]]
def nonempty_comment_fields_for_job(job: PipelineJob) -> list[str]:
qs = JdJobCommentRow.objects.filter(job=job)
present = {k: False for k in COMMENT_FIELDS_ORDER}
for row in qs.iterator(chunk_size=400):
for k in COMMENT_FIELDS_ORDER:
if not present[k] and _is_nonempty(getattr(row, k, None)):
present[k] = True
return [k for k in COMMENT_FIELDS_ORDER if present[k]]
def nonempty_merged_fields_for_job(job: PipelineJob) -> list[str]:
qs = JdJobMergedRow.objects.filter(job=job)
present = {k: False for k in MERGED_INTERNAL_KEYS}
for row in qs.iterator(chunk_size=400):
for k in MERGED_INTERNAL_KEYS:
if not present[k] and _is_nonempty(getattr(row, k, None)):
present[k] = True
return [k for k in MERGED_INTERNAL_KEYS if present[k]]
def search_columns_for_api(job: PipelineJob) -> list[dict[str, str]]:
cols = [
{"key": k, "label": JD_SEARCH_CSV_HEADERS[k]}
for k in nonempty_search_keys_for_job(job)
]
if JdJobSearchRow.objects.filter(job=job).exclude(matrix_group_label="").exists():
cols.append(dict(MATRIX_GROUP_COLUMN))
return cols
def _detail_field_to_csv_col(field: str) -> str:
for col, fn in DETAIL_CSV_TO_FIELD.items():
if fn == field:
return col
return field
def detail_columns_for_api(job: PipelineJob) -> list[dict[str, str]]:
cols = [
{"key": f, "label": _detail_field_to_csv_col(f)}
for f in nonempty_detail_fields_for_job(job)
]
if JdJobDetailRow.objects.filter(job=job).exclude(matrix_group_label="").exists():
cols.append(dict(MATRIX_GROUP_COLUMN))
return cols
def _comment_field_to_csv_col(field: str) -> str:
for col, fn in COMMENT_CSV_TO_FIELD.items():
if fn == field:
return col
return field
def comment_columns_for_api(job: PipelineJob) -> list[dict[str, str]]:
return [
{"key": f, "label": _comment_field_to_csv_col(f)}
for f in nonempty_comment_fields_for_job(job)
]
def merged_columns_for_api(job: PipelineJob) -> list[dict[str, str]]:
cols = [
{"key": k, "label": MERGED_FIELD_TO_CSV_HEADER[k]}
for k in nonempty_merged_fields_for_job(job)
]
if JdJobMergedRow.objects.filter(job=job).exclude(matrix_group_label="").exists():
cols.append(dict(MATRIX_GROUP_COLUMN))
return cols
def search_export_headers(job: PipelineJob) -> list[str]:
keys = nonempty_search_keys_for_job(job)
h = ["id", "row_index"] + [JD_SEARCH_CSV_HEADERS[k] for k in keys]
if JdJobSearchRow.objects.filter(job=job).exclude(matrix_group_label="").exists():
h.append(MATRIX_GROUP_COLUMN["label"])
return h
def detail_export_headers(job: PipelineJob) -> list[str]:
fields = set(nonempty_detail_fields_for_job(job))
cols = [c for c in DETAIL_CSV_COLUMNS if DETAIL_CSV_TO_FIELD[c] in fields]
h = ["id", "row_index"] + cols
if JdJobDetailRow.objects.filter(job=job).exclude(matrix_group_label="").exists():
h.append(MATRIX_GROUP_COLUMN["label"])
return h
def comment_export_headers(job: PipelineJob) -> list[str]:
fields = set(nonempty_comment_fields_for_job(job))
cols = [c for c in COMMENT_CSV_COLUMNS if COMMENT_CSV_TO_FIELD[c] in fields]
return ["id", "row_index"] + cols
def merged_export_headers(job: PipelineJob) -> list[str]:
keys = nonempty_merged_fields_for_job(job)
h = ["id", "row_index"] + [MERGED_FIELD_TO_CSV_HEADER[k] for k in keys]
if JdJobMergedRow.objects.filter(job=job).exclude(matrix_group_label="").exists():
h.append(MATRIX_GROUP_COLUMN["label"])
return h