yeref 0.24.44__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 +134 -12
- {yeref-0.24.44.dist-info → yeref-0.24.47.dist-info}/METADATA +1 -1
- yeref-0.24.47.dist-info/RECORD +8 -0
- yeref-0.24.44.dist-info/RECORD +0 -8
- {yeref-0.24.44.dist-info → yeref-0.24.47.dist-info}/WHEEL +0 -0
- {yeref-0.24.44.dist-info → yeref-0.24.47.dist-info}/top_level.txt +0 -0
yeref/yeref.py
CHANGED
@@ -15953,8 +15953,9 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
15953
15953
|
seen_dau = set()
|
15954
15954
|
wallets_set = set()
|
15955
15955
|
users_set = set()
|
15956
|
+
pay_set = set()
|
15956
15957
|
|
15957
|
-
def process_user_rows(rows
|
15958
|
+
def process_user_rows(rows):
|
15958
15959
|
# months = ["2025-06", "2025-07", "2025-08", "2025-09"]
|
15959
15960
|
# data_users = []
|
15960
15961
|
# for _ in range(3):
|
@@ -16062,16 +16063,22 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16062
16063
|
if USER_WALLET:
|
16063
16064
|
metrics_by_month[month_key]["wallets"] += 1
|
16064
16065
|
|
16065
|
-
|
16066
|
-
|
16067
|
-
|
16068
|
-
|
16069
|
-
|
16070
|
-
|
16071
|
-
# metrics_by_month[month_key]["tvl"] += amount
|
16066
|
+
for it in USER_TXS:
|
16067
|
+
dt_start = it.get("DT_START", "")
|
16068
|
+
amount = int(it.get("AMOUNT", "0"))
|
16069
|
+
dt_tx = datetime.strptime(dt_start, "%d-%m-%Y_%H-%M-%S")
|
16070
|
+
month_key = dt_tx.strftime("%Y-%m")
|
16071
|
+
metrics_by_month[month_key]["tx"] += 1
|
16072
16072
|
|
16073
|
-
|
16074
|
-
|
16073
|
+
for it in USER_PAYMENTS:
|
16074
|
+
dt_start = it.get("DT_START", "")
|
16075
|
+
amount = int(it.get("AMOUNT", "0"))
|
16076
|
+
dt_tx = datetime.strptime(dt_start, "%d-%m-%Y_%H-%M-%S")
|
16077
|
+
month_key = dt_tx.strftime("%Y-%m")
|
16078
|
+
metrics_by_month[month_key]["pay"] += 1
|
16079
|
+
|
16080
|
+
# metrics_by_month[month_key]["tx"] += len(USER_TXS)
|
16081
|
+
# metrics_by_month[month_key]["pay"] += len(USER_PAYMENTS)
|
16075
16082
|
|
16076
16083
|
if USER_DT:
|
16077
16084
|
dt_obj = datetime.strptime(USER_DT, "%d-%m-%Y_%H-%M-%S")
|
@@ -16084,7 +16091,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16084
16091
|
sql = f'SELECT USER_TID, USER_VARS, USER_LSTS FROM {schema_name}_{ENT_TID}.USER'
|
16085
16092
|
data_users = await db_select_pg(sql, (), BASE_P)
|
16086
16093
|
print(f"schema_name {data_users=}")
|
16087
|
-
process_user_rows(data_users
|
16094
|
+
process_user_rows(data_users)
|
16088
16095
|
|
16089
16096
|
sql = f"SELECT USER_TID, USER_VARS, USER_LSTS FROM \"USER\""
|
16090
16097
|
data_users = await db_select_pg(sql, (), BASE_P)
|
@@ -16092,7 +16099,7 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16092
16099
|
process_user_rows(data_users)
|
16093
16100
|
|
16094
16101
|
all_months = sorted(metrics_by_month.keys())
|
16095
|
-
f_name = os.path.join(EXTRA_D, "
|
16102
|
+
f_name = os.path.join(EXTRA_D, "1_activity_metrics.csv")
|
16096
16103
|
with open(f_name, mode="w", encoding="utf-8", newline="") as csvfile:
|
16097
16104
|
writer = csv.writer(csvfile, delimiter=',')
|
16098
16105
|
writer.writerow(["MO", "/start", "/startapp", "DAU", "MAU", "Wallets", "TX Count", "PAY Count", "TVL"])
|
@@ -16118,6 +16125,121 @@ async def return_activity_metrics(bot, PROJECT_USERNAME, EXTRA_D, BASE_P):
|
|
16118
16125
|
except Exception as e:
|
16119
16126
|
logger.info(log_ % str(e))
|
16120
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))
|
16121
16243
|
# endregion
|
16122
16244
|
|
16123
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.44.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=HMkqoO9mzXB3OtE_3s2rtqfI0GnaiZOyb5twEq07PaE,1035956
|
5
|
-
yeref-0.24.44.dist-info/METADATA,sha256=GqjZ6h44GwuVWwlhdN32HBlXO6qrt0zjbWNHnkTzYTw,119
|
6
|
-
yeref-0.24.44.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
-
yeref-0.24.44.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
|
8
|
-
yeref-0.24.44.dist-info/RECORD,,
|
File without changes
|
File without changes
|