wen Qiu commited on
Commit
48fbfa9
·
1 Parent(s): ec533d1

fix: prevent duplicate data when Export table and platform sheets overlap

Browse files

Root cause: _load_from_google_sheets was reading both the Export sheet
(which contains all platforms) AND individual platform sheets (Meta,
Google, Criteo, Bing written by API sync), then concatenating them.
This caused campaigns present in both sources to be double-counted,
inflating Cost/Revenue figures (e.g. US Conversion showing $407K
instead of the correct $307K).

Fix: When a platform has its own API-synced sheet, exclude that
platform's rows from the Export sheet before merging. The API sheet
data takes priority as it's more current and accurately formatted.

Made-with: Cursor

Files changed (1) hide show
  1. src/loader.py +32 -10
src/loader.py CHANGED
@@ -104,7 +104,6 @@ def _load_from_google_sheets(config: dict[str, Any]) -> pd.DataFrame | None:
104
  gc = gspread.authorize(creds)
105
  sh = gc.open_by_url(url)
106
 
107
- all_dfs = []
108
  platform_sheets = gs_cfg.get("platform_sheets", ["Meta", "Google", "Criteo", "Bing"])
109
  main_sheet = gs_cfg.get("worksheet", "Export")
110
 
@@ -117,14 +116,14 @@ def _load_from_google_sheets(config: dict[str, Any]) -> pd.DataFrame | None:
117
  col_cfg.get("date_idx", 4): "date",
118
  }
119
 
120
- for sheet_name in [main_sheet] + platform_sheets:
121
  try:
122
- ws = sh.worksheet(sheet_name)
123
  records = ws.get_all_values()
124
  if len(records) < 2:
125
- continue
126
  header = records[0]
127
- seen = {}
128
  unique_header = []
129
  for col in header:
130
  if col in seen:
@@ -139,17 +138,40 @@ def _load_from_google_sheets(config: dict[str, Any]) -> pd.DataFrame | None:
139
  if idx < len(sdf.columns):
140
  rename[sdf.columns[idx]] = name
141
  sdf = sdf.rename(columns=rename)
142
- log.info(f"从 Sheet [{sheet_name}] 读取 {len(sdf)} 行")
143
- all_dfs.append(sdf)
144
  except Exception:
145
- pass
146
 
147
- if not all_dfs:
 
 
 
 
 
 
 
 
148
  log.warning("Google Sheets 所有工作表均无数据")
149
  return None
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  df = pd.concat(all_dfs, ignore_index=True)
152
- log.info(f"从 Google Sheets 共读取 {len(df)} 行数据")
153
  return df
154
 
155
  except Exception as e:
 
104
  gc = gspread.authorize(creds)
105
  sh = gc.open_by_url(url)
106
 
 
107
  platform_sheets = gs_cfg.get("platform_sheets", ["Meta", "Google", "Criteo", "Bing"])
108
  main_sheet = gs_cfg.get("worksheet", "Export")
109
 
 
116
  col_cfg.get("date_idx", 4): "date",
117
  }
118
 
119
+ def _read_one_sheet(ws_name: str) -> pd.DataFrame | None:
120
  try:
121
+ ws = sh.worksheet(ws_name)
122
  records = ws.get_all_values()
123
  if len(records) < 2:
124
+ return None
125
  header = records[0]
126
+ seen: dict[str, int] = {}
127
  unique_header = []
128
  for col in header:
129
  if col in seen:
 
138
  if idx < len(sdf.columns):
139
  rename[sdf.columns[idx]] = name
140
  sdf = sdf.rename(columns=rename)
141
+ log.info(f"从 Sheet [{ws_name}] 读取 {len(sdf)} 行")
142
+ return sdf
143
  except Exception:
144
+ return None
145
 
146
+ main_df = _read_one_sheet(main_sheet)
147
+
148
+ api_platform_dfs: dict[str, pd.DataFrame] = {}
149
+ for ps in platform_sheets:
150
+ ps_df = _read_one_sheet(ps)
151
+ if ps_df is not None and not ps_df.empty:
152
+ api_platform_dfs[ps] = ps_df
153
+
154
+ if main_df is None and not api_platform_dfs:
155
  log.warning("Google Sheets 所有工作表均无数据")
156
  return None
157
 
158
+ if main_df is not None and api_platform_dfs:
159
+ platforms_with_api_data = set(api_platform_dfs.keys())
160
+ if "platform" in main_df.columns:
161
+ main_keep = main_df[~main_df["platform"].astype(str).str.strip().isin(platforms_with_api_data)]
162
+ main_drop_count = len(main_df) - len(main_keep)
163
+ if main_drop_count:
164
+ log.info(f"Export 表中排除 {main_drop_count} 行已有分平台 API 数据的平台({', '.join(platforms_with_api_data)}),避免重复")
165
+ all_dfs = [main_keep] + list(api_platform_dfs.values())
166
+ else:
167
+ all_dfs = [main_df] + list(api_platform_dfs.values())
168
+ elif main_df is not None:
169
+ all_dfs = [main_df]
170
+ else:
171
+ all_dfs = list(api_platform_dfs.values())
172
+
173
  df = pd.concat(all_dfs, ignore_index=True)
174
+ log.info(f"从 Google Sheets 共读取 {len(df)} 行数据(去重后)")
175
  return df
176
 
177
  except Exception as e: