yeref 0.24.44__py3-none-any.whl → 0.24.47__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
@@ -15953,8 +15953,9 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
15953
15953
  seen_dau = set()
15954
15954
  wallets_set = set()
15955
15955
  users_set = set()
15956
+ pay_set = set()
15956
15957
 
15957
- def process_user_rows(rows, schema_name=""):
15958
+ def process_user_rows(rows):
15958
15959
  # months = ["2025-06", "2025-07", "2025-08", "2025-09"]
15959
15960
  # data_users = []
15960
15961
  # for _ in range(3):
@@ -16062,16 +16063,22 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16062
16063
  if USER_WALLET:
16063
16064
  metrics_by_month[month_key]["wallets"] += 1
16064
16065
 
16065
- # for tx in USER_TXS:
16066
- # dt_start = tx.get("DT_START", "")
16067
- # amount = int(tx.get("AMOUNT", "0"))
16068
- # dt_tx = datetime.strptime(dt_start, "%d-%m-%Y_%H-%M-%S")
16069
- # month_key = dt_tx.strftime("%Y-%m")
16070
- # metrics_by_month[month_key]["tx"] += 1
16071
- # metrics_by_month[month_key]["tvl"] += amount
16066
+ for it in USER_TXS:
16067
+ dt_start = it.get("DT_START", "")
16068
+ amount = int(it.get("AMOUNT", "0"))
16069
+ dt_tx = datetime.strptime(dt_start, "%d-%m-%Y_%H-%M-%S")
16070
+ month_key = dt_tx.strftime("%Y-%m")
16071
+ metrics_by_month[month_key]["tx"] += 1
16072
16072
 
16073
- metrics_by_month[month_key]["tx"] += len(USER_TXS)
16074
- metrics_by_month[month_key]["pay"] += len(USER_PAYMENTS)
16073
+ for it in USER_PAYMENTS:
16074
+ dt_start = it.get("DT_START", "")
16075
+ amount = int(it.get("AMOUNT", "0"))
16076
+ dt_tx = datetime.strptime(dt_start, "%d-%m-%Y_%H-%M-%S")
16077
+ month_key = dt_tx.strftime("%Y-%m")
16078
+ metrics_by_month[month_key]["pay"] += 1
16079
+
16080
+ # metrics_by_month[month_key]["tx"] += len(USER_TXS)
16081
+ # metrics_by_month[month_key]["pay"] += len(USER_PAYMENTS)
16075
16082
 
16076
16083
  if USER_DT:
16077
16084
  dt_obj = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S")
@@ -16084,7 +16091,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16084
16091
  sql = f'SELECT USER_TID, USER_VARS, USER_LSTS FROM {schema_name}_{ENT_TID}.USER'
16085
16092
  data_users = await db_select_pg(sql, (), BASE_P)
16086
16093
  print(f"schema_name {data_users=}")
16087
- process_user_rows(data_users, schema_name)
16094
+ process_user_rows(data_users)
16088
16095
 
16089
16096
  sql = f"SELECT USER_TID, USER_VARS, USER_LSTS FROM \"USER\""
16090
16097
  data_users = await db_select_pg(sql, (), BASE_P)
@@ -16092,7 +16099,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16092
16099
  process_user_rows(data_users)
16093
16100
 
16094
16101
  all_months = sorted(metrics_by_month.keys())
16095
- f_name = os.path.join(EXTRA_D, "1_metrics.csv")
16102
+ f_name = os.path.join(EXTRA_D, "1_activity_metrics.csv")
16096
16103
  with open(f_name, mode="w", encoding="utf-8", newline="") as csvfile:
16097
16104
  writer = csv.writer(csvfile, delimiter=',')
16098
16105
  writer.writerow(["MO", "/start", "/startapp", "DAU", "MAU", "Wallets", "TX Count", "PAY Count", "TVL"])
@@ -16118,6 +16125,121 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16118
16125
  except Exception as e:
16119
16126
  logger.info(log_ % str(e))
16120
16127
  await asyncio.sleep(round(random.uniform(0, 1), 2))
16128
+
16129
+
16130
+ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16131
+ try:
16132
+ # собрать всех пользователей
16133
+ sql = 'SELECT USER_TID, USER_VARS, USER_LSTS FROM "USER"'
16134
+ data_users = await db_select_pg(sql, (), BASE_P)
16135
+
16136
+ # структура: метрики по месяцу
16137
+ metrics = defaultdict(lambda: {
16138
+ "N": 0,
16139
+ "sum_amount": 0.0,
16140
+ "payments_count": 0,
16141
+ "churn_count": 0
16142
+ })
16143
+ # набор пользователей, чтобы не дважды считать N в одном месяце
16144
+ seen_new = set()
16145
+
16146
+ for USER_TID, USER_VARS, USER_LSTS in data_users:
16147
+ vars_ = json.loads(USER_VARS or "{}")
16148
+ lsts = json.loads(USER_LSTS or "{}")
16149
+ dt_str = vars_.get("USER_DT", "")
16150
+ # месяц регистрации
16151
+ if dt_str:
16152
+ dt_obj = datetime.strptime(dt_str, "%d-%m-%Y_%H-%M-%S")
16153
+ mo = dt_obj.strftime("%Y-%m")
16154
+ if (USER_TID, mo) not in seen_new:
16155
+ seen_new.add((USER_TID, mo))
16156
+ metrics[mo]["N"] += 1
16157
+
16158
+ # платежи этого пользователя
16159
+ for pay in lsts.get("USER_PAYMENTS", []):
16160
+ start = pay.get("DT_START", "")
16161
+ amt = pay.get("AMOUNT", 0)
16162
+ if start and amt:
16163
+ dt_p = datetime.strptime(start, "%d-%m-%Y_%H-%M-%S")
16164
+ mo_p = dt_p.strftime("%Y-%m")
16165
+ val = float(amt) * 0.013
16166
+ metrics[mo_p]["sum_amount"] += val
16167
+ metrics[mo_p]["payments_count"] += 1
16168
+
16169
+ # статусы (отток)
16170
+ for status in lsts.get("USER_STATUSES", []):
16171
+ key, ts = next(iter(status.items()))
16172
+ if key in ("left", "kicked"):
16173
+ dt_s = datetime.strptime(ts, "%d-%m-%Y_%H-%M-%S")
16174
+ mo_s = dt_s.strftime("%Y-%m")
16175
+ metrics[mo_s]["churn_count"] += 1
16176
+ break
16177
+
16178
+ # список месяцев в порядке
16179
+ all_months = sorted(metrics.keys())
16180
+ # посчитать доп. метрики
16181
+ results = []
16182
+ first_mrr = None
16183
+ for idx, mo in enumerate(all_months):
16184
+ data = metrics[mo]
16185
+ N = data["N"]
16186
+ MRR = data["sum_amount"]
16187
+ # округлить до 2х знаков без лишних нулей
16188
+ def fmt(x):
16189
+ return (f"{x:.2f}".rstrip("0").rstrip(".")) if x is not None else ""
16190
+ mrr_fmt = fmt(MRR)
16191
+
16192
+ ARPU = MRR / N if N else None
16193
+ ARR = MRR * 12 if N else None
16194
+ churn = data["churn_count"]
16195
+ ChurnR = churn / N if N else None
16196
+ LTV1 = (ARPU / ChurnR) if (ARPU is not None and ChurnR and ChurnR > 0) else None
16197
+
16198
+ pay_cnt = data["payments_count"]
16199
+ # LTV2 = (payments_count/N) * (sum_amount/payments_count)
16200
+ if N and pay_cnt:
16201
+ LTV2 = (pay_cnt / N) * (MRR / pay_cnt)
16202
+ else:
16203
+ LTV2 = None
16204
+
16205
+ # CMGR: от первого месяца до текущего
16206
+ if idx == 0 or first_mrr is None or first_mrr == 0:
16207
+ CMGR = None
16208
+ if idx == 0:
16209
+ first_mrr = MRR
16210
+ else:
16211
+ CMGR = ( (MRR / first_mrr) ** (1 / idx) ) - 1
16212
+
16213
+ results.append({
16214
+ "MO": mo,
16215
+ "N": str(N),
16216
+ "MRR": mrr_fmt,
16217
+ "ARPU": fmt(ARPU),
16218
+ "ARR": fmt(ARR),
16219
+ "ChurnR": fmt(ChurnR),
16220
+ "LTV1": fmt(LTV1),
16221
+ "LTV2": fmt(LTV2),
16222
+ "CMGR": fmt(CMGR),
16223
+ "CAC": ""
16224
+ })
16225
+
16226
+ # записать CSV
16227
+ path = os.path.join(EXTRA_D, "2_unit_metrics.csv")
16228
+ with open(path, "w", newline="", encoding="utf-8") as f:
16229
+ writer = csv.writer(f)
16230
+ writer.writerow(["MO", "N", "MRR", "ARPU", "ARR", "ChurnR", "LTV1", "LTV2", "CMGR", "CAC"])
16231
+ for row in results:
16232
+ writer.writerow([
16233
+ row["MO"], row["N"], row["MRR"], row["ARPU"], row["ARR"],
16234
+ row["ChurnR"], row["LTV1"], row["LTV2"], row["CMGR"], row["CAC"]
16235
+ ])
16236
+
16237
+ # отправить файл
16238
+ thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
16239
+ await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
16240
+ except Exception as e:
16241
+ logger.info(log_ % str(e))
16242
+ await asyncio.sleep(round(random.uniform(0, 1), 2))
16121
16243
  # endregion
16122
16244
 
16123
16245
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yeref
3
- Version: 0.24.44
3
+ Version: 0.24.47
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=Yf6eODA8nBmwtjCaNKjYT9NDeOgdBOavfrBbp7Kn-3I,1040962
5
+ yeref-0.24.47.dist-info/METADATA,sha256=okbZiO7_-MNM4Ccx9dyac1ofOgCFgr8s-cXVBfi-dQE,119
6
+ yeref-0.24.47.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ yeref-0.24.47.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
8
+ yeref-0.24.47.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=HMkqoO9mzXB3OtE_3s2rtqfI0GnaiZOyb5twEq07PaE,1035956
5
- yeref-0.24.44.dist-info/METADATA,sha256=GqjZ6h44GwuVWwlhdN32HBlXO6qrt0zjbWNHnkTzYTw,119
6
- yeref-0.24.44.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- yeref-0.24.44.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
8
- yeref-0.24.44.dist-info/RECORD,,