feat: show Adpingid in order coverage
This commit is contained in:
+20
-2
@@ -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"],
|
||||||
|
|||||||
+16
-7
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user