170 lines
6.2 KiB
Bash
170 lines
6.2 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
set -eu
|
||
|
|
export BK="/root/aza-app/backups/woocommerce_period_sync_env_deploy_20260518_221614"
|
||
|
|
|
||
|
|
python3 << 'PY'
|
||
|
|
import json, os, sqlite3, statistics
|
||
|
|
|
||
|
|
BK = os.environ["BK"]
|
||
|
|
|
||
|
|
def stats(path, label):
|
||
|
|
con = sqlite3.connect(path)
|
||
|
|
cur = con.cursor()
|
||
|
|
|
||
|
|
def one(q):
|
||
|
|
return cur.execute(q).fetchone()[0]
|
||
|
|
|
||
|
|
active = one("SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active'")
|
||
|
|
wc = one(
|
||
|
|
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND subscription_id LIKE 'wc_sub_%'"
|
||
|
|
)
|
||
|
|
st = one(
|
||
|
|
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND current_period_start IS NOT NULL"
|
||
|
|
)
|
||
|
|
en = one(
|
||
|
|
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND current_period_end IS NOT NULL"
|
||
|
|
)
|
||
|
|
both = one(
|
||
|
|
"""SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active'
|
||
|
|
AND current_period_start IS NOT NULL AND current_period_end IS NOT NULL
|
||
|
|
AND current_period_start < current_period_end"""
|
||
|
|
)
|
||
|
|
rows = cur.execute(
|
||
|
|
"""SELECT current_period_end - current_period_start FROM licenses
|
||
|
|
WHERE lower(trim(status))='active' AND current_period_start IS NOT NULL
|
||
|
|
AND current_period_end IS NOT NULL AND current_period_start < current_period_end"""
|
||
|
|
).fetchall()
|
||
|
|
lengths = [int(r[0]) for r in rows if r[0] is not None]
|
||
|
|
med = int(statistics.median(lengths)) if lengths else None
|
||
|
|
mn = min(lengths) if lengths else None
|
||
|
|
mx = max(lengths) if lengths else None
|
||
|
|
con.close()
|
||
|
|
print(label, json.dumps({
|
||
|
|
"active_total": active,
|
||
|
|
"active_wc_sub": wc,
|
||
|
|
"active_with_start": st,
|
||
|
|
"active_with_end": en,
|
||
|
|
"active_start_before_end": both,
|
||
|
|
"period_len_sec_min": mn,
|
||
|
|
"period_len_sec_median": med,
|
||
|
|
"period_len_sec_max": mx,
|
||
|
|
}))
|
||
|
|
|
||
|
|
|
||
|
|
stats(os.path.join(BK, "stripe_webhook.sqlite"), "BEFORE_BACKUP_DB")
|
||
|
|
stats("/root/aza-app/data/stripe_webhook.sqlite", "AFTER_DEPLOY_PRE_SYNC")
|
||
|
|
PY
|
||
|
|
|
||
|
|
docker exec aza-api sh -c 'curl -sS -X POST http://127.0.0.1:8000/admin/woocommerce_sync_periods -H "X-Admin-Token: $AZA_ADMIN_TOKEN"' > /tmp/woo_sync_out.json
|
||
|
|
|
||
|
|
python3 << 'PY'
|
||
|
|
import json
|
||
|
|
with open("/tmp/woo_sync_out.json", "r", encoding="utf-8") as f:
|
||
|
|
d = json.load(f)
|
||
|
|
print("SYNC_OK", d.get("ok"))
|
||
|
|
for k in (
|
||
|
|
"total_active_wc_sub_rows",
|
||
|
|
"updated",
|
||
|
|
"skipped",
|
||
|
|
"failed_count",
|
||
|
|
):
|
||
|
|
print(k, d.get(k))
|
||
|
|
errs = d.get("errors") or []
|
||
|
|
print("errors_truncated_count", len(errs))
|
||
|
|
if errs:
|
||
|
|
safe = []
|
||
|
|
for e in errs[:8]:
|
||
|
|
if isinstance(e, dict):
|
||
|
|
safe.append(
|
||
|
|
{
|
||
|
|
"subscription_id_prefix": e.get("subscription_id_prefix"),
|
||
|
|
"detail": e.get("detail"),
|
||
|
|
}
|
||
|
|
)
|
||
|
|
print("errors_sample", json.dumps(safe))
|
||
|
|
PY
|
||
|
|
|
||
|
|
python3 << 'PY'
|
||
|
|
import json, sqlite3, statistics
|
||
|
|
|
||
|
|
|
||
|
|
def stats(path, label):
|
||
|
|
con = sqlite3.connect(path)
|
||
|
|
cur = con.cursor()
|
||
|
|
|
||
|
|
def one(q):
|
||
|
|
return cur.execute(q).fetchone()[0]
|
||
|
|
|
||
|
|
active = one("SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active'")
|
||
|
|
wc = one(
|
||
|
|
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND subscription_id LIKE 'wc_sub_%'"
|
||
|
|
)
|
||
|
|
st = one(
|
||
|
|
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND current_period_start IS NOT NULL"
|
||
|
|
)
|
||
|
|
en = one(
|
||
|
|
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND current_period_end IS NOT NULL"
|
||
|
|
)
|
||
|
|
both = one(
|
||
|
|
"""SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active'
|
||
|
|
AND current_period_start IS NOT NULL AND current_period_end IS NOT NULL
|
||
|
|
AND current_period_start < current_period_end"""
|
||
|
|
)
|
||
|
|
rows = cur.execute(
|
||
|
|
"""SELECT current_period_end - current_period_start FROM licenses
|
||
|
|
WHERE lower(trim(status))='active' AND current_period_start IS NOT NULL
|
||
|
|
AND current_period_end IS NOT NULL AND current_period_start < current_period_end"""
|
||
|
|
).fetchall()
|
||
|
|
lengths = [int(r[0]) for r in rows if r[0] is not None]
|
||
|
|
med = int(statistics.median(lengths)) if lengths else None
|
||
|
|
mn = min(lengths) if lengths else None
|
||
|
|
mx = max(lengths) if lengths else None
|
||
|
|
con.close()
|
||
|
|
print(label, json.dumps({
|
||
|
|
"active_total": active,
|
||
|
|
"active_wc_sub": wc,
|
||
|
|
"active_with_start": st,
|
||
|
|
"active_with_end": en,
|
||
|
|
"active_start_before_end": both,
|
||
|
|
"period_len_sec_min": mn,
|
||
|
|
"period_len_sec_median": med,
|
||
|
|
"period_len_sec_max": mx,
|
||
|
|
}))
|
||
|
|
|
||
|
|
|
||
|
|
stats("/root/aza-app/data/stripe_webhook.sqlite", "AFTER_SYNC")
|
||
|
|
PY
|
||
|
|
|
||
|
|
rm -f /tmp/woo_sync_out.json
|
||
|
|
|
||
|
|
OVERVIEW_CODE=$(docker exec aza-api sh -c 'curl -sS -o /tmp/bo.json -w "%{http_code}" http://127.0.0.1:8000/admin/ai_budget_overview -H "X-Admin-Token: $AZA_ADMIN_TOKEN"')
|
||
|
|
echo "AI_BUDGET_OVERVIEW_HTTP" "$OVERVIEW_CODE"
|
||
|
|
docker cp aza-api:/tmp/bo.json /tmp/bo.json
|
||
|
|
python3 << 'PY'
|
||
|
|
import json
|
||
|
|
try:
|
||
|
|
with open("/tmp/bo.json", "r", encoding="utf-8") as f:
|
||
|
|
d = json.load(f)
|
||
|
|
except Exception as e:
|
||
|
|
print("AI_BUDGET_OVERVIEW_PARSE_ERROR", type(e).__name__)
|
||
|
|
raise SystemExit(0)
|
||
|
|
# summarize structure only
|
||
|
|
lic = d.get("licenses")
|
||
|
|
if isinstance(lic, list) and lic:
|
||
|
|
row = lic[0]
|
||
|
|
keys = sorted(row.keys())
|
||
|
|
need = {"budget_usd", "used_usd", "remaining_usd", "available_percent"}
|
||
|
|
print("overview_first_row_keys_has_budget_cols", {k: (k in row) for k in sorted(need)})
|
||
|
|
pe = row.get("current_period_end")
|
||
|
|
ps = row.get("current_period_start")
|
||
|
|
print("overview_first_row_period_start_present", ps is not None)
|
||
|
|
print("overview_first_row_period_end_present", pe is not None)
|
||
|
|
print("overview_license_row_count", len(lic) if isinstance(lic, list) else None)
|
||
|
|
PY
|
||
|
|
rm -f /tmp/bo.json
|
||
|
|
|
||
|
|
CSV_CODE=$(docker exec aza-api sh -c 'curl -sS -o /tmp/bc.csv -w "%{http_code}" "http://127.0.0.1:8000/admin/ai_budget_export.csv" -H "X-Admin-Token: $AZA_ADMIN_TOKEN"')
|
||
|
|
echo "AI_BUDGET_CSV_HTTP" "$CSV_CODE"
|
||
|
|
docker exec aza-api head -n 1 /tmp/bc.csv | python3 -c "import sys; line=sys.stdin.read().strip().split(','); need=['budget_usd','used_usd','remaining_usd','available_percent']; print('csv_header_has', {n:(n in line) for n in need}); print('csv_header_col_count', len(line))"
|
||
|
|
docker exec aza-api rm -f /tmp/bc.json /tmp/bo.json /tmp/bc.csv 2>/dev/null || true
|