yeref 0.24.65__py3-none-any.whl → 0.24.66__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
@@ -16235,77 +16235,11 @@ async def return_unit_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16235
16235
|
|
16236
16236
|
async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
16237
16237
|
try:
|
16238
|
-
# Получаем всех пользователей
|
16239
16238
|
sql = 'SELECT USER_TID, USER_VARS, USER_LSTS FROM "USER"'
|
16240
16239
|
data_users = await db_select_pg(sql, (), BASE_P)
|
16241
16240
|
|
16242
|
-
|
16243
|
-
|
16244
|
-
activity_months = defaultdict(set) # USER_TID -> set месяцов, в которые был DAU
|
16245
|
-
|
16246
|
-
months = ["2025-06", "2025-07", "2025-08", "2025-09"]
|
16247
|
-
data_users = []
|
16248
|
-
for _ in range(20):
|
16249
|
-
# дата входа
|
16250
|
-
entry_month = random.choice(months)
|
16251
|
-
entry_day = random.randint(1, 28)
|
16252
|
-
entry_date = f"{entry_month}-{entry_day:02}"
|
16253
|
-
entry_dt_obj = datetime.strptime(entry_date, '%Y-%m-%d')
|
16254
|
-
entry_dt = f"{entry_dt_obj.strftime('%d-%m-%Y')}_{datetime.now().strftime('%H-%M-%S')}"
|
16255
|
-
utm = random.choice(["/start", "/startapp"])
|
16256
|
-
|
16257
|
-
# месяцы от входа и дальше
|
16258
|
-
valid_months = [m for m in months if datetime.strptime(m + "-01", "%Y-%m-%d") >= entry_dt_obj.replace(day=1)]
|
16259
|
-
if not valid_months:
|
16260
|
-
valid_months = [entry_month]
|
16261
|
-
|
16262
|
-
user_mau = sorted(random.sample(valid_months, k=random.randint(1, len(valid_months))))
|
16263
|
-
user_dau_dates = set()
|
16264
|
-
txs, payments = [], []
|
16265
|
-
|
16266
|
-
# платеж
|
16267
|
-
if user_mau:
|
16268
|
-
pay_month = random.choice(user_mau)
|
16269
|
-
pay_day = random.randint(1, 28)
|
16270
|
-
pay_date = f"{pay_month}-{pay_day:02}"
|
16271
|
-
dt_pay = datetime.strptime(pay_date, "%Y-%m-%d")
|
16272
|
-
# if dt_pay >= entry_dt_obj:
|
16273
|
-
payments = [{
|
16274
|
-
"TYPE": random.choice(["don", "sub", "pst"]),
|
16275
|
-
"DT_START": f"{dt_pay.strftime('%d-%m-%Y')}_14-00-00",
|
16276
|
-
"DT_END": "0",
|
16277
|
-
"AMOUNT": str(random.randint(1, 10))
|
16278
|
-
}]
|
16279
|
-
user_dau_dates.add(pay_date)
|
16280
|
-
|
16281
|
-
# вход в приложение
|
16282
|
-
user_dau_dates.add(entry_date)
|
16283
|
-
for m in user_mau:
|
16284
|
-
day = random.randint(1, 28)
|
16285
|
-
visit = f"{m}-{day:02}"
|
16286
|
-
dt_visit = datetime.strptime(visit, "%Y-%m-%d")
|
16287
|
-
if dt_visit >= entry_dt_obj and random.random() < 0.7:
|
16288
|
-
user_dau_dates.add(visit)
|
16289
|
-
|
16290
|
-
# статусы (отток) с низкой вероятностью
|
16291
|
-
USER_STATUSES = []
|
16292
|
-
if random.random() < 0.2: # 10% шанс оттока
|
16293
|
-
churn_month = random.choice(valid_months)
|
16294
|
-
churn_day = random.randint(1, 28)
|
16295
|
-
churn_date = f"{churn_month}-{churn_day:02}"
|
16296
|
-
churn_ts = datetime.strptime(churn_date, "%Y-%m-%d").strftime("%d-%m-%Y") + "_23-59-59"
|
16297
|
-
USER_STATUSES = [{random.choice(["left", "kicked"]): churn_ts}]
|
16298
|
-
|
16299
|
-
user_dau = sorted(user_dau_dates)
|
16300
|
-
wallet = f"wallet{random.randint(1, 100)}" if txs else random.choice([f"wallet{random.randint(1, 100)}", ""])
|
16301
|
-
|
16302
|
-
data_users.append((
|
16303
|
-
random.randint(100000, 999999),
|
16304
|
-
json.dumps({"USER_WALLET": wallet, "USER_UTM": utm, "USER_DT": entry_dt}),
|
16305
|
-
json.dumps({"USER_DAU": user_dau, "USER_MAU": user_mau, "USER_TXS": txs,
|
16306
|
-
"USER_PAYMENTS": payments, "USER_STATUSES": USER_STATUSES})
|
16307
|
-
))
|
16308
|
-
print(f"gen {data_users=}")
|
16241
|
+
cohorts = defaultdict(set)
|
16242
|
+
activity_months = defaultdict(set)
|
16309
16243
|
|
16310
16244
|
for USER_TID, USER_VARS, USER_LSTS in data_users:
|
16311
16245
|
USER_VARS = json.loads(USER_VARS or "{}")
|
@@ -16313,11 +16247,12 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16313
16247
|
USER_DT = USER_VARS.get("USER_DT", "")
|
16314
16248
|
USER_DAU = USER_LSTS.get("USER_DAU", [])
|
16315
16249
|
|
16316
|
-
|
16250
|
+
if not USER_DT:
|
16251
|
+
continue
|
16252
|
+
|
16317
16253
|
entry_mo = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S").strftime("%Y-%m")
|
16318
16254
|
cohorts[entry_mo].add(USER_TID)
|
16319
16255
|
|
16320
|
-
# сохраняем месяцы активности
|
16321
16256
|
for day_str in USER_DAU:
|
16322
16257
|
try:
|
16323
16258
|
mo = datetime.strptime(day_str, "%Y-%m-%d").strftime("%Y-%m")
|
@@ -16325,10 +16260,8 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16325
16260
|
except:
|
16326
16261
|
pass
|
16327
16262
|
|
16328
|
-
# Список уникальных месяцев cohort, отсортированный
|
16329
16263
|
cohort_months = sorted(cohorts.keys())
|
16330
16264
|
|
16331
|
-
# Функция для прибавления месяцев
|
16332
16265
|
def add_months(mo_str, n):
|
16333
16266
|
y, m = map(int, mo_str.split("-"))
|
16334
16267
|
total = m + n
|
@@ -16337,32 +16270,23 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16337
16270
|
return f"{new_y:04d}-{new_m:02d}"
|
16338
16271
|
|
16339
16272
|
num_cohorts = len(cohort_months)
|
16340
|
-
|
16341
|
-
# Формируем таблицу: строки M1..M{num_cohorts}
|
16342
16273
|
table = []
|
16343
16274
|
header = ["Месяц/Когорта"]
|
16344
|
-
# Добавляем в заголовок каждый cohort + его размер
|
16345
16275
|
for mo in cohort_months:
|
16346
16276
|
header.append(f"{mo} ({len(cohorts[mo])})")
|
16347
16277
|
header.append("∑")
|
16348
16278
|
table.append(header)
|
16349
16279
|
|
16350
|
-
#
|
16280
|
+
# Собираем саму таблицу
|
16351
16281
|
for i in range(num_cohorts):
|
16352
16282
|
row = [f"M{i+1}"]
|
16353
16283
|
row_sum = 0
|
16354
|
-
for
|
16355
|
-
# вычисляем целевой месяц = cohort_mo + i месяцев
|
16284
|
+
for cohort_mo in cohort_months:
|
16356
16285
|
target_mo = add_months(cohort_mo, i)
|
16357
16286
|
if i == 0:
|
16358
|
-
# M1 = размер когорты
|
16359
16287
|
val = len(cohorts[cohort_mo])
|
16360
16288
|
else:
|
16361
|
-
|
16362
|
-
cnt = 0
|
16363
|
-
for uid in cohorts[cohort_mo]:
|
16364
|
-
if target_mo in activity_months.get(uid, set()):
|
16365
|
-
cnt += 1
|
16289
|
+
cnt = sum(1 for uid in cohorts[cohort_mo] if target_mo in activity_months.get(uid, set()))
|
16366
16290
|
val = cnt
|
16367
16291
|
if val:
|
16368
16292
|
row.append(str(val))
|
@@ -16372,14 +16296,39 @@ async def return_cohort_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16372
16296
|
row.append(str(row_sum))
|
16373
16297
|
table.append(row)
|
16374
16298
|
|
16299
|
+
# Считаем общий отток по всем когортам
|
16300
|
+
total_lost = 0
|
16301
|
+
total_start = 0
|
16302
|
+
for cohort_mo in cohort_months:
|
16303
|
+
counts = []
|
16304
|
+
for i in range(num_cohorts):
|
16305
|
+
target_mo = add_months(cohort_mo, i)
|
16306
|
+
if i == 0:
|
16307
|
+
count = len(cohorts[cohort_mo])
|
16308
|
+
else:
|
16309
|
+
count = sum(1 for uid in cohorts[cohort_mo] if target_mo in activity_months.get(uid, set()))
|
16310
|
+
counts.append(count)
|
16311
|
+
for k in range(len(counts) - 1):
|
16312
|
+
start_cnt = counts[k]
|
16313
|
+
next_cnt = counts[k + 1]
|
16314
|
+
if start_cnt > 0:
|
16315
|
+
lost = start_cnt - next_cnt
|
16316
|
+
total_lost += lost
|
16317
|
+
total_start += start_cnt
|
16318
|
+
|
16319
|
+
avg_churn = (total_lost / total_start * 100) if total_start else 0
|
16320
|
+
|
16375
16321
|
path = os.path.join(EXTRA_D, "3_cohort_metrics.csv")
|
16376
16322
|
with open(path, "w", newline="", encoding="utf-8") as f:
|
16377
16323
|
writer = csv.writer(f)
|
16378
16324
|
for r in table:
|
16379
16325
|
writer.writerow(r)
|
16326
|
+
writer.writerow([])
|
16327
|
+
writer.writerow([f"Avg monthly cohort churn: {avg_churn:.2f}%"])
|
16380
16328
|
|
16381
16329
|
thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
|
16382
16330
|
await bot.send_document(chat_id=my_tid, document=types.FSInputFile(path), thumbnail=thumb)
|
16331
|
+
|
16383
16332
|
except Exception as e:
|
16384
16333
|
logger.info(log_ % str(e))
|
16385
16334
|
await asyncio.sleep(round(random.uniform(0, 1), 2))
|
@@ -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=jPkWUJXO_GvUGd6SXXRnAhGNr_G0snlu5NoCgvOyI5Q,1049848
|
5
|
+
yeref-0.24.66.dist-info/METADATA,sha256=GWW789HTQT4WP3uucPD8e7cSrrUpekFvWNjSqHXdwcg,119
|
6
|
+
yeref-0.24.66.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
+
yeref-0.24.66.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
|
8
|
+
yeref-0.24.66.dist-info/RECORD,,
|
yeref-0.24.65.dist-info/RECORD
DELETED
@@ -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=fze5I1x5ZD0D4OgYpK1sv-bDgu2RXtMGPhDwjiwBa64,1053049
|
5
|
-
yeref-0.24.65.dist-info/METADATA,sha256=Q5AZlJwo8T2kw62j1kSn8eFMPG3O6DBs7KoKHmLDWYo,119
|
6
|
-
yeref-0.24.65.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
-
yeref-0.24.65.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
|
8
|
-
yeref-0.24.65.dist-info/RECORD,,
|
File without changes
|
File without changes
|