yeref 0.24.45__py3-none-any.whl → 0.24.47__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
@@ -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
|
|
@@ -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]["
|
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
|
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, "
|
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
|
|
@@ -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=Yf6eODA8nBmwtjCaNKjYT9NDeOgdBOavfrBbp7Kn-3I,1040962
|
5
|
+
yeref-0.24.47.dist-info/METADATA,sha256=okbZiO7_-MNM4Ccx9dyac1ofOgCFgr8s-cXVBfi-dQE,119
|
6
|
+
yeref-0.24.47.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
+
yeref-0.24.47.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
|
8
|
+
yeref-0.24.47.dist-info/RECORD,,
|
yeref-0.24.45.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=hXlYJap_OSiPqNcxebfzaI9gQXAOWexOZCM-DHrOFaU,1036295
|
5
|
-
yeref-0.24.45.dist-info/METADATA,sha256=oH1PRSgXYrjI2-rxst1wxtnvkIKg_nfwPH4idxBvrmw,119
|
6
|
-
yeref-0.24.45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
-
yeref-0.24.45.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
|
8
|
-
yeref-0.24.45.dist-info/RECORD,,
|
File without changes
|
File without changes
|