yeref 0.24.80__tar.gz → 0.24.82__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yeref
3
- Version: 0.24.80
3
+ Version: 0.24.82
4
4
  Summary: desc-f
5
5
  Author: john smith
6
6
  Dynamic: author
@@ -2,7 +2,7 @@ from setuptools import setup
2
2
 
3
3
  setup(
4
4
  name='yeref',
5
- version='0.24.80',
5
+ version='0.24.82',
6
6
  description='desc-f',
7
7
  author='john smith',
8
8
  packages=['yeref'],
@@ -16373,16 +16373,15 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16373
16373
  total_lost = 0
16374
16374
  total_start = 0
16375
16375
  for j in range(num_months):
16376
- # для каждой когорты собираем по календарным месяцам
16377
16376
  for i in range(j, num_months - 1):
16378
16377
  start_cnt = counts[i][j]
16379
16378
  next_cnt = counts[i + 1][j]
16380
16379
  if start_cnt > 0:
16381
- lost = start_cnt - next_cnt
16380
+ lost = max(start_cnt - next_cnt, 0)
16382
16381
  total_lost += lost
16383
16382
  total_start += start_cnt
16384
16383
 
16385
- avg_churn = (total_lost / total_start * 100) if total_start else 0
16384
+ avg_churn = (total_lost / total_start) if total_start else 0
16386
16385
 
16387
16386
  path = os.path.join(EXTRA_D, "3_cohort_metrics.csv")
16388
16387
  with open(path, "w", newline="", encoding="utf-8") as f:
@@ -16390,11 +16389,10 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16390
16389
  for r in table:
16391
16390
  writer.writerow(r)
16392
16391
  writer.writerow([])
16393
- writer.writerow([f"Churn ~ ×{avg_churn:.0f} monthly"])
16392
+ writer.writerow([f"Churn ~ ×{avg_churn:.2f} monthly"])
16394
16393
 
16395
16394
  thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
16396
16395
  await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
16397
-
16398
16396
  except Exception as e:
16399
16397
  logger.info(log_ % str(e))
16400
16398
  await asyncio.sleep(round(random.uniform(0, 1), 2))
@@ -16555,6 +16553,131 @@ async def return_retention_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16555
16553
  await asyncio.sleep(round(random.uniform(0, 1), 2))
16556
16554
 
16557
16555
 
16556
+ async def return_profit_and_loss_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16557
+ try:
16558
+ sql = 'SELECT USER_LSTS FROM "USER"'
16559
+ data_users = await db_select_pg(sql, (), BASE_P)
16560
+
16561
+ months = ["2025-06", "2025-07", "2025-08", "2025-09"]
16562
+ data_users = []
16563
+ for _ in range(30):
16564
+ # дата входа
16565
+ entry_month = random.choice(months)
16566
+ entry_day = random.randint(1, 28)
16567
+ entry_date = f"{entry_month}-{entry_day:02}"
16568
+ entry_dt_obj = datetime.strptime(entry_date, '%Y-%m-%d')
16569
+ entry_dt = f"{entry_dt_obj.strftime('%d-%m-%Y')}_{datetime.now().strftime('%H-%M-%S')}"
16570
+ utm = random.choice(["/start", "/startapp"])
16571
+
16572
+ # месяцы от входа и дальше
16573
+ valid_months = [m for m in months if datetime.strptime(m + "-01", "%Y-%m-%d") >= entry_dt_obj.replace(day=1)]
16574
+ if not valid_months:
16575
+ valid_months = [entry_month]
16576
+
16577
+ user_mau = sorted(random.sample(valid_months, k=random.randint(1, len(valid_months))))
16578
+ user_dau_dates = set()
16579
+ txs, payments = [], []
16580
+
16581
+ # платеж
16582
+ if user_mau:
16583
+ pay_month = random.choice(user_mau)
16584
+ pay_day = random.randint(1, 28)
16585
+ pay_date = f"{pay_month}-{pay_day:02}"
16586
+ dt_pay = datetime.strptime(pay_date, "%Y-%m-%d")
16587
+ # if dt_pay >= entry_dt_obj:
16588
+ payments = [{
16589
+ "TYPE": random.choice(["don", "sub", "pst"]),
16590
+ "DT_START": f"{dt_pay.strftime('%d-%m-%Y')}_14-00-00",
16591
+ "DT_END": "0",
16592
+ "AMOUNT": str(random.randint(1, 10))
16593
+ }]
16594
+ user_dau_dates.add(pay_date)
16595
+
16596
+ # вход в приложение
16597
+ user_dau_dates.add(entry_date)
16598
+ for m in user_mau:
16599
+ day = random.randint(1, 28)
16600
+ visit = f"{m}-{day:02}"
16601
+ dt_visit = datetime.strptime(visit, "%Y-%m-%d")
16602
+ if dt_visit >= entry_dt_obj and random.random() < 0.7:
16603
+ user_dau_dates.add(visit)
16604
+
16605
+ # статусы (отток) с низкой вероятностью
16606
+ USER_STATUSES = []
16607
+ if random.random() < 0.2: # 10% шанс оттока
16608
+ churn_month = random.choice(valid_months)
16609
+ churn_day = random.randint(1, 28)
16610
+ churn_date = f"{churn_month}-{churn_day:02}"
16611
+ churn_ts = datetime.strptime(churn_date, "%Y-%m-%d").strftime("%d-%m-%Y") + "_23-59-59"
16612
+ USER_STATUSES = [{random.choice(["left", "kicked"]): churn_ts}]
16613
+
16614
+ user_dau = sorted(user_dau_dates)
16615
+ wallet = f"wallet{random.randint(1, 100)}" if txs else random.choice([f"wallet{random.randint(1, 100)}", ""])
16616
+
16617
+ data_users.append((
16618
+ random.randint(100000, 999999),
16619
+ json.dumps({"USER_WALLET": wallet, "USER_UTM": utm, "USER_DT": entry_dt}),
16620
+ json.dumps({"USER_DAU": user_dau, "USER_MAU": user_mau, "USER_TXS": txs,
16621
+ "USER_PAYMENTS": payments, "USER_STATUSES": USER_STATUSES})
16622
+ ))
16623
+ print(f"gen {data_users=}")
16624
+
16625
+ metrics = defaultdict(lambda: {"sum_amount": 0.0})
16626
+
16627
+ for (USER_LSTS,) in data_users:
16628
+ USER_LSTS = json.loads(USER_LSTS or "{}")
16629
+ USER_PAYMENTS = USER_LSTS.get("USER_PAYMENTS", [])
16630
+
16631
+ for pay in USER_PAYMENTS:
16632
+ dt_p = datetime.strptime(pay.get("DT_START", ""), "%d-%m-%Y_%H-%M-%S")
16633
+ mo_p = dt_p.strftime("%Y-%m")
16634
+ amt = float(pay.get("AMOUNT", 0)) * 0.013
16635
+ metrics[mo_p]["sum_amount"] += amt
16636
+
16637
+ def fmt(x):
16638
+ return f"{x:.2f}".rstrip("0").rstrip(".")
16639
+
16640
+ months_sorted = sorted(metrics.keys())
16641
+ results = []
16642
+
16643
+ for mo in months_sorted:
16644
+ MRR = metrics[mo]["sum_amount"]
16645
+ COGS = 0.0
16646
+ GP = MRR - COGS
16647
+ OPEX = 4.5
16648
+ OP = GP - OPEX
16649
+ COMM = MRR * 0.3
16650
+ EXCH = MRR * 0.01
16651
+ NP = OP - COMM - EXCH
16652
+
16653
+ results.append([
16654
+ mo.upper()[5:] + "_" + mo[:4], # Format to "JUN_2024"
16655
+ fmt(MRR),
16656
+ fmt(COGS),
16657
+ fmt(GP),
16658
+ fmt(OPEX),
16659
+ fmt(OP),
16660
+ fmt(COMM),
16661
+ fmt(EXCH),
16662
+ fmt(NP)
16663
+ ])
16664
+
16665
+ path = os.path.join(EXTRA_D, "4_profit_and_loss_metrics.csv")
16666
+ with open(path, "w", newline="", encoding="utf-8") as f:
16667
+ writer = csv.writer(f)
16668
+ writer.writerow([
16669
+ "Месяц", "Total MRR", "COGS", "Валовая прибыль",
16670
+ "OPEX", "Операционная прибыль", "Комиссия (30%)", "Обмен (1%)", "Чистая прибыль"
16671
+ ])
16672
+ for row in results:
16673
+ writer.writerow(row)
16674
+
16675
+ thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
16676
+ await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
16677
+ except Exception as e:
16678
+ logger.info(log_ % str(e))
16679
+ await asyncio.sleep(round(random.uniform(0, 1), 2))
16680
+
16558
16681
  # endregion
16559
16682
 
16560
16683
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yeref
3
- Version: 0.24.80
3
+ Version: 0.24.82
4
4
  Summary: desc-f
5
5
  Author: john smith
6
6
  Dynamic: author
File without changes
File without changes
File without changes
File without changes
File without changes