yeref 0.24.69__py3-none-any.whl → 0.24.71__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
yeref/yeref.py CHANGED
@@ -16240,7 +16240,7 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16240
16240
 
16241
16241
  months = ["2025-06", "2025-07", "2025-08", "2025-09"]
16242
16242
  data_users = []
16243
- for _ in range(20):
16243
+ for _ in range(30):
16244
16244
  # дата входа
16245
16245
  entry_month = random.choice(months)
16246
16246
  entry_day = random.randint(1, 28)
@@ -16302,7 +16302,6 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16302
16302
  ))
16303
16303
  print(f"gen {data_users=}")
16304
16304
 
16305
-
16306
16305
  cohorts = defaultdict(set)
16307
16306
  activity_months = defaultdict(set)
16308
16307
 
@@ -16365,7 +16364,7 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16365
16364
  row.append(str(val))
16366
16365
  row_sum += val
16367
16366
  else:
16368
- row.append("")
16367
+ row.append("0")
16369
16368
 
16370
16369
  row.append(str(row_sum))
16371
16370
  table.append(row)
@@ -16391,7 +16390,7 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16391
16390
  for r in table:
16392
16391
  writer.writerow(r)
16393
16392
  writer.writerow([])
16394
- writer.writerow([f"Churn ~ ×{avg_churn:.1f} monthly"])
16393
+ writer.writerow([f"Churn ~ ×{avg_churn:.0f} monthly"])
16395
16394
 
16396
16395
  thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
16397
16396
  await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
@@ -16401,127 +16400,81 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16401
16400
  await asyncio.sleep(round(random.uniform(0, 1), 2))
16402
16401
 
16403
16402
 
16404
-
16405
16403
  async def return_retention_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16406
16404
  try:
16407
16405
  sql = 'SELECT USER_TID, USER_VARS, USER_LSTS FROM "USER"'
16408
16406
  data_users = await db_select_pg(sql, (), BASE_P)
16409
16407
 
16410
- months = ["2025-06", "2025-07", "2025-08", "2025-09"]
16411
- data_users = []
16412
- for _ in range(20):
16413
- # дата входа
16414
- entry_month = random.choice(months)
16415
- entry_day = random.randint(1, 28)
16416
- entry_date = f"{entry_month}-{entry_day:02}"
16417
- entry_dt_obj = datetime.strptime(entry_date, '%Y-%m-%d')
16418
- entry_dt = f"{entry_dt_obj.strftime('%d-%m-%Y')}_{datetime.now().strftime('%H-%M-%S')}"
16419
- utm = random.choice(["/start", "/startapp"])
16420
-
16421
- # месяцы от входа и дальше
16422
- valid_months = [m for m in months if datetime.strptime(m + "-01", "%Y-%m-%d") >= entry_dt_obj.replace(day=1)]
16423
- if not valid_months:
16424
- valid_months = [entry_month]
16425
-
16426
- user_mau = sorted(random.sample(valid_months, k=random.randint(1, len(valid_months))))
16427
- user_dau_dates = set()
16428
- txs, payments = [], []
16429
-
16430
- # платеж
16431
- if user_mau:
16432
- pay_month = random.choice(user_mau)
16433
- pay_day = random.randint(1, 28)
16434
- pay_date = f"{pay_month}-{pay_day:02}"
16435
- dt_pay = datetime.strptime(pay_date, "%Y-%m-%d")
16436
- # if dt_pay >= entry_dt_obj:
16437
- payments = [{
16438
- "TYPE": random.choice(["don", "sub", "pst"]),
16439
- "DT_START": f"{dt_pay.strftime('%d-%m-%Y')}_14-00-00",
16440
- "DT_END": "0",
16441
- "AMOUNT": str(random.randint(1, 10))
16442
- }]
16443
- user_dau_dates.add(pay_date)
16408
+ rev_by_cohort = defaultdict(lambda: defaultdict(float))
16409
+ cohort_users = defaultdict(set)
16444
16410
 
16445
- # вход в приложение
16446
- user_dau_dates.add(entry_date)
16447
- for m in user_mau:
16448
- day = random.randint(1, 28)
16449
- visit = f"{m}-{day:02}"
16450
- dt_visit = datetime.strptime(visit, "%Y-%m-%d")
16451
- if dt_visit >= entry_dt_obj and random.random() < 0.7:
16452
- user_dau_dates.add(visit)
16453
-
16454
- # статусы (отток) с низкой вероятностью
16455
- USER_STATUSES = []
16456
- if random.random() < 0.2: # 10% шанс оттока
16457
- churn_month = random.choice(valid_months)
16458
- churn_day = random.randint(1, 28)
16459
- churn_date = f"{churn_month}-{churn_day:02}"
16460
- churn_ts = datetime.strptime(churn_date, "%Y-%m-%d").strftime("%d-%m-%Y") + "_23-59-59"
16461
- USER_STATUSES = [{random.choice(["left", "kicked"]): churn_ts}]
16462
-
16463
- user_dau = sorted(user_dau_dates)
16464
- wallet = f"wallet{random.randint(1, 100)}" if txs else random.choice([f"wallet{random.randint(1, 100)}", ""])
16465
-
16466
- data_users.append((
16467
- random.randint(100000, 999999),
16468
- json.dumps({"USER_WALLET": wallet, "USER_UTM": utm, "USER_DT": entry_dt}),
16469
- json.dumps({"USER_DAU": user_dau, "USER_MAU": user_mau, "USER_TXS": txs,
16470
- "USER_PAYMENTS": payments, "USER_STATUSES": USER_STATUSES})
16471
- ))
16472
- print(f"gen {data_users=}")
16473
-
16474
- # собираем выручку по пользователю и месяцу
16475
- user_month_rev = defaultdict(lambda: defaultdict(float))
16476
16411
  for USER_TID, USER_VARS, USER_LSTS in data_users:
16412
+ USER_VARS = json.loads(USER_VARS or "{}")
16477
16413
  USER_LSTS = json.loads(USER_LSTS or "{}")
16414
+ dt_entry_raw = USER_VARS.get("USER_DT", "").split("_")[0]
16415
+ if not dt_entry_raw:
16416
+ continue
16417
+ cohort_mo = datetime.strptime(dt_entry_raw, "%d-%m-%Y").strftime("%Y-%m")
16418
+ cohort_users[cohort_mo].add(USER_TID)
16419
+
16478
16420
  for pay in USER_LSTS.get("USER_PAYMENTS", []):
16479
- dt_p = datetime.strptime(pay.get("DT_START", ""), "%d-%m-%Y_%H-%M-%S")
16480
- mo = dt_p.strftime("%Y-%m")
16421
+ dt_pay = datetime.strptime(pay.get("DT_START", ""), "%d-%m-%Y_%H-%M-%S")
16422
+ pay_mo = dt_pay.strftime("%Y-%m")
16423
+ y0, m0 = map(int, cohort_mo.split("-"))
16424
+ y1, m1 = map(int, pay_mo.split("-"))
16425
+ offset = (y1 - y0) * 12 + (m1 - m0)
16426
+ if offset < 0:
16427
+ continue
16481
16428
  amt = float(pay.get("AMOUNT", 0)) * 0.013
16482
- user_month_rev[USER_TID][mo] += amt
16429
+ rev_by_cohort[cohort_mo][offset] += amt
16483
16430
 
16484
- # список всех месяцев с выручкой, отсортированный
16485
- all_months = sorted({mo for revs in user_month_rev.values() for mo in revs.keys()})
16431
+ cohort_months = sorted(cohort_users.keys())
16432
+ if not cohort_months:
16433
+ return
16486
16434
 
16487
- results = []
16488
- for idx, mo in enumerate(all_months):
16489
- if idx == 0:
16490
- results.append({"MO": mo, "NRR": ""})
16491
- continue
16492
- prev_mo = all_months[idx - 1]
16493
- # общая выручка в prev_mo
16494
- prev_total = sum(user_month_rev[u][prev_mo] for u in user_month_rev if prev_mo in user_month_rev[u])
16495
- if prev_total == 0:
16496
- results.append({"MO": mo, "NRR": ""})
16497
- continue
16498
- # выручка текущего месяца от тех же пользователей
16499
- curr_existing = sum(
16500
- user_month_rev[u][mo]
16501
- for u in user_month_rev
16502
- if prev_mo in user_month_rev[u] and mo in user_month_rev[u]
16503
- )
16504
- nrr = curr_existing / prev_total
16505
- results.append({"MO": mo, "NRR": f"{nrr * 100:.2f}"})
16435
+ max_offset = 0
16436
+ for c in cohort_months:
16437
+ if rev_by_cohort[c]:
16438
+ max_offset = max(max_offset, max(rev_by_cohort[c].keys()))
16506
16439
 
16507
- # средний NRR (арифметическое по ненулевым)
16508
- vals = [float(r["NRR"]) for r in results if r["NRR"]]
16509
- avg_nrr = f"{(sum(vals) / len(vals)):.2f}" if vals else ""
16440
+ base_rev = {c: rev_by_cohort[c].get(0, 0.0) for c in cohort_months}
16510
16441
 
16511
16442
  path = os.path.join(EXTRA_D, "4_retention_metrics.csv")
16512
16443
  with open(path, "w", newline="", encoding="utf-8") as f:
16513
16444
  writer = csv.writer(f)
16514
- writer.writerow(["MO", "NRR (%)"])
16515
- for r in results:
16516
- writer.writerow([r["MO"], r["NRR"]])
16517
- writer.writerow([])
16518
- writer.writerow([f"Avg NRR (%)", avg_nrr])
16445
+ header = ["Месяц / Когорта"] + [
16446
+ f"{c} ({len(cohort_users[c])})" for c in cohort_months
16447
+ ] + [""]
16448
+ writer.writerow(header)
16449
+
16450
+ for i in range(max_offset + 1):
16451
+ row = [f"M{i+1} ({i} мес)"]
16452
+ row_sum = 0.0
16453
+ for c in cohort_months:
16454
+ rev = rev_by_cohort[c].get(i, 0.0)
16455
+ if i == 0:
16456
+ pct = 100
16457
+ cell = f"{rev:.1f} ({pct}%)"
16458
+ row.append(cell)
16459
+ row_sum += rev
16460
+ else:
16461
+ if base_rev[c] > 0 and rev > 0:
16462
+ pct = rev / base_rev[c] * 100
16463
+ cell = f"{rev:.1f} ({pct:.0f}%)"
16464
+ row.append(cell)
16465
+ row_sum += rev
16466
+ else:
16467
+ row.append("")
16468
+ row.append(f"{row_sum:.1f}")
16469
+ writer.writerow(row)
16519
16470
 
16520
16471
  thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
16521
16472
  await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
16473
+
16522
16474
  except Exception as e:
16523
16475
  logger.info(log_ % str(e))
16524
16476
  await asyncio.sleep(round(random.uniform(0, 1), 2))
16477
+
16525
16478
  # endregion
16526
16479
 
16527
16480
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yeref
3
- Version: 0.24.69
3
+ Version: 0.24.71
4
4
  Summary: desc-f
5
5
  Author: john smith
6
6
  Dynamic: author
@@ -0,0 +1,8 @@
1
+ yeref/__init__.py,sha256=Qpv3o6Xa78VdLcsSRmctGtpnYE9btpAkCekgGhgJyXM,49
2
+ yeref/l_.py,sha256=LMX_olmJwq-tgoALJCnhV_fGrL_i_43yBLkLIcEVqGo,1176743
3
+ yeref/tonweb.js,sha256=Jf6aFOQ1OIY4q7fINYz-m5LsI3seMus124M5SYYZmtE,443659
4
+ yeref/yeref.py,sha256=mrSmu7Lp9EB_o7Xh47tbZJxZamujZfBXlLtTrAEKUsU,1050482
5
+ yeref-0.24.71.dist-info/METADATA,sha256=nqpY31UqBjgapTkhgjx3PBXVkF5ApMcWmugeYGUSYVI,119
6
+ yeref-0.24.71.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ yeref-0.24.71.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
8
+ yeref-0.24.71.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- yeref/__init__.py,sha256=Qpv3o6Xa78VdLcsSRmctGtpnYE9btpAkCekgGhgJyXM,49
2
- yeref/l_.py,sha256=LMX_olmJwq-tgoALJCnhV_fGrL_i_43yBLkLIcEVqGo,1176743
3
- yeref/tonweb.js,sha256=Jf6aFOQ1OIY4q7fINYz-m5LsI3seMus124M5SYYZmtE,443659
4
- yeref/yeref.py,sha256=Bz4O6nHqtnk7yl1BEFKGa4QqwN8l0-yrVek7aMF3a2s,1053206
5
- yeref-0.24.69.dist-info/METADATA,sha256=5gcF9XblmBzDf_vsb5p-b4HE9HSZ7JW_W5XGJBO7d4A,119
6
- yeref-0.24.69.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- yeref-0.24.69.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
8
- yeref-0.24.69.dist-info/RECORD,,