yeref 0.24.58__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"'
@@ -16293,11 +16293,12 @@ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16293
16293
  row["ChurnR"], row["LTV1"], row["LTV2"], row["CMGR"], row["CAC"]
16294
16294
  ])
16295
16295
 
16296
- cmgr_values = [float(row["CMGR"]) for row in results if row["CMGR"] != '']
16297
- if cmgr_values:
16298
- avg_cmgr = sum(cmgr_values) / len(cmgr_values)
16296
+ cmgr = [float(r["CMGR"]) for r in results if r["CMGR"] != ""]
16297
+ if cmgr:
16298
+ factors = [1 + v for v in cmgr]
16299
+ avg = math.prod(factors) ** (1 / len(factors))
16299
16300
  writer.writerow([])
16300
- writer.writerow([f"Rev ~ ×{round(1 + avg_cmgr, 2)} monthly"])
16301
+ writer.writerow([f"Rev ~ ×{round(avg, 2)} monthly"])
16301
16302
 
16302
16303
  # отправить файл
16303
16304
  thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
@@ -16305,6 +16306,182 @@ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16305
16306
  except Exception as e:
16306
16307
  logger.info(log_ % str(e))
16307
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
+
16308
16485
  # endregion
16309
16486
 
16310
16487
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yeref
3
- Version: 0.24.58
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=oYcAHaa446TWJ629Jdp-HTUWUxKybIgPG8n5h8JQ-hE,1043913
5
- yeref-0.24.58.dist-info/METADATA,sha256=ImkuOmjIcywQPSe2bTJStJWvt7aQUhsF7DDi093zEZw,119
6
- yeref-0.24.58.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- yeref-0.24.58.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
8
- yeref-0.24.58.dist-info/RECORD,,