yeref 0.24.59__py3-none-any.whl → 0.24.60__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
@@ -16122,7 +16122,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16122
16122
  await asyncio.sleep(round(random.uniform(0, 1), 2))
16123
16123
 
16124
16124
 
16125
- async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16125
+ async def return_unit_metrics2(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16126
16126
  try:
16127
16127
  # собрать всех пользователей
16128
16128
  sql = 'SELECT USER_TID, USER_VARS, USER_LSTS FROM "USER"'
@@ -16306,6 +16306,182 @@ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16306
16306
  except Exception as e:
16307
16307
  logger.info(log_ % str(e))
16308
16308
  await asyncio.sleep(round(random.uniform(0, 1), 2))
16309
+
16310
+
16311
+ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16312
+ try:
16313
+ sql = 'SELECT USER_TID, USER_VARS, USER_LSTS FROM "USER"'
16314
+ data_users = await db_select_pg(sql, (), BASE_P)
16315
+
16316
+ metrics = defaultdict(lambda: {
16317
+ "new_users": 0,
16318
+ "sum_amount": 0.0,
16319
+ "payments_count": 0,
16320
+ "churn_count": 0
16321
+ })
16322
+ seen_new = set()
16323
+
16324
+ months = ["2025-06", "2025-07", "2025-08", "2025-09"]
16325
+ data_users = []
16326
+ for _ in range(20):
16327
+ # дата входа
16328
+ entry_month = random.choice(months)
16329
+ entry_day = random.randint(1, 28)
16330
+ entry_date = f"{entry_month}-{entry_day:02}"
16331
+ entry_dt_obj = datetime.strptime(entry_date, '%Y-%m-%d')
16332
+ entry_dt = f"{entry_dt_obj.strftime('%d-%m-%Y')}_{datetime.now().strftime('%H-%M-%S')}"
16333
+ utm = random.choice(["/start", "/startapp"])
16334
+
16335
+ # месяцы от входа и дальше
16336
+ valid_months = [m for m in months if datetime.strptime(m + "-01", "%Y-%m-%d") >= entry_dt_obj.replace(day=1)]
16337
+ if not valid_months:
16338
+ valid_months = [entry_month]
16339
+
16340
+ user_mau = sorted(random.sample(valid_months, k=random.randint(1, len(valid_months))))
16341
+ user_dau_dates = set()
16342
+ txs, payments = [], []
16343
+
16344
+ # платеж
16345
+ if user_mau:
16346
+ pay_month = random.choice(user_mau)
16347
+ pay_day = random.randint(1, 28)
16348
+ pay_date = f"{pay_month}-{pay_day:02}"
16349
+ dt_pay = datetime.strptime(pay_date, "%Y-%m-%d")
16350
+ # if dt_pay >= entry_dt_obj:
16351
+ payments = [{
16352
+ "TYPE": random.choice(["don", "sub", "pst"]),
16353
+ "DT_START": f"{dt_pay.strftime('%d-%m-%Y')}_14-00-00",
16354
+ "DT_END": "0",
16355
+ "AMOUNT": str(random.randint(1, 10))
16356
+ }]
16357
+ user_dau_dates.add(pay_date)
16358
+
16359
+ # вход в приложение
16360
+ user_dau_dates.add(entry_date)
16361
+ for m in user_mau:
16362
+ day = random.randint(1, 28)
16363
+ visit = f"{m}-{day:02}"
16364
+ dt_visit = datetime.strptime(visit, "%Y-%m-%d")
16365
+ if dt_visit >= entry_dt_obj and random.random() < 0.7:
16366
+ user_dau_dates.add(visit)
16367
+
16368
+ # статусы (отток) с низкой вероятностью
16369
+ USER_STATUSES = []
16370
+ if random.random() < 0.2: # 10% шанс оттока
16371
+ churn_month = random.choice(valid_months)
16372
+ churn_day = random.randint(1, 28)
16373
+ churn_date = f"{churn_month}-{churn_day:02}"
16374
+ churn_ts = datetime.strptime(churn_date, "%Y-%m-%d").strftime("%d-%m-%Y") + "_23-59-59"
16375
+ USER_STATUSES = [{random.choice(["left", "kicked"]): churn_ts}]
16376
+
16377
+ user_dau = sorted(user_dau_dates)
16378
+ wallet = f"wallet{random.randint(1, 100)}" if txs else random.choice([f"wallet{random.randint(1, 100)}", ""])
16379
+
16380
+ data_users.append((
16381
+ random.randint(100000, 999999),
16382
+ json.dumps({"USER_WALLET": wallet, "USER_UTM": utm, "USER_DT": entry_dt}),
16383
+ json.dumps({"USER_DAU": user_dau, "USER_MAU": user_mau, "USER_TXS": txs,
16384
+ "USER_PAYMENTS": payments, "USER_STATUSES": USER_STATUSES})
16385
+ ))
16386
+ print(f"gen {data_users=}")
16387
+
16388
+ for USER_TID, USER_VARS, USER_LSTS in data_users:
16389
+ USER_VARS = json.loads(USER_VARS or "{}")
16390
+ USER_LSTS = json.loads(USER_LSTS or "{}")
16391
+ USER_DT = USER_VARS.get("USER_DT", "")
16392
+ USER_PAYMENTS = USER_LSTS.get("USER_PAYMENTS", [])
16393
+ USER_STATUSES = USER_LSTS.get("USER_STATUSES", [])
16394
+
16395
+ if USER_DT:
16396
+ mo = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S").strftime("%Y-%m")
16397
+ if (USER_TID, mo) not in seen_new:
16398
+ seen_new.add((USER_TID, mo))
16399
+ metrics[mo]["new_users"] += 1
16400
+
16401
+ for pay in USER_PAYMENTS:
16402
+ dt_p = datetime.strptime(pay.get("DT_START", ""), "%d-%m-%Y_%H-%M-%S")
16403
+ mo_p = dt_p.strftime("%Y-%m")
16404
+ amt = float(pay.get("AMOUNT", 0)) * 0.013
16405
+ metrics[mo_p]["sum_amount"] += amt
16406
+ metrics[mo_p]["payments_count"] += 1
16407
+
16408
+ for status in USER_STATUSES:
16409
+ key, ts = next(iter(status.items()))
16410
+ if key in ("left", "kicked"):
16411
+ mo_s = datetime.strptime(ts, "%d-%m-%Y_%H-%M-%S").strftime("%Y-%m")
16412
+ metrics[mo_s]["churn_count"] += 1
16413
+ break
16414
+
16415
+ all_months = sorted(metrics.keys())
16416
+ cumulative_users = 0
16417
+ results = []
16418
+
16419
+ for idx, mo in enumerate(all_months):
16420
+ data = metrics[mo]
16421
+ new_u = data["new_users"]
16422
+ cumulative_users += new_u
16423
+
16424
+ MRR = data["sum_amount"]
16425
+ def fmt(x):
16426
+ return f"{x:.2f}".rstrip("0").rstrip(".") if x is not None else ""
16427
+
16428
+ mrr_fmt = fmt(MRR)
16429
+ N = cumulative_users
16430
+ ARPU = MRR / N if N else None
16431
+ ARR = MRR * 12 if N else None
16432
+
16433
+ churn = data["churn_count"]
16434
+ ChurnR = churn / N if N else None
16435
+ LTV1 = (ARPU / ChurnR) if (ARPU is not None and ChurnR and ChurnR > 0) else None
16436
+
16437
+ pay_cnt = data["payments_count"]
16438
+ if N and pay_cnt:
16439
+ LTV2 = (pay_cnt / N) * (MRR / pay_cnt)
16440
+ else:
16441
+ LTV2 = None
16442
+
16443
+ if idx == 0:
16444
+ CMGR = None
16445
+ first_mrr = MRR
16446
+ else:
16447
+ CMGR = ((MRR / first_mrr) ** (1 / idx)) - 1 if first_mrr and MRR is not None else None
16448
+
16449
+ results.append({
16450
+ "MO": mo,
16451
+ "N": str(N),
16452
+ "MRR": mrr_fmt,
16453
+ "ARPU": fmt(ARPU),
16454
+ "ARR": fmt(ARR),
16455
+ "ChurnR": fmt(ChurnR),
16456
+ "LTV1": fmt(LTV1),
16457
+ "LTV2": fmt(LTV2),
16458
+ "CMGR": fmt(CMGR),
16459
+ "CAC": ""
16460
+ })
16461
+
16462
+ path = os.path.join(EXTRA_D, "2_unit_metrics.csv")
16463
+ with open(path, "w", newline="", encoding="utf-8") as f:
16464
+ writer = csv.writer(f)
16465
+ writer.writerow(["MO", "N", "MRR", "ARPU", "ARR", "ChurnR", "LTV1", "LTV2", "CMGR", "CAC"])
16466
+ for row in results:
16467
+ writer.writerow([
16468
+ row["MO"], row["N"], row["MRR"], row["ARPU"], row["ARR"],
16469
+ row["ChurnR"], row["LTV1"], row["LTV2"], row["CMGR"], row["CAC"]
16470
+ ])
16471
+
16472
+ cmgr_vals = [float(r["CMGR"]) for r in results if r["CMGR"] != ""]
16473
+ if cmgr_vals:
16474
+ factors = [1 + v for v in cmgr_vals]
16475
+ avg = math.prod(factors) ** (1 / len(factors))
16476
+ writer.writerow([])
16477
+ writer.writerow([f"Rev ~ ×{round(avg, 2)} monthly"])
16478
+
16479
+ thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
16480
+ await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
16481
+ except Exception as e:
16482
+ logger.info(log_ % str(e))
16483
+ await asyncio.sleep(round(random.uniform(0, 1), 2))
16484
+
16309
16485
  # endregion
16310
16486
 
16311
16487
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yeref
3
- Version: 0.24.59
3
+ Version: 0.24.60
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=xp4p1Kz_G-4O-ExGlelMry9JppEm7N2Ky6CvwPGII_E,1051287
5
+ yeref-0.24.60.dist-info/METADATA,sha256=fPl-asX2-FJ_aqVXMb81Gck0z1XdclNMbKMq-ObmRO0,119
6
+ yeref-0.24.60.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ yeref-0.24.60.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
8
+ yeref-0.24.60.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=agNJ36MiGH-INGAhoaeSPgym6QO9nlJmrKf5ZObMZr0,1043932
5
- yeref-0.24.59.dist-info/METADATA,sha256=klrdD5dBXpfBzMifbDoE3Vm79Z6FRhgZPOJyTSFHNQY,119
6
- yeref-0.24.59.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- yeref-0.24.59.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
8
- yeref-0.24.59.dist-info/RECORD,,