feat: show Adpingid in order coverage

This commit is contained in:
王源
2026-10-14 17:18:21 +08:00
parent 90e485e478
commit 62373fde87
3 changed files with 3845 additions and 10 deletions
+20 -2
View File
@@ -17,7 +17,7 @@ DEFAULT_SOURCE = Path(
"/Users/wangyuan/Documents/SHAREit/运营/TaurusX搬单/武汉自投投放下单_AM&Media Buy.xlsx" "/Users/wangyuan/Documents/SHAREit/运营/TaurusX搬单/武汉自投投放下单_AM&Media Buy.xlsx"
) )
TARGET_PLATFORM = "TaurusxDSP" TARGET_PLATFORM = "TaurusxDSP"
REQUIRED_COLUMNS = {"状态", "PromotionAppName", "pkg", "投放平台", "投放国家"} REQUIRED_COLUMNS = {"状态", "Adpingid", "PromotionAppName", "pkg", "投放平台", "投放国家"}
PLATFORM_ALIASES = { PLATFORM_ALIASES = {
"mtg": "MTG", "mtg": "MTG",
@@ -74,6 +74,13 @@ def normalize_status(value: Any) -> str:
return "其他/不可投" return "其他/不可投"
def sort_adpingids(values: set[str]) -> list[str]:
def sort_key(value: str) -> tuple[int, int | str]:
return (0, int(value)) if value.isdigit() else (1, value.casefold())
return sorted(values, key=sort_key)
def parse_workbook(source: Path) -> tuple[list[dict[str, str]], dict[str, int]]: def parse_workbook(source: Path) -> tuple[list[dict[str, str]], dict[str, int]]:
if not source.is_file(): if not source.is_file():
raise FileNotFoundError(f"Workbook not found: {source}") raise FileNotFoundError(f"Workbook not found: {source}")
@@ -103,6 +110,7 @@ def parse_workbook(source: Path) -> tuple[list[dict[str, str]], dict[str, int]]:
counters["excluded_examples"] += 1 counters["excluded_examples"] += 1
continue continue
adpingid = clean_text(row[indexes["Adpingid"]])
product = clean_text(row[indexes["PromotionAppName"]]) product = clean_text(row[indexes["PromotionAppName"]])
pkg = clean_text(row[indexes["pkg"]]) pkg = clean_text(row[indexes["pkg"]])
country = clean_text(row[indexes["投放国家"]]).upper() country = clean_text(row[indexes["投放国家"]]).upper()
@@ -116,6 +124,7 @@ def parse_workbook(source: Path) -> tuple[list[dict[str, str]], dict[str, int]]:
records.append( records.append(
{ {
"adpingid": adpingid,
"product": product or "未命名产品", "product": product or "未命名产品",
"pkg": pkg, "pkg": pkg,
"country": country, "country": country,
@@ -139,13 +148,20 @@ def aggregate(records: list[dict[str, str]], counters: dict[str, int], source: P
"pkg": row["pkg"], "pkg": row["pkg"],
"country": row["country"], "country": row["country"],
"products": set(), "products": set(),
"platforms": defaultdict(lambda: {"statuses": Counter(), "order_count": 0}), "adpingids": set(),
"platforms": defaultdict(
lambda: {"statuses": Counter(), "adpingids": set(), "order_count": 0}
),
"order_count": 0, "order_count": 0,
}, },
) )
group["products"].add(row["product"]) group["products"].add(row["product"])
if row["adpingid"]:
group["adpingids"].add(row["adpingid"])
detail = group["platforms"][row["platform"]] detail = group["platforms"][row["platform"]]
detail["statuses"][row["status"]] += 1 detail["statuses"][row["status"]] += 1
if row["adpingid"]:
detail["adpingids"].add(row["adpingid"])
detail["order_count"] += 1 detail["order_count"] += 1
group["order_count"] += 1 group["order_count"] += 1
@@ -160,12 +176,14 @@ def aggregate(records: list[dict[str, str]], counters: dict[str, int], source: P
platform_details.append( platform_details.append(
{ {
"platform": name, "platform": name,
"adpingids": sort_adpingids(detail["adpingids"]),
"statuses": dict(sorted(detail["statuses"].items())), "statuses": dict(sorted(detail["statuses"].items())),
"order_count": detail["order_count"], "order_count": detail["order_count"],
} }
) )
output_records.append( output_records.append(
{ {
"adpingids": sort_adpingids(group["adpingids"]),
"product": " / ".join(sorted(group["products"])), "product": " / ".join(sorted(group["products"])),
"pkg": group["pkg"], "pkg": group["pkg"],
"country": group["country"], "country": group["country"],
+3809 -1
View File
File diff suppressed because it is too large Load Diff
+16 -7
View File
@@ -332,13 +332,16 @@
} }
function applyFilters() { function applyFilters() {
const query = document.getElementById('search').value.trim().toLocaleLowerCase(); const search = document.getElementById('search');
search.placeholder = '搜索 Adpingid、Pkg 或产品名';
const query = search.value.trim().toLocaleLowerCase();
const country = document.getElementById('countryFilter').value; const country = document.getElementById('countryFilter').value;
const platform = document.getElementById('platformFilter').value; const platform = document.getElementById('platformFilter').value;
const status = document.getElementById('statusFilter').value; const status = document.getElementById('statusFilter').value;
state.filtered = state.data.records.filter(row => { state.filtered = state.data.records.filter(row => {
const recordStatus = statusOf(row).key; const recordStatus = statusOf(row).key;
return (!query || `${row.pkg} ${row.product}`.toLocaleLowerCase().includes(query)) const searchable = `${row.adpingids.join(' ')} ${row.pkg} ${row.product}`.toLocaleLowerCase();
return (!query || searchable.includes(query))
&& (country === 'all' || row.country === country) && (country === 'all' || row.country === country)
&& (platform === 'all' || row.platforms.includes(platform)) && (platform === 'all' || row.platforms.includes(platform))
&& (status === 'all' || recordStatus === status); && (status === 'all' || recordStatus === status);
@@ -364,17 +367,18 @@
const table = el('table'); const table = el('table');
const thead = el('thead'); const thead = el('thead');
const header = el('tr'); const header = el('tr');
['搬单状态', '产品', 'Pkg', '国家', '已出现平台', '源订单行数'].forEach(label => header.append(el('th', '', label))); ['搬单状态', 'Adpingid', '产品', 'Pkg', '国家', '已出现平台', '源订单行数'].forEach(label => header.append(el('th', '', label)));
thead.append(header); table.append(thead); thead.append(header); table.append(thead);
const tbody = el('tbody'); const tbody = el('tbody');
if (!rows.length) { if (!rows.length) {
const cell = el('td', 'empty', '没有符合当前条件的记录'); cell.colSpan = 6; const cell = el('td', 'empty', '没有符合当前条件的记录'); cell.colSpan = 7;
const row = el('tr'); row.append(cell); tbody.append(row); const row = el('tr'); row.append(cell); tbody.append(row);
} }
rows.forEach(record => { rows.forEach(record => {
const row = el('tr'); const row = el('tr');
const status = statusOf(record); const status = statusOf(record);
const badgeCell = el('td'); badgeCell.append(el('span', `badge ${status.key}`, status.label)); const badgeCell = el('td'); badgeCell.append(el('span', `badge ${status.key}`, status.label));
const adpingids = el('td', 'pkg', record.adpingids.join(' / ') || '-');
const product = el('td', 'product', record.product); const product = el('td', 'product', record.product);
const pkg = el('td', 'pkg', record.pkg); const pkg = el('td', 'pkg', record.pkg);
const country = el('td'); country.append(el('span', 'country-pill', record.country)); const country = el('td'); country.append(el('span', 'country-pill', record.country));
@@ -382,7 +386,7 @@
const pills = el('div', 'platforms'); const pills = el('div', 'platforms');
record.platforms.forEach(name => pills.append(el('span', `platform-pill ${name === state.data.meta.target_platform ? 'target' : ''}`.trim(), name))); record.platforms.forEach(name => pills.append(el('span', `platform-pill ${name === state.data.meta.target_platform ? 'target' : ''}`.trim(), name)));
platforms.append(pills); platforms.append(pills);
row.append(badgeCell, product, pkg, country, platforms, el('td', '', nf.format(record.order_count))); row.append(badgeCell, adpingids, product, pkg, country, platforms, el('td', '', nf.format(record.order_count)));
tbody.append(row); tbody.append(row);
}); });
table.append(tbody); wrap.append(table); table.append(tbody); wrap.append(table);
@@ -405,7 +409,11 @@
rows.forEach(record => { rows.forEach(record => {
const row = el('tr'); const row = el('tr');
const key = el('td'); const key = el('td');
key.append(el('div', 'pkg', record.pkg), el('div', 'panel-desc', record.country)); key.append(
el('div', 'pkg', record.pkg),
el('div', 'panel-desc', record.country),
el('div', 'panel-desc', `Adpingid:${record.adpingids.join(' / ') || '-'}`)
);
row.append(key); row.append(key);
platforms.forEach(name => { platforms.forEach(name => {
const cell = el('td'); const cell = el('td');
@@ -430,9 +438,10 @@
} }
function exportCsv() { function exportCsv() {
const headers = ['搬单状态', '产品', 'Pkg', '投放国家', '已出现平台', '是否有TaurusxDSP', '源订单行数']; const headers = ['搬单状态', 'Adpingid', '产品', 'Pkg', '投放国家', '已出现平台', '是否有TaurusxDSP', '源订单行数'];
const csvRows = [headers, ...state.filtered.map(record => [ const csvRows = [headers, ...state.filtered.map(record => [
statusOf(record).label, statusOf(record).label,
record.adpingids.join(' / '),
record.product, record.product,
record.pkg, record.pkg,
record.country, record.country,