yeref 0.24.59__tar.gz → 0.24.61__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.59
3
+ Version: 0.24.61
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.59',
5
+ version='0.24.61',
6
6
  description='desc-f',
7
7
  author='john smith',
8
8
  packages=['yeref'],
@@ -16124,18 +16124,15 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16124
16124
 
16125
16125
  async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16126
16126
  try:
16127
- # собрать всех пользователей
16128
16127
  sql = 'SELECT USER_TID, USER_VARS, USER_LSTS FROM "USER"'
16129
16128
  data_users = await db_select_pg(sql, (), BASE_P)
16130
16129
 
16131
- # структура: метрики по месяцу
16132
16130
  metrics = defaultdict(lambda: {
16133
- "N": 0,
16131
+ "new_users": 0,
16134
16132
  "sum_amount": 0.0,
16135
16133
  "payments_count": 0,
16136
16134
  "churn_count": 0
16137
16135
  })
16138
- # набор пользователей, чтобы не дважды считать N в одном месяце
16139
16136
  seen_new = set()
16140
16137
 
16141
16138
  months = ["2025-06", "2025-07", "2025-08", "2025-09"]
@@ -16203,72 +16200,65 @@ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16203
16200
  print(f"gen {data_users=}")
16204
16201
 
16205
16202
  for USER_TID, USER_VARS, USER_LSTS in data_users:
16206
- USER_VARS = json.loads(USER_VARS or USER_VARS_)
16207
- USER_LSTS = json.loads(USER_LSTS or USER_LSTS_)
16203
+ USER_VARS = json.loads(USER_VARS or "{}")
16204
+ USER_LSTS = json.loads(USER_LSTS or "{}")
16208
16205
  USER_DT = USER_VARS.get("USER_DT", "")
16209
16206
  USER_PAYMENTS = USER_LSTS.get("USER_PAYMENTS", [])
16210
16207
  USER_STATUSES = USER_LSTS.get("USER_STATUSES", [])
16211
16208
 
16212
16209
  if USER_DT:
16213
- dt_obj = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S")
16214
- mo = dt_obj.strftime("%Y-%m")
16210
+ mo = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S").strftime("%Y-%m")
16215
16211
  if (USER_TID, mo) not in seen_new:
16216
16212
  seen_new.add((USER_TID, mo))
16217
- metrics[mo]["N"] += 1
16213
+ metrics[mo]["new_users"] += 1
16218
16214
 
16219
- # платежи этого пользователя
16220
16215
  for pay in USER_PAYMENTS:
16221
- start = pay.get("DT_START", "")
16222
- amt = pay.get("AMOUNT", 0)
16223
-
16224
- dt_p = datetime.strptime(start, "%d-%m-%Y_%H-%M-%S")
16216
+ dt_p = datetime.strptime(pay.get("DT_START", ""), "%d-%m-%Y_%H-%M-%S")
16225
16217
  mo_p = dt_p.strftime("%Y-%m")
16226
- val = float(amt) * 0.013
16227
- metrics[mo_p]["sum_amount"] += val
16218
+ amt = float(pay.get("AMOUNT", 0)) * 0.013
16219
+ metrics[mo_p]["sum_amount"] += amt
16228
16220
  metrics[mo_p]["payments_count"] += 1
16229
16221
 
16230
- # статусы (отток)
16231
16222
  for status in USER_STATUSES:
16232
16223
  key, ts = next(iter(status.items()))
16233
16224
  if key in ("left", "kicked"):
16234
- dt_s = datetime.strptime(ts, "%d-%m-%Y_%H-%M-%S")
16235
- mo_s = dt_s.strftime("%Y-%m")
16225
+ mo_s = datetime.strptime(ts, "%d-%m-%Y_%H-%M-%S").strftime("%Y-%m")
16236
16226
  metrics[mo_s]["churn_count"] += 1
16237
16227
  break
16238
16228
 
16239
16229
  all_months = sorted(metrics.keys())
16230
+ cumulative_users = 0
16240
16231
  results = []
16241
- first_mrr = None
16232
+
16242
16233
  for idx, mo in enumerate(all_months):
16243
16234
  data = metrics[mo]
16244
- N = data["N"]
16245
- MRR = data["sum_amount"]
16235
+ new_u = data["new_users"]
16236
+ cumulative_users += new_u
16246
16237
 
16238
+ MRR = data["sum_amount"]
16247
16239
  def fmt(x):
16248
- return (f"{x:.2f}".rstrip("0").rstrip(".")) if x is not None else ""
16249
- mrr_fmt = fmt(MRR)
16240
+ return f"{x:.2f}".rstrip("0").rstrip(".") if x is not None else ""
16250
16241
 
16242
+ mrr_fmt = fmt(MRR)
16243
+ N = cumulative_users
16251
16244
  ARPU = MRR / N if N else None
16252
16245
  ARR = MRR * 12 if N else None
16246
+
16253
16247
  churn = data["churn_count"]
16254
16248
  ChurnR = churn / N if N else None
16255
16249
  LTV1 = (ARPU / ChurnR) if (ARPU is not None and ChurnR and ChurnR > 0) else None
16256
16250
 
16257
16251
  pay_cnt = data["payments_count"]
16258
- # LTV2 = (payments_count/N) * (sum_amount/payments_count)
16259
16252
  if N and pay_cnt:
16260
16253
  LTV2 = (pay_cnt / N) * (MRR / pay_cnt)
16261
16254
  else:
16262
16255
  LTV2 = None
16263
16256
 
16264
- # LTV1 = LTV2
16265
- # CMGR: от первого месяца до текущего
16266
- if idx == 0 or first_mrr is None or first_mrr == 0:
16257
+ if idx == 0:
16267
16258
  CMGR = None
16268
- if idx == 0:
16269
- first_mrr = MRR
16259
+ first_mrr = MRR
16270
16260
  else:
16271
- CMGR = ((MRR / first_mrr) ** (1 / idx)) - 1
16261
+ CMGR = ((MRR / first_mrr) ** (1 / idx)) - 1 if first_mrr and MRR is not None else None
16272
16262
 
16273
16263
  results.append({
16274
16264
  "MO": mo,
@@ -16293,14 +16283,102 @@ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16293
16283
  row["ChurnR"], row["LTV1"], row["LTV2"], row["CMGR"], row["CAC"]
16294
16284
  ])
16295
16285
 
16296
- cmgr = [float(r["CMGR"]) for r in results if r["CMGR"] != ""]
16297
- if cmgr:
16298
- factors = [1 + v for v in cmgr]
16286
+ cmgr_vals = [float(r["CMGR"]) for r in results if r["CMGR"] != ""]
16287
+ if cmgr_vals:
16288
+ factors = [1 + v for v in cmgr_vals]
16299
16289
  avg = math.prod(factors) ** (1 / len(factors))
16300
16290
  writer.writerow([])
16301
16291
  writer.writerow([f"Rev ~ ×{round(avg, 2)} monthly"])
16302
16292
 
16303
- # отправить файл
16293
+ thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
16294
+ await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
16295
+ except Exception as e:
16296
+ logger.info(log_ % str(e))
16297
+ await asyncio.sleep(round(random.uniform(0, 1), 2))
16298
+
16299
+
16300
+ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16301
+ try:
16302
+ # Получаем всех пользователей
16303
+ sql = 'SELECT USER_TID, USER_VARS, USER_LSTS FROM "USER"'
16304
+ data_users = await db_select_pg(sql, (), BASE_P)
16305
+
16306
+ # Собираем для каждого пользователя: месяц входа и набор месяцев активности
16307
+ cohorts = defaultdict(set) # cohort_month -> set(USER_TID)
16308
+ activity_months = defaultdict(set) # USER_TID -> set месяцов, в которые был DAU
16309
+
16310
+ for USER_TID, USER_VARS, USER_LSTS in data_users:
16311
+ USER_VARS = json.loads(USER_VARS or "{}")
16312
+ USER_LSTS = json.loads(USER_LSTS or "{}")
16313
+ USER_DT = USER_VARS.get("USER_DT", "")
16314
+ USER_DAU = USER_LSTS.get("USER_DAU", [])
16315
+
16316
+ # вычисляем месяц входа
16317
+ entry_mo = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S").strftime("%Y-%m")
16318
+ cohorts[entry_mo].add(USER_TID)
16319
+
16320
+ # сохраняем месяцы активности
16321
+ for day_str in USER_DAU:
16322
+ try:
16323
+ mo = datetime.strptime(day_str, "%Y-%m-%d").strftime("%Y-%m")
16324
+ activity_months[USER_TID].add(mo)
16325
+ except:
16326
+ pass
16327
+
16328
+ # Список уникальных месяцев cohort, отсортированный
16329
+ cohort_months = sorted(cohorts.keys())
16330
+
16331
+ # Функция для прибавления месяцев
16332
+ def add_months(mo_str, n):
16333
+ y, m = map(int, mo_str.split("-"))
16334
+ total = m + n
16335
+ new_y = y + (total - 1) // 12
16336
+ new_m = (total - 1) % 12 + 1
16337
+ return f"{new_y:04d}-{new_m:02d}"
16338
+
16339
+ num_cohorts = len(cohort_months)
16340
+
16341
+ # Формируем таблицу: строки M1..M{num_cohorts}
16342
+ table = []
16343
+ header = ["Месяц/Когорта"]
16344
+ # Добавляем в заголовок каждый cohort + его размер
16345
+ for mo in cohort_months:
16346
+ header.append(f"{mo} ({len(cohorts[mo])})")
16347
+ header.append("∑")
16348
+ table.append(header)
16349
+
16350
+ # Для каждой строки-месяца кортаций
16351
+ for i in range(num_cohorts):
16352
+ row = [f"M{i+1}"]
16353
+ row_sum = 0
16354
+ for j, cohort_mo in enumerate(cohort_months):
16355
+ # вычисляем целевой месяц = cohort_mo + i месяцев
16356
+ target_mo = add_months(cohort_mo, i)
16357
+ if i == 0:
16358
+ # M1 = размер когорты
16359
+ val = len(cohorts[cohort_mo])
16360
+ else:
16361
+ # считаем, сколько пользователей из когорты были активны в target_mo
16362
+ cnt = 0
16363
+ for uid in cohorts[cohort_mo]:
16364
+ if target_mo in activity_months.get(uid, set()):
16365
+ cnt += 1
16366
+ val = cnt
16367
+ if val:
16368
+ row.append(str(val))
16369
+ row_sum += val
16370
+ else:
16371
+ row.append("")
16372
+ row.append(str(row_sum))
16373
+ table.append(row)
16374
+
16375
+ # Записываем CSV
16376
+ path = os.path.join(EXTRA_D, "3_cohort_metrics.csv")
16377
+ with open(path, "w", newline="", encoding="utf-8") as f:
16378
+ writer = csv.writer(f)
16379
+ for r in table:
16380
+ writer.writerow(r)
16381
+
16304
16382
  thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
16305
16383
  await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
16306
16384
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yeref
3
- Version: 0.24.59
3
+ Version: 0.24.61
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