xync-db 0.5.5.dev9__tar.gz → 0.5.5.dev11__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.
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/PKG-INFO +1 -1
- xync_db-0.5.5.dev11/src/xync_db/migrations/models/0012_country_ccn3.py +42 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/models/CLAUDE.md +7 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/models/__init__.py +27 -2
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/.gitignore +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/README.md +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/pyproject.toml +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/__init__.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/config.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/enums.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/exceptions.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/graph.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/i18n.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/login_store.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/logo.png +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/CLAUDE.md +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/__init__.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/0001_initial.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/0002_triggers.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/0003_seed.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/__init__.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/__init__.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/a_actor_seed.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/a_bin_seed.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/a_country_seed.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/a_tg_seed.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/b_htx_seed.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/c_net_seed.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/countries.json +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/ranges.csv +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/xync_files.sql +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/py.typed +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/shared.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/typs/__init__.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/typs/db/__init__.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/typs/db/actor.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/typs/db/ad.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/typs/db/common.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/typs/db/cred.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/typs/db/order.py +0 -0
- {xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/typs/db/pm.py +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""`country.code` → `country.ccn3`: колонка была телефонным кодом (нигде в логике не читалась),
|
|
2
|
+
становится числовым ISO 3166-1. Значения переустанавливает канонический сидер стран
|
|
3
|
+
(`a_country_seed.country_seed`), который заодно доводит уже развёрнутую БД до полного набора
|
|
4
|
+
mledoze (250 стран + 2 meta) с реальными валютами вместо USD-заглушек.
|
|
5
|
+
|
|
6
|
+
Fresh-миграции получают колонку `ccn3` сразу из `0001_initial`, поэтому здесь — транзитное
|
|
7
|
+
переименование ТОЛЬКО для БД, созданных до этого изменения (guarded: срабатывает лишь пока в
|
|
8
|
+
таблице ещё есть колонка `code`; на свежей схеме — no-op, без повторного ресида).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import ClassVar
|
|
12
|
+
|
|
13
|
+
from tortoise.migrations.migration import Migration as BaseMigration
|
|
14
|
+
from tortoise.migrations.operations import RunPython
|
|
15
|
+
|
|
16
|
+
from xync_db.migrations.models.seed.a_country_seed import country_seed
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def country_ccn3(_apps, schema_editor) -> None:
|
|
20
|
+
db = schema_editor.client
|
|
21
|
+
has_code = await db.execute_query_dict(
|
|
22
|
+
"SELECT 1 FROM information_schema.columns WHERE table_name = 'country' AND column_name = 'code'"
|
|
23
|
+
)
|
|
24
|
+
if not has_code: # свежая схема — колонка уже `ccn3` (0001), переносить нечего
|
|
25
|
+
return
|
|
26
|
+
await db.execute_script('ALTER TABLE "country" RENAME COLUMN "code" TO "ccn3";')
|
|
27
|
+
await country_seed(db)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def country_ccn3_down(_apps, schema_editor) -> None:
|
|
31
|
+
db = schema_editor.client
|
|
32
|
+
has_ccn3 = await db.execute_query_dict(
|
|
33
|
+
"SELECT 1 FROM information_schema.columns WHERE table_name = 'country' AND column_name = 'ccn3'"
|
|
34
|
+
)
|
|
35
|
+
if has_ccn3:
|
|
36
|
+
await db.execute_script('ALTER TABLE "country" RENAME COLUMN "ccn3" TO "code";')
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Migration(BaseMigration):
|
|
40
|
+
atomic = False
|
|
41
|
+
dependencies: ClassVar = [("models", "0011_pair_scale")]
|
|
42
|
+
operations: ClassVar = [RunPython(country_ccn3, country_ccn3_down)]
|
|
@@ -110,6 +110,13 @@ actor.set_user(username_id):
|
|
|
110
110
|
### `exclude(**kwargs)` negates each kwarg separately (NOT Django's `NOT(AND)`)
|
|
111
111
|
Tortoise `QuerySet.exclude(a=1, b=2)` → `NOT(a=1) AND NOT(b=2)` = `a!=1 AND b!=2` (drops a row if EITHER holds), NOT Django's `NOT(a=1 AND b=2)`. Source: `tortoise.queryset.QuerySet._filter_or_exclude` appends `~Q(**{k:v})` per kwarg, AND-joined. To exclude the **intersection**, wrap in ONE `Q`: `exclude(Q(a=1, b=2))` → `NOT(a=1 AND b=2)`. Real bug: `User.get_hot_ads` `exclude(hex__isnull=True, ad__maker__ex_id__in=SHARE_HEX_EX_IDS)` hid all hex-less ads AND all share-hex ads separately instead of only the unshared-share-hex ones; fixed with `exclude(Q(...))` + `from tortoise.expressions import Q`.
|
|
112
112
|
|
|
113
|
+
### Filtering by an m2m field multiplies rows — add `.distinct()`
|
|
114
|
+
`filter(credexs__cred__ovr_pm_id=0)` is a JOIN through `myad_cred`, so a `MyAd` with TWO matching
|
|
115
|
+
credentials comes back **twice**. Real bug: `/hot/ads?mine=true` showed every 2-payment ad doubled
|
|
116
|
+
(the taker path hid it — `_dedup_hot_by_slot` collapses by slot) and `BaseAgentClient.ads_share`
|
|
117
|
+
shared it twice. Fixed with `.distinct()` in `MyAd.hot_mads_query` (test `test_hot_mads_distinct.py`);
|
|
118
|
+
`unshared_ad_ids` already had it. Rule: any queryset filtered through an m2m relation needs `.distinct()`.
|
|
119
|
+
|
|
113
120
|
### id=0 rows are unhashable — never `set[Model]`
|
|
114
121
|
The XyncPay `Pm` row has **pk id=0** (norm='xyncpay'). Tortoise's `Model.__hash__` does `if not self.pk: raise TypeError(... unhashable)` — and `not 0` is `True`, so a fully-saved id=0 row is unhashable; putting it in a `set[Model]` (literal, `set(...)`, comprehension, or a pydantic `set[Model]` field) raises at construction. Rule: never build a `set` of model instances if any row can be id=0 (Pm especially, ovr_pm_id=0). Diff/dedup m2m by `pm.id` instead (`{p.id: p for p in pms}`, `{p.id for p in ...}`) — int sets are always hashable. Bit us in `Ad.upsert`/`_sync_pms` (the pms m2m path: `AdXIn.pms` is `list[Pm]` not `set`, diffs by id). Test `test_ad_upsert_pms_sync`.
|
|
115
122
|
|
|
@@ -1421,6 +1421,27 @@ class Ad(Model, TsTrait):
|
|
|
1421
1421
|
"price",
|
|
1422
1422
|
}
|
|
1423
1423
|
|
|
1424
|
+
@classmethod
|
|
1425
|
+
def maker_hot_ads(cls, maker_id: int, cur_id: int, coin_id: int, is_sell: bool):
|
|
1426
|
+
"""ВСЕ hot-объявы (с XyncPay-credex) мейкера в ОДНОМ направлении (пара+сторона) — ими ads_fresh
|
|
1427
|
+
и обновляет цены, и считает занятость слотов. Два обязательных свойства выборки:
|
|
1428
|
+
- `.distinct()`: credexs — m2m, объява с ДВУМЯ XyncPay-платёжками приезжает JOIN'ом дважды
|
|
1429
|
+
(тот же класс бага, что чинил distinct в `hot_mads_query`);
|
|
1430
|
+
- БЕЗ `.limit()`: срезать выборку бюджетом `same_dir_ad` нельзя — хвост направления тогда не
|
|
1431
|
+
обновляется (висит на бирже со старым курсом) И не попадает в занятые слоты, а ads_fresh
|
|
1432
|
+
досоздаёт в «свободный» слот дубль уже стоящей объявы (Bybit отбивает его 90043).
|
|
1433
|
+
`exid=0` (заготовка, на бирже ещё нет) и `my_ad.blocked` — вне hot-оборота."""
|
|
1434
|
+
return cls.filter(
|
|
1435
|
+
exid__not=0,
|
|
1436
|
+
my_ad__blocked=False,
|
|
1437
|
+
my_ad__credexs__cred__ovr_pm_id=0,
|
|
1438
|
+
my_ad__credexs__cred__pmcur__cur_id=cur_id,
|
|
1439
|
+
pair_side__pair__coin_id=coin_id,
|
|
1440
|
+
pair_side__pair__cur_id=cur_id,
|
|
1441
|
+
pair_side__is_sell=is_sell,
|
|
1442
|
+
maker_id=maker_id,
|
|
1443
|
+
).distinct()
|
|
1444
|
+
|
|
1424
1445
|
@staticmethod
|
|
1425
1446
|
def slot_of(min_fiat: int, filtered: bool, minimum: int) -> tuple[int, int] | None:
|
|
1426
1447
|
"""Слот hot-объявы — пара `(множитель_мин_лимита, фильтрованность)` по её нижнему лимиту и
|
|
@@ -1567,8 +1588,12 @@ class MyAd(Model): # Road
|
|
|
1567
1588
|
fltr["ad__status"] = AdStatus.active
|
|
1568
1589
|
if ex_ids:
|
|
1569
1590
|
fltr["ad__maker__ex_id__in"] = ex_ids
|
|
1570
|
-
|
|
1571
|
-
|
|
1591
|
+
# distinct: credexs — m2m, и объява с двумя платёжками (2 credex с ovr_pm_id=0) иначе # noqa: RUF003
|
|
1592
|
+
# приезжает JOIN'ом дважды (дубли строк в /hot/ads?mine=true и двойной ads_share) # noqa: RUF003
|
|
1593
|
+
return (
|
|
1594
|
+
cls.filter(**fltr)
|
|
1595
|
+
.distinct()
|
|
1596
|
+
.prefetch_related("ad__pair_side__pair__coin", "ad__pair_side__pair__cur", "ad__maker__ex")
|
|
1572
1597
|
)
|
|
1573
1598
|
|
|
1574
1599
|
@classmethod
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/a_actor_seed.py
RENAMED
|
File without changes
|
|
File without changes
|
{xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/a_country_seed.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/countries.json
RENAMED
|
File without changes
|
|
File without changes
|
{xync_db-0.5.5.dev9 → xync_db-0.5.5.dev11}/src/xync_db/migrations/models/seed/xync_files.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|