yeref 0.24.60__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.
- {yeref-0.24.60 → yeref-0.24.61}/PKG-INFO +1 -1
- {yeref-0.24.60 → yeref-0.24.61}/setup.py +1 -1
- {yeref-0.24.60 → yeref-0.24.61}/yeref/yeref.py +88 -186
- {yeref-0.24.60 → yeref-0.24.61}/yeref.egg-info/PKG-INFO +1 -1
- {yeref-0.24.60 → yeref-0.24.61}/pyproject.toml +0 -0
- {yeref-0.24.60 → yeref-0.24.61}/setup.cfg +0 -0
- {yeref-0.24.60 → yeref-0.24.61}/yeref/__init__.py +0 -0
- {yeref-0.24.60 → yeref-0.24.61}/yeref/l_.py +0 -0
- {yeref-0.24.60 → yeref-0.24.61}/yeref/tonweb.js +0 -0
- {yeref-0.24.60 → yeref-0.24.61}/yeref.egg-info/SOURCES.txt +0 -0
- {yeref-0.24.60 → yeref-0.24.61}/yeref.egg-info/dependency_links.txt +0 -0
- {yeref-0.24.60 → yeref-0.24.61}/yeref.egg-info/top_level.txt +0 -0
@@ -16122,192 +16122,6 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16122
16122
|
await asyncio.sleep(round(random.uniform(0, 1), 2))
|
16123
16123
|
|
16124
16124
|
|
16125
|
-
async def return_unit_metrics2(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(20):
|
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:
|
16163
|
-
pay_month = random.choice(user_mau)
|
16164
|
-
pay_day = random.randint(1, 28)
|
16165
|
-
pay_date = f"{pay_month}-{pay_day:02}"
|
16166
|
-
dt_pay = datetime.strptime(pay_date, "%Y-%m-%d")
|
16167
|
-
# if dt_pay >= entry_dt_obj:
|
16168
|
-
payments = [{
|
16169
|
-
"TYPE": random.choice(["don", "sub", "pst"]),
|
16170
|
-
"DT_START": f"{dt_pay.strftime('%d-%m-%Y')}_14-00-00",
|
16171
|
-
"DT_END": "0",
|
16172
|
-
"AMOUNT": str(random.randint(1, 10))
|
16173
|
-
}]
|
16174
|
-
user_dau_dates.add(pay_date)
|
16175
|
-
|
16176
|
-
# вход в приложение
|
16177
|
-
user_dau_dates.add(entry_date)
|
16178
|
-
for m in user_mau:
|
16179
|
-
day = random.randint(1, 28)
|
16180
|
-
visit = f"{m}-{day:02}"
|
16181
|
-
dt_visit = datetime.strptime(visit, "%Y-%m-%d")
|
16182
|
-
if dt_visit >= entry_dt_obj and random.random() < 0.7:
|
16183
|
-
user_dau_dates.add(visit)
|
16184
|
-
|
16185
|
-
# статусы (отток) с низкой вероятностью
|
16186
|
-
USER_STATUSES = []
|
16187
|
-
if random.random() < 0.2: # 10% шанс оттока
|
16188
|
-
churn_month = random.choice(valid_months)
|
16189
|
-
churn_day = random.randint(1, 28)
|
16190
|
-
churn_date = f"{churn_month}-{churn_day:02}"
|
16191
|
-
churn_ts = datetime.strptime(churn_date, "%Y-%m-%d").strftime("%d-%m-%Y") + "_23-59-59"
|
16192
|
-
USER_STATUSES = [{random.choice(["left", "kicked"]): churn_ts}]
|
16193
|
-
|
16194
|
-
user_dau = sorted(user_dau_dates)
|
16195
|
-
wallet = f"wallet{random.randint(1, 100)}" if txs else random.choice([f"wallet{random.randint(1, 100)}", ""])
|
16196
|
-
|
16197
|
-
data_users.append((
|
16198
|
-
random.randint(100000, 999999),
|
16199
|
-
json.dumps({"USER_WALLET": wallet, "USER_UTM": utm, "USER_DT": entry_dt}),
|
16200
|
-
json.dumps({"USER_DAU": user_dau, "USER_MAU": user_mau, "USER_TXS": txs,
|
16201
|
-
"USER_PAYMENTS": payments, "USER_STATUSES": USER_STATUSES})
|
16202
|
-
))
|
16203
|
-
print(f"gen {data_users=}")
|
16204
|
-
|
16205
|
-
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_)
|
16208
|
-
USER_DT = USER_VARS.get("USER_DT", "")
|
16209
|
-
USER_PAYMENTS = USER_LSTS.get("USER_PAYMENTS", [])
|
16210
|
-
USER_STATUSES = USER_LSTS.get("USER_STATUSES", [])
|
16211
|
-
|
16212
|
-
if USER_DT:
|
16213
|
-
dt_obj = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S")
|
16214
|
-
mo = dt_obj.strftime("%Y-%m")
|
16215
|
-
if (USER_TID, mo) not in seen_new:
|
16216
|
-
seen_new.add((USER_TID, mo))
|
16217
|
-
metrics[mo]["N"] += 1
|
16218
|
-
|
16219
|
-
# платежи этого пользователя
|
16220
|
-
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")
|
16225
|
-
mo_p = dt_p.strftime("%Y-%m")
|
16226
|
-
val = float(amt) * 0.013
|
16227
|
-
metrics[mo_p]["sum_amount"] += val
|
16228
|
-
metrics[mo_p]["payments_count"] += 1
|
16229
|
-
|
16230
|
-
# статусы (отток)
|
16231
|
-
for status in USER_STATUSES:
|
16232
|
-
key, ts = next(iter(status.items()))
|
16233
|
-
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")
|
16236
|
-
metrics[mo_s]["churn_count"] += 1
|
16237
|
-
break
|
16238
|
-
|
16239
|
-
all_months = sorted(metrics.keys())
|
16240
|
-
results = []
|
16241
|
-
first_mrr = None
|
16242
|
-
for idx, mo in enumerate(all_months):
|
16243
|
-
data = metrics[mo]
|
16244
|
-
N = data["N"]
|
16245
|
-
MRR = data["sum_amount"]
|
16246
|
-
|
16247
|
-
def fmt(x):
|
16248
|
-
return (f"{x:.2f}".rstrip("0").rstrip(".")) if x is not None else ""
|
16249
|
-
mrr_fmt = fmt(MRR)
|
16250
|
-
|
16251
|
-
ARPU = MRR / N if N else None
|
16252
|
-
ARR = MRR * 12 if N else None
|
16253
|
-
churn = data["churn_count"]
|
16254
|
-
ChurnR = churn / N if N else None
|
16255
|
-
LTV1 = (ARPU / ChurnR) if (ARPU is not None and ChurnR and ChurnR > 0) else None
|
16256
|
-
|
16257
|
-
pay_cnt = data["payments_count"]
|
16258
|
-
# LTV2 = (payments_count/N) * (sum_amount/payments_count)
|
16259
|
-
if N and pay_cnt:
|
16260
|
-
LTV2 = (pay_cnt / N) * (MRR / pay_cnt)
|
16261
|
-
else:
|
16262
|
-
LTV2 = None
|
16263
|
-
|
16264
|
-
# LTV1 = LTV2
|
16265
|
-
# CMGR: от первого месяца до текущего
|
16266
|
-
if idx == 0 or first_mrr is None or first_mrr == 0:
|
16267
|
-
CMGR = None
|
16268
|
-
if idx == 0:
|
16269
|
-
first_mrr = MRR
|
16270
|
-
else:
|
16271
|
-
CMGR = ((MRR / first_mrr) ** (1 / idx)) - 1
|
16272
|
-
|
16273
|
-
results.append({
|
16274
|
-
"MO": mo,
|
16275
|
-
"N": str(N),
|
16276
|
-
"MRR": mrr_fmt,
|
16277
|
-
"ARPU": fmt(ARPU),
|
16278
|
-
"ARR": fmt(ARR),
|
16279
|
-
"ChurnR": fmt(ChurnR),
|
16280
|
-
"LTV1": fmt(LTV1),
|
16281
|
-
"LTV2": fmt(LTV2),
|
16282
|
-
"CMGR": fmt(CMGR),
|
16283
|
-
"CAC": ""
|
16284
|
-
})
|
16285
|
-
|
16286
|
-
path = os.path.join(EXTRA_D, "2_unit_metrics.csv")
|
16287
|
-
with open(path, "w", newline="", encoding="utf-8") as f:
|
16288
|
-
writer = csv.writer(f)
|
16289
|
-
writer.writerow(["MO", "N", "MRR", "ARPU", "ARR", "ChurnR", "LTV1", "LTV2", "CMGR", "CAC"])
|
16290
|
-
for row in results:
|
16291
|
-
writer.writerow([
|
16292
|
-
row["MO"], row["N"], row["MRR"], row["ARPU"], row["ARR"],
|
16293
|
-
row["ChurnR"], row["LTV1"], row["LTV2"], row["CMGR"], row["CAC"]
|
16294
|
-
])
|
16295
|
-
|
16296
|
-
cmgr = [float(r["CMGR"]) for r in results if r["CMGR"] != ""]
|
16297
|
-
if cmgr:
|
16298
|
-
factors = [1 + v for v in cmgr]
|
16299
|
-
avg = math.prod(factors) ** (1 / len(factors))
|
16300
|
-
writer.writerow([])
|
16301
|
-
writer.writerow([f"Rev ~ ×{round(avg, 2)} monthly"])
|
16302
|
-
|
16303
|
-
# отправить файл
|
16304
|
-
thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
|
16305
|
-
await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
|
16306
|
-
except Exception as e:
|
16307
|
-
logger.info(log_ % str(e))
|
16308
|
-
await asyncio.sleep(round(random.uniform(0, 1), 2))
|
16309
|
-
|
16310
|
-
|
16311
16125
|
async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
16312
16126
|
try:
|
16313
16127
|
sql = 'SELECT USER_TID, USER_VARS, USER_LSTS FROM "USER"'
|
@@ -16482,6 +16296,94 @@ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16482
16296
|
logger.info(log_ % str(e))
|
16483
16297
|
await asyncio.sleep(round(random.uniform(0, 1), 2))
|
16484
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
|
+
|
16382
|
+
thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
|
16383
|
+
await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
|
16384
|
+
except Exception as e:
|
16385
|
+
logger.info(log_ % str(e))
|
16386
|
+
await asyncio.sleep(round(random.uniform(0, 1), 2))
|
16485
16387
|
# endregion
|
16486
16388
|
|
16487
16389
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|