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 +55 -102
- {yeref-0.24.69.dist-info → yeref-0.24.71.dist-info}/METADATA +1 -1
- yeref-0.24.71.dist-info/RECORD +8 -0
- yeref-0.24.69.dist-info/RECORD +0 -8
- {yeref-0.24.69.dist-info → yeref-0.24.71.dist-info}/WHEEL +0 -0
- {yeref-0.24.69.dist-info → yeref-0.24.71.dist-info}/top_level.txt +0 -0
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(
|
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:.
|
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
|
-
|
16411
|
-
|
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
|
-
|
16480
|
-
|
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
|
-
|
16429
|
+
rev_by_cohort[cohort_mo][offset] += amt
|
16483
16430
|
|
16484
|
-
|
16485
|
-
|
16431
|
+
cohort_months = sorted(cohort_users.keys())
|
16432
|
+
if not cohort_months:
|
16433
|
+
return
|
16486
16434
|
|
16487
|
-
|
16488
|
-
for
|
16489
|
-
if
|
16490
|
-
|
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
|
-
|
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
|
-
|
16515
|
-
|
16516
|
-
|
16517
|
-
writer.writerow(
|
16518
|
-
|
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
|
|
@@ -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,,
|
yeref-0.24.69.dist-info/RECORD
DELETED
@@ -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,,
|
File without changes
|
File without changes
|