yeref 0.25.5__py3-none-any.whl → 0.25.7__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
@@ -16013,7 +16013,7 @@ async def calc_metrics(bot, PROJECT_USERNAME, dataroom_folder_id, EXTRA_D, BASE_
|
|
16013
16013
|
|
16014
16014
|
# --- Начинаем сразу с объединённого цикла по всем метрикам r1–r5 ---
|
16015
16015
|
metrics_paths = [r1, r2, r3, r4, r5]
|
16016
|
-
tables = []
|
16016
|
+
tables = []
|
16017
16017
|
|
16018
16018
|
# 1) Собираем существующие CSV-файлы
|
16019
16019
|
for path in metrics_paths:
|
@@ -16038,45 +16038,43 @@ async def calc_metrics(bot, PROJECT_USERNAME, dataroom_folder_id, EXTRA_D, BASE_
|
|
16038
16038
|
http_auth = credentials.authorize(httplib2.Http())
|
16039
16039
|
sheets_service = build('sheets', 'v4', http=http_auth, cache_discovery=False)
|
16040
16040
|
|
16041
|
-
# 4)
|
16041
|
+
# 4) Получаем metadata, ищем лист с названием PROJECT_USERNAME
|
16042
16042
|
spreadsheet = sheets_service.spreadsheets().get(spreadsheetId=dataroom_folder_id).execute()
|
16043
|
-
|
16043
|
+
sheet_id_target = None
|
16044
16044
|
for sheet in spreadsheet.get('sheets', []):
|
16045
16045
|
props = sheet.get('properties', {})
|
16046
|
-
|
16047
|
-
|
16048
|
-
|
16046
|
+
if props.get('title') == PROJECT_USERNAME:
|
16047
|
+
sheet_id_target = props.get('sheetId')
|
16048
|
+
logger.info(f"Найден существующий лист '{PROJECT_USERNAME}', sheetId={sheet_id_target}")
|
16049
16049
|
break
|
16050
16050
|
|
16051
|
-
|
16052
|
-
|
16051
|
+
# 5) Если листа нет — создаём новый лист с названием PROJECT_USERNAME
|
16052
|
+
if sheet_id_target is None:
|
16053
|
+
add_request = {
|
16053
16054
|
'requests': [
|
16054
16055
|
{
|
16055
|
-
'
|
16056
|
+
'addSheet': {
|
16056
16057
|
'properties': {
|
16057
|
-
'sheetId': sheet_id_to_rename,
|
16058
16058
|
'title': PROJECT_USERNAME
|
16059
|
-
}
|
16060
|
-
'fields': 'title'
|
16059
|
+
}
|
16061
16060
|
}
|
16062
16061
|
}
|
16063
16062
|
]
|
16064
16063
|
}
|
16065
|
-
sheets_service.spreadsheets().batchUpdate(
|
16064
|
+
response = sheets_service.spreadsheets().batchUpdate(
|
16066
16065
|
spreadsheetId=dataroom_folder_id,
|
16067
|
-
body=
|
16066
|
+
body=add_request
|
16068
16067
|
).execute()
|
16069
|
-
|
16070
|
-
|
16071
|
-
logger.warning("Не найден лист 'Лист1' или 'Sheet1' для переименования.")
|
16068
|
+
sheet_id_target = response['replies'][0]['addSheet']['properties']['sheetId']
|
16069
|
+
logger.info(f"Создан новый лист '{PROJECT_USERNAME}', sheetId={sheet_id_target}")
|
16072
16070
|
|
16073
|
-
#
|
16071
|
+
# 6) Готовим запросы для записи всех таблиц одна за другой с промежутком
|
16074
16072
|
safe_title = PROJECT_USERNAME.replace("'", "\\'")
|
16075
16073
|
offset = 1 # первая строка, с которой начнём писать
|
16076
16074
|
data_requests = []
|
16077
16075
|
|
16078
16076
|
for tbl in tables:
|
16079
|
-
#
|
16077
|
+
# 6.1) Заголовок: имя CSV-файла
|
16080
16078
|
header_range = f"'{safe_title}'!A{offset}"
|
16081
16079
|
data_requests.append({
|
16082
16080
|
'range': header_range,
|
@@ -16086,7 +16084,7 @@ async def calc_metrics(bot, PROJECT_USERNAME, dataroom_folder_id, EXTRA_D, BASE_
|
|
16086
16084
|
logger.info(f"Добавлен заголовок '{tbl['name']}' → A{offset}")
|
16087
16085
|
offset += 1
|
16088
16086
|
|
16089
|
-
#
|
16087
|
+
# 6.2) Содержимое CSV: все строки сразу под заголовком
|
16090
16088
|
data_range = f"'{safe_title}'!A{offset}"
|
16091
16089
|
data_requests.append({
|
16092
16090
|
'range': data_range,
|
@@ -16096,10 +16094,10 @@ async def calc_metrics(bot, PROJECT_USERNAME, dataroom_folder_id, EXTRA_D, BASE_
|
|
16096
16094
|
logger.info(f"Добавлены {len(tbl['rows'])} строк из '{tbl['name']}' → A{offset}")
|
16097
16095
|
offset += len(tbl['rows'])
|
16098
16096
|
|
16099
|
-
#
|
16097
|
+
# 6.3) Оставляем минимум 2 пустые строки перед следующей таблицей
|
16100
16098
|
offset += 2
|
16101
16099
|
|
16102
|
-
#
|
16100
|
+
# 7) Выполняем единый batchUpdate для всех data_requests
|
16103
16101
|
write_body = {
|
16104
16102
|
'valueInputOption': 'USER_ENTERED',
|
16105
16103
|
'data': data_requests
|
@@ -16342,7 +16340,7 @@ async def return_unit_metrics(bot, data_users, EXTRA_D):
|
|
16342
16340
|
factors = [1 + v for v in cmgr_vals]
|
16343
16341
|
avg = math.prod(factors) ** (1 / len(factors))
|
16344
16342
|
writer.writerow([])
|
16345
|
-
writer.writerow([f"Rev ~ ×{round(avg, 2)} monthly"])
|
16343
|
+
writer.writerow([f"Rev ~ ×{round(avg, 2)} monthly".replace('.', ',')])
|
16346
16344
|
|
16347
16345
|
result = f_name
|
16348
16346
|
thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
|
@@ -16437,7 +16435,7 @@ async def return_cohort_metrics(bot, data_users, EXTRA_D):
|
|
16437
16435
|
for r in table:
|
16438
16436
|
writer.writerow(r)
|
16439
16437
|
writer.writerow([])
|
16440
|
-
writer.writerow([f"Churn ~ ×{avg_churn:.2f} monthly"])
|
16438
|
+
writer.writerow([f"Churn ~ ×{avg_churn:.2f} monthly".replace('.', ',')])
|
16441
16439
|
|
16442
16440
|
result = f_name
|
16443
16441
|
thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
|
@@ -16511,12 +16509,12 @@ async def return_retention_metrics(bot, data_users, EXTRA_D):
|
|
16511
16509
|
else:
|
16512
16510
|
rev = rev_by_cohort[c].get(offset, 0.0)
|
16513
16511
|
if rev > 0:
|
16514
|
-
cell = f"{rev:.1f}"
|
16512
|
+
cell = f"{rev:.1f}".replace('.', ',')
|
16515
16513
|
row.append(cell)
|
16516
16514
|
row_sum += rev
|
16517
16515
|
else:
|
16518
|
-
row.append("0
|
16519
|
-
row.append(f"{row_sum:.1f}")
|
16516
|
+
row.append("0,0")
|
16517
|
+
row.append(f"{row_sum:.1f}".replace('.', ','))
|
16520
16518
|
writer.writerow(row)
|
16521
16519
|
|
16522
16520
|
factors = []
|
@@ -16529,7 +16527,7 @@ async def return_retention_metrics(bot, data_users, EXTRA_D):
|
|
16529
16527
|
avg_multiplier = math.prod(factors) ** (1 / len(factors)) if factors else 1.0
|
16530
16528
|
|
16531
16529
|
writer.writerow([])
|
16532
|
-
writer.writerow([f"NRR ~ ×{avg_multiplier:.2f} monthly"])
|
16530
|
+
writer.writerow([f"NRR ~ ×{avg_multiplier:.2f} monthly".replace('.', ',')])
|
16533
16531
|
|
16534
16532
|
result = f_name
|
16535
16533
|
thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
|
@@ -16546,6 +16544,7 @@ async def return_profit_and_loss_metrics(bot, data_users, EXTRA_D):
|
|
16546
16544
|
try:
|
16547
16545
|
metrics = defaultdict(lambda: {"sum_amount": 0.0})
|
16548
16546
|
|
16547
|
+
# Собираем данные по месяцам
|
16549
16548
|
for USER_TID, USER_VARS, USER_LSTS in data_users:
|
16550
16549
|
USER_LSTS = json.loads(USER_LSTS or "{}")
|
16551
16550
|
USER_PAYMENTS = USER_LSTS.get("USER_PAYMENTS", [])
|
@@ -16556,12 +16555,14 @@ async def return_profit_and_loss_metrics(bot, data_users, EXTRA_D):
|
|
16556
16555
|
amt = float(pay.get("AMOUNT", 0)) * 0.013
|
16557
16556
|
metrics[mo_p]["sum_amount"] += amt
|
16558
16557
|
|
16558
|
+
# Функция форматирования с запятой
|
16559
16559
|
def fmt(x):
|
16560
|
-
return f"{x:.2f}".rstrip("0").rstrip(".") if x is not None else ""
|
16560
|
+
return f"{x:.2f}".rstrip("0").rstrip(".").replace(".", ",") if x is not None else ""
|
16561
16561
|
|
16562
16562
|
# Сортируем месяцы
|
16563
16563
|
months_sorted = sorted(metrics.keys())
|
16564
16564
|
results = []
|
16565
|
+
np_values = []
|
16565
16566
|
|
16566
16567
|
for mo in months_sorted:
|
16567
16568
|
MRR = metrics[mo]["sum_amount"]
|
@@ -16570,12 +16571,12 @@ async def return_profit_and_loss_metrics(bot, data_users, EXTRA_D):
|
|
16570
16571
|
OPEX = 4.5
|
16571
16572
|
OP = GP - OPEX
|
16572
16573
|
|
16573
|
-
#
|
16574
|
-
after_comm = OP * 0.70
|
16575
|
-
after_fiat = after_comm * 0.99
|
16576
|
-
COMM = OP - after_comm
|
16577
|
-
EXCH = after_comm - after_fiat
|
16578
|
-
NP = after_fiat
|
16574
|
+
# комиссия 30% и обмен 1%
|
16575
|
+
after_comm = OP * 0.70
|
16576
|
+
after_fiat = after_comm * 0.99
|
16577
|
+
COMM = OP - after_comm
|
16578
|
+
EXCH = after_comm - after_fiat
|
16579
|
+
NP = after_fiat
|
16579
16580
|
|
16580
16581
|
results.append([
|
16581
16582
|
mo,
|
@@ -16588,16 +16589,27 @@ async def return_profit_and_loss_metrics(bot, data_users, EXTRA_D):
|
|
16588
16589
|
fmt(EXCH),
|
16589
16590
|
fmt(NP)
|
16590
16591
|
])
|
16592
|
+
np_values.append(NP)
|
16593
|
+
|
16594
|
+
# Вычисляем средний NP (если есть хотя бы одно значение)
|
16595
|
+
avg_np = sum(np_values) / len(np_values) if np_values else 0.0
|
16596
|
+
avg_np_str = fmt(avg_np)
|
16591
16597
|
|
16592
16598
|
f_name = os.path.join(EXTRA_D, "5_profit_and_loss_metrics.csv")
|
16593
16599
|
with open(f_name, "w", newline="", encoding="utf-8") as f:
|
16594
16600
|
writer = csv.writer(f)
|
16601
|
+
# Заголовок
|
16595
16602
|
writer.writerow([
|
16596
16603
|
"Mo", "MRR", "COGS", "Gross Profit",
|
16597
16604
|
"OPEX", "Operating Profit", "Comission (30%)", "Fiat (1%)", "Net Profit"
|
16598
16605
|
])
|
16606
|
+
# Строки по месяцам
|
16599
16607
|
for row in results:
|
16600
16608
|
writer.writerow(row)
|
16609
|
+
# Пустая строка перед средним
|
16610
|
+
writer.writerow([])
|
16611
|
+
# Строка со средним Net Profit
|
16612
|
+
writer.writerow([f"Average Net Profit: {avg_np_str}"])
|
16601
16613
|
|
16602
16614
|
result = f_name
|
16603
16615
|
thumb = types.FSInputFile(os.path.join(EXTRA_D, "parse.jpg"))
|
@@ -16607,6 +16619,7 @@ async def return_profit_and_loss_metrics(bot, data_users, EXTRA_D):
|
|
16607
16619
|
await asyncio.sleep(round(random.uniform(0, 1), 2))
|
16608
16620
|
finally:
|
16609
16621
|
return result
|
16622
|
+
|
16610
16623
|
# endregion
|
16611
16624
|
|
16612
16625
|
|
@@ -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=wRpSWql996MWX-i-4GjL4SUHNmNjwheGS0I0yrx7eRg,1056354
|
5
|
+
yeref-0.25.7.dist-info/METADATA,sha256=020yfGCI6pVZfOZEIAcchgFDr7IsnqdOluNCjrHkPo4,118
|
6
|
+
yeref-0.25.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
+
yeref-0.25.7.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
|
8
|
+
yeref-0.25.7.dist-info/RECORD,,
|
yeref-0.25.5.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=ljsCvG1omKMY2PpVKfFunM1eTcMNd7w_aW_GFX3b8Ps,1055985
|
5
|
-
yeref-0.25.5.dist-info/METADATA,sha256=L3eS_VIOpEfv271siW57nYb5571PNCcE-XqbHoAID-k,118
|
6
|
-
yeref-0.25.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
-
yeref-0.25.5.dist-info/top_level.txt,sha256=yCQKchWHbfV-3OuQPYRdi2loypD-nmbDJbtt3OuKKkY,6
|
8
|
-
yeref-0.25.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|