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.
- {yeref-0.24.45 → yeref-0.24.48}/PKG-INFO +1 -1
- {yeref-0.24.45 → yeref-0.24.48}/setup.py +1 -1
- {yeref-0.24.45 → yeref-0.24.48}/yeref/yeref.py +189 -10
- {yeref-0.24.45 → yeref-0.24.48}/yeref.egg-info/PKG-INFO +1 -1
- {yeref-0.24.45 → yeref-0.24.48}/pyproject.toml +0 -0
- {yeref-0.24.45 → yeref-0.24.48}/setup.cfg +0 -0
- {yeref-0.24.45 → yeref-0.24.48}/yeref/__init__.py +0 -0
- {yeref-0.24.45 → yeref-0.24.48}/yeref/l_.py +0 -0
- {yeref-0.24.45 → yeref-0.24.48}/yeref/tonweb.js +0 -0
- {yeref-0.24.45 → yeref-0.24.48}/yeref.egg-info/SOURCES.txt +0 -0
- {yeref-0.24.45 → yeref-0.24.48}/yeref.egg-info/dependency_links.txt +0 -0
- {yeref-0.24.45 → yeref-0.24.48}/yeref.egg-info/top_level.txt +0 -0
@@ -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
|
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]["
|
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
|
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, "
|
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
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|