yeref 0.24.45__tar.gz → 0.24.47__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.45
3
+ Version: 0.24.47
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.45',
5
+ version='0.24.47',
6
6
  description='desc-f',
7
7
  author='john smith',
8
8
  packages=['yeref'],
@@ -15955,7 +15955,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
15955
15955
  users_set = set()
15956
15956
  pay_set = set()
15957
15957
 
15958
- def process_user_rows(rows, schema_name=""):
15958
+ def process_user_rows(rows):
15959
15959
  # months = ["2025-06", "2025-07", "2025-08", "2025-09"]
15960
15960
  # data_users = []
15961
15961
  # for _ in range(3):
@@ -16044,7 +16044,6 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16044
16044
  last_key = list(last_status.keys())[0]
16045
16045
  if last_key not in ['left', 'kicked']:
16046
16046
  users_set.add(USER_TID)
16047
- pay_set.add(USER_TID)
16048
16047
  else:
16049
16048
  users_set.add(USER_TID)
16050
16049
 
@@ -16076,7 +16075,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16076
16075
  amount = int(it.get("AMOUNT", "0"))
16077
16076
  dt_tx = datetime.strptime(dt_start, "%d-%m-%Y_%H-%M-%S")
16078
16077
  month_key = dt_tx.strftime("%Y-%m")
16079
- metrics_by_month[month_key]["tx"] += 1
16078
+ metrics_by_month[month_key]["pay"] += 1
16080
16079
 
16081
16080
  # metrics_by_month[month_key]["tx"] += len(USER_TXS)
16082
16081
  # metrics_by_month[month_key]["pay"] += len(USER_PAYMENTS)
@@ -16092,7 +16091,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16092
16091
  sql = f'SELECT USER_TID, USER_VARS, USER_LSTS FROM {schema_name}_{ENT_TID}.USER'
16093
16092
  data_users = await db_select_pg(sql, (), BASE_P)
16094
16093
  print(f"schema_name {data_users=}")
16095
- process_user_rows(data_users, schema_name)
16094
+ process_user_rows(data_users)
16096
16095
 
16097
16096
  sql = f"SELECT USER_TID, USER_VARS, USER_LSTS FROM \"USER\""
16098
16097
  data_users = await db_select_pg(sql, (), BASE_P)
@@ -16100,7 +16099,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16100
16099
  process_user_rows(data_users)
16101
16100
 
16102
16101
  all_months = sorted(metrics_by_month.keys())
16103
- f_name = os.path.join(EXTRA_D, "1_metrics.csv")
16102
+ f_name = os.path.join(EXTRA_D, "1_activity_metrics.csv")
16104
16103
  with open(f_name, mode="w", encoding="utf-8", newline="") as csvfile:
16105
16104
  writer = csv.writer(csvfile, delimiter=',')
16106
16105
  writer.writerow(["MO", "/start", "/startapp", "DAU", "MAU", "Wallets", "TX Count", "PAY Count", "TVL"])
@@ -16126,6 +16125,121 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16126
16125
  except Exception as e:
16127
16126
  logger.info(log_ % str(e))
16128
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))
16129
16243
  # endregion
16130
16244
 
16131
16245
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yeref
3
- Version: 0.24.45
3
+ Version: 0.24.47
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