yeref 0.24.45__tar.gz → 0.24.48__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.48
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.48',
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
 
@@ -16066,20 +16065,15 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16066
16065
 
16067
16066
  for it in USER_TXS:
16068
16067
  dt_start = it.get("DT_START", "")
16069
- amount = int(it.get("AMOUNT", "0"))
16070
16068
  dt_tx = datetime.strptime(dt_start, "%d-%m-%Y_%H-%M-%S")
16071
16069
  month_key = dt_tx.strftime("%Y-%m")
16072
16070
  metrics_by_month[month_key]["tx"] += 1
16073
16071
 
16074
16072
  for it in USER_PAYMENTS:
16075
16073
  dt_start = it.get("DT_START", "")
16076
- amount = int(it.get("AMOUNT", "0"))
16077
16074
  dt_tx = datetime.strptime(dt_start, "%d-%m-%Y_%H-%M-%S")
16078
16075
  month_key = dt_tx.strftime("%Y-%m")
16079
- metrics_by_month[month_key]["tx"] += 1
16080
-
16081
- # metrics_by_month[month_key]["tx"] += len(USER_TXS)
16082
- # metrics_by_month[month_key]["pay"] += len(USER_PAYMENTS)
16076
+ metrics_by_month[month_key]["pay"] += 1
16083
16077
 
16084
16078
  if USER_DT:
16085
16079
  dt_obj = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S")
@@ -16092,7 +16086,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16092
16086
  sql = f'SELECT USER_TID, USER_VARS, USER_LSTS FROM {schema_name}_{ENT_TID}.USER'
16093
16087
  data_users = await db_select_pg(sql, (), BASE_P)
16094
16088
  print(f"schema_name {data_users=}")
16095
- process_user_rows(data_users, schema_name)
16089
+ process_user_rows(data_users)
16096
16090
 
16097
16091
  sql = f"SELECT USER_TID, USER_VARS, USER_LSTS FROM \"USER\""
16098
16092
  data_users = await db_select_pg(sql, (), BASE_P)
@@ -16100,7 +16094,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16100
16094
  process_user_rows(data_users)
16101
16095
 
16102
16096
  all_months = sorted(metrics_by_month.keys())
16103
- f_name = os.path.join(EXTRA_D, "1_metrics.csv")
16097
+ f_name = os.path.join(EXTRA_D, "1_activity_metrics.csv")
16104
16098
  with open(f_name, mode="w", encoding="utf-8", newline="") as csvfile:
16105
16099
  writer = csv.writer(csvfile, delimiter=',')
16106
16100
  writer.writerow(["MO", "/start", "/startapp", "DAU", "MAU", "Wallets", "TX Count", "PAY Count", "TVL"])
@@ -16126,6 +16120,191 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16126
16120
  except Exception as e:
16127
16121
  logger.info(log_ % str(e))
16128
16122
  await asyncio.sleep(round(random.uniform(0, 1), 2))
16123
+
16124
+
16125
+ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
16126
+ try:
16127
+ # собрать всех пользователей
16128
+ sql = 'SELECT USER_TID, USER_VARS, USER_LSTS FROM "USER"'
16129
+ data_users = await db_select_pg(sql, (), BASE_P)
16130
+
16131
+ # структура: метрики по месяцу
16132
+ metrics = defaultdict(lambda: {
16133
+ "N": 0,
16134
+ "sum_amount": 0.0,
16135
+ "payments_count": 0,
16136
+ "churn_count": 0
16137
+ })
16138
+ # набор пользователей, чтобы не дважды считать N в одном месяце
16139
+ seen_new = set()
16140
+
16141
+ months = ["2025-06", "2025-07", "2025-08", "2025-09"]
16142
+ data_users = []
16143
+ for _ in range(3):
16144
+ # дата входа
16145
+ entry_month = random.choice(months)
16146
+ entry_day = random.randint(1, 28)
16147
+ entry_date = f"{entry_month}-{entry_day:02}"
16148
+ entry_dt_obj = datetime.strptime(entry_date, '%Y-%m-%d')
16149
+ entry_dt = f"{entry_dt_obj.strftime('%d-%m-%Y')}_{datetime.now().strftime('%H-%M-%S')}"
16150
+ utm = random.choice(["/start", "/startapp"])
16151
+
16152
+ # месяцы от входа и дальше
16153
+ valid_months = [m for m in months if datetime.strptime(m + "-01", "%Y-%m-%d") >= entry_dt_obj.replace(day=1)]
16154
+ if not valid_months:
16155
+ valid_months = [entry_month]
16156
+
16157
+ user_mau = sorted(random.sample(valid_months, k=random.randint(1, len(valid_months))))
16158
+ user_dau_dates = set()
16159
+ txs, payments = [], []
16160
+
16161
+ # транзакция
16162
+ if user_mau and random.random() < 0.5:
16163
+ tx_month = random.choice(user_mau)
16164
+ tx_day = random.randint(1, 28)
16165
+ tx_date = f"{tx_month}-{tx_day:02}"
16166
+ dt_tx = datetime.strptime(tx_date, "%Y-%m-%d")
16167
+ if dt_tx >= entry_dt_obj:
16168
+ txs = [{
16169
+ "TYPE": random.choice(["don", "sub", "pst"]),
16170
+ "AMOUNT": str(random.randint(1, 10)),
16171
+ "ADDRESS": f"address{random.randint(1, 999)}",
16172
+ "DT_START": f"{dt_tx.strftime('%d-%m-%Y')}_12-00-00",
16173
+ }]
16174
+ user_dau_dates.add(tx_date)
16175
+
16176
+ # платеж
16177
+ if user_mau and random.random() < 0.5:
16178
+ pay_month = random.choice(user_mau)
16179
+ pay_day = random.randint(1, 28)
16180
+ pay_date = f"{pay_month}-{pay_day:02}"
16181
+ dt_pay = datetime.strptime(pay_date, "%Y-%m-%d")
16182
+ if dt_pay >= entry_dt_obj:
16183
+ payments = [{
16184
+ "TYPE": random.choice(["don", "sub", "pst"]),
16185
+ "DT_START": f"{dt_pay.strftime('%d-%m-%Y')}_14-00-00",
16186
+ "DT_END": "0",
16187
+ "AMOUNT": str(random.randint(1, 10))
16188
+ }]
16189
+ user_dau_dates.add(pay_date)
16190
+
16191
+ # вход в приложение
16192
+ user_dau_dates.add(entry_date)
16193
+
16194
+ # остальные посещения
16195
+ for m in user_mau:
16196
+ day = random.randint(1, 28)
16197
+ visit = f"{m}-{day:02}"
16198
+ dt_visit = datetime.strptime(visit, "%Y-%m-%d")
16199
+ if dt_visit >= entry_dt_obj and random.random() < 0.7:
16200
+ user_dau_dates.add(visit)
16201
+
16202
+ user_dau = sorted(user_dau_dates)
16203
+ wallet = f"wallet{random.randint(1, 100)}" if txs else random.choice([f"wallet{random.randint(1, 100)}", ""])
16204
+
16205
+ data_users.append((
16206
+ random.randint(100000, 999999),
16207
+ json.dumps({"USER_WALLET": wallet, "USER_UTM": utm, "USER_DT": entry_dt}),
16208
+ json.dumps({"USER_DAU": user_dau, "USER_MAU": user_mau, "USER_TXS": txs, "USER_PAYMENTS": payments})
16209
+ ))
16210
+
16211
+ for USER_TID, USER_VARS, USER_LSTS in data_users:
16212
+ USER_VARS = json.loads(USER_VARS or USER_VARS_)
16213
+ USER_LSTS = json.loads(USER_LSTS or USER_LSTS_)
16214
+ USER_DT = USER_VARS.get("USER_DT", "")
16215
+ USER_PAYMENTS = USER_LSTS.get("USER_PAYMENTS", [])
16216
+ USER_STATUSES = USER_LSTS.get("USER_STATUSES", [])
16217
+ # месяц регистрации
16218
+
16219
+ if USER_DT:
16220
+ dt_obj = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S")
16221
+ mo = dt_obj.strftime("%Y-%m")
16222
+ if (USER_TID, mo) not in seen_new:
16223
+ seen_new.add((USER_TID, mo))
16224
+ metrics[mo]["N"] += 1
16225
+
16226
+ # платежи этого пользователя
16227
+ for pay in USER_PAYMENTS:
16228
+ start = pay.get("DT_START", "")
16229
+ amt = pay.get("AMOUNT", 0)
16230
+
16231
+ dt_p = datetime.strptime(start, "%d-%m-%Y_%H-%M-%S")
16232
+ mo_p = dt_p.strftime("%Y-%m")
16233
+ val = float(amt) * 0.013
16234
+ metrics[mo_p]["sum_amount"] += val
16235
+ metrics[mo_p]["payments_count"] += 1
16236
+
16237
+ # статусы (отток)
16238
+ for status in USER_STATUSES:
16239
+ key, ts = next(iter(status.items()))
16240
+ if key in ("left", "kicked"):
16241
+ dt_s = datetime.strptime(ts, "%d-%m-%Y_%H-%M-%S")
16242
+ mo_s = dt_s.strftime("%Y-%m")
16243
+ metrics[mo_s]["churn_count"] += 1
16244
+ break
16245
+
16246
+ # список месяцев в порядке
16247
+ all_months = sorted(metrics.keys())
16248
+ results = []
16249
+ first_mrr = None
16250
+ for idx, mo in enumerate(all_months):
16251
+ data = metrics[mo]
16252
+ N = data["N"]
16253
+ MRR = data["sum_amount"]
16254
+
16255
+ def fmt(x):
16256
+ return (f"{x:.2f}".rstrip("0").rstrip(".")) if x is not None else ""
16257
+ mrr_fmt = fmt(MRR)
16258
+
16259
+ ARPU = MRR / N if N else None
16260
+ ARR = MRR * 12 if N else None
16261
+ churn = data["churn_count"]
16262
+ ChurnR = churn / N if N else None
16263
+ LTV1 = (ARPU / ChurnR) if (ARPU is not None and ChurnR and ChurnR > 0) else None
16264
+
16265
+ pay_cnt = data["payments_count"]
16266
+ # LTV2 = (payments_count/N) * (sum_amount/payments_count)
16267
+ if N and pay_cnt:
16268
+ LTV2 = (pay_cnt / N) * (MRR / pay_cnt)
16269
+ else:
16270
+ LTV2 = None
16271
+
16272
+ # CMGR: от первого месяца до текущего
16273
+ if idx == 0 or first_mrr is None or first_mrr == 0:
16274
+ CMGR = None
16275
+ if idx == 0:
16276
+ first_mrr = MRR
16277
+ else:
16278
+ CMGR = ((MRR / first_mrr) ** (1 / idx) ) - 1
16279
+
16280
+ results.append({
16281
+ "MO": mo,
16282
+ "N": str(N),
16283
+ "MRR": mrr_fmt,
16284
+ "ARPU": fmt(ARPU),
16285
+ "ARR": fmt(ARR),
16286
+ "ChurnR": fmt(ChurnR),
16287
+ "LTV1": fmt(LTV1),
16288
+ "CMGR": fmt(CMGR),
16289
+ "CAC": ""
16290
+ })
16291
+
16292
+ path = os.path.join(EXTRA_D, "2_unit_metrics.csv")
16293
+ with open(path, "w", newline="", encoding="utf-8") as f:
16294
+ writer = csv.writer(f)
16295
+ writer.writerow(["MO", "N", "MRR", "ARPU", "ARR", "ChurnR", "LTV1", "CMGR", "CAC"])
16296
+ for row in results:
16297
+ writer.writerow([
16298
+ row["MO"], row["N"], row["MRR"], row["ARPU"], row["ARR"],
16299
+ row["ChurnR"], row["LTV1"], row["CMGR"], row["CAC"]
16300
+ ])
16301
+
16302
+ # отправить файл
16303
+ thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
16304
+ await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
16305
+ except Exception as e:
16306
+ logger.info(log_ % str(e))
16307
+ await asyncio.sleep(round(random.uniform(0, 1), 2))
16129
16308
  # endregion
16130
16309
 
16131
16310
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yeref
3
- Version: 0.24.45
3
+ Version: 0.24.48
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