sud-cli 0.1.1__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.
- sud_cli/__init__.py +3 -0
- sud_cli/__main__.py +4 -0
- sud_cli/_wm.py +118 -0
- sud_cli/agenda.py +90 -0
- sud_cli/autofill.py +192 -0
- sud_cli/autofill_markers.json +56 -0
- sud_cli/backup.py +256 -0
- sud_cli/calendar_ics.py +191 -0
- sud_cli/case_diff.py +165 -0
- sud_cli/case_search_bridge.py +1322 -0
- sud_cli/chain.py +226 -0
- sud_cli/cli.py +3721 -0
- sud_cli/completeness.py +105 -0
- sud_cli/config.py +20 -0
- sud_cli/conflict.py +210 -0
- sud_cli/conflicts.py +207 -0
- sud_cli/court_alerts.py +156 -0
- sud_cli/court_source.py +108 -0
- sud_cli/data/completeness_rules.json +18 -0
- sud_cli/data/key_rates.json +59 -0
- sud_cli/data/npa_historical_editions.json +19 -0
- sud_cli/db.py +2072 -0
- sud_cli/deadline_matrix.py +99 -0
- sud_cli/deadline_state.py +25 -0
- sud_cli/deadlines.py +8 -0
- sud_cli/deadlines_state.py +1 -0
- sud_cli/deferral.py +5 -0
- sud_cli/efrsb_manual.py +85 -0
- sud_cli/efrsb_public.py +210 -0
- sud_cli/egrul_nalog.py +150 -0
- sud_cli/explain.py +190 -0
- sud_cli/fees_extra.py +104 -0
- sud_cli/forms.py +204 -0
- sud_cli/fssp_manual.py +91 -0
- sud_cli/fssp_semi.py +285 -0
- sud_cli/fssp_stats.py +105 -0
- sud_cli/government_transfers.json +8 -0
- sud_cli/holidays_rf.json +62 -0
- sud_cli/idempotency.py +78 -0
- sud_cli/importing.py +575 -0
- sud_cli/interest.py +144 -0
- sud_cli/jurisdiction.py +177 -0
- sud_cli/jurisdiction_rules.json +265 -0
- sud_cli/legal_deadlines.py +278 -0
- sud_cli/legal_engine.py +558 -0
- sud_cli/legal_rules.json +1058 -0
- sud_cli/limitation.py +70 -0
- sud_cli/mcp_server.py +1594 -0
- sud_cli/metrics.py +142 -0
- sud_cli/npa_as_of.py +21 -0
- sud_cli/output.py +89 -0
- sud_cli/payment.py +24 -0
- sud_cli/pdn.py +259 -0
- sud_cli/plenum_corpus.py +134 -0
- sud_cli/plenums_seed.json +45 -0
- sud_cli/practice_status.py +136 -0
- sud_cli/pravo_publication.py +361 -0
- sud_cli/pretrial.py +151 -0
- sud_cli/production_calendar.py +233 -0
- sud_cli/providers.py +95 -0
- sud_cli/readiness.py +234 -0
- sud_cli/regional_holidays.py +37 -0
- sud_cli/reminders.py +92 -0
- sud_cli/reports.py +349 -0
- sud_cli/sud_netguard.py +943 -0
- sud_cli/timeline_audit.py +80 -0
- sud_cli/verify.py +201 -0
- sud_cli-0.1.1.dist-info/METADATA +119 -0
- sud_cli-0.1.1.dist-info/RECORD +74 -0
- sud_cli-0.1.1.dist-info/WHEEL +4 -0
- sud_cli-0.1.1.dist-info/entry_points.txt +2 -0
- sud_cli-0.1.1.dist-info/licenses/AUTHORS +5 -0
- sud_cli-0.1.1.dist-info/licenses/LICENSE.md +661 -0
- sud_cli-0.1.1.dist-info/licenses/NOTICE +7 -0
sud_cli/chain.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""the wave §1 (S-12): цепочка инстанций дела поверх VERIFIED deadline_matrix.
|
|
2
|
+
|
|
3
|
+
Границы (правило «тексты только из реестра»):
|
|
4
|
+
- ни одной даты «из воздуха»: точка без якорной базы = due_at=None +
|
|
5
|
+
needs_anchor=True, ровно как в build_matrix (the wave) — движок переиспользуется,
|
|
6
|
+
вторая копия логики якорения не заводится;
|
|
7
|
+
- порядок цепочки — НЕ нормативка, а процессуальная механика (кого за кем
|
|
8
|
+
обжалуют). Он захардкожен константами CHAIN_ORDER_* ПО КЛЮЧАМ матрицы;
|
|
9
|
+
в выводе стоит справочный disclaimer, обязывающий сверяться с нормой;
|
|
10
|
+
- cure-ключ (as_cure_left_without_movement) в цепочку НЕ входит: duration=null,
|
|
11
|
+
он вне эскалации инстанций;
|
|
12
|
+
- ключ, которого нет в матрице этой юрисдикции, не выдумывается: он просто
|
|
13
|
+
отсутствует, а расхождение попадёт в missing_from_matrix.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
18
|
+
|
|
19
|
+
from .deadline_matrix import INSTANCE_TO_JURISDICTION, build_matrix
|
|
20
|
+
|
|
21
|
+
#: Порядок эскалации по реальным ключам матрицы legal_rules.json (v1.4.0).
|
|
22
|
+
#: Арбитраж: решение -> апелляция (ч.1 ст.259 АПК) -> кассация округа
|
|
23
|
+
#: (ч.1 ст.276 АПК). Ключа кассации ВС в матрице нет — он не выдумывается.
|
|
24
|
+
CHAIN_ORDER_ARBITRATION: Tuple[str, ...] = (
|
|
25
|
+
"as_appeal_decision",
|
|
26
|
+
"as_cassation_district",
|
|
27
|
+
)
|
|
28
|
+
#: ГПК: решение -> апелляция (ст.321) -> кассация (ч.1 ст.376.1).
|
|
29
|
+
CHAIN_ORDER_GENERAL: Tuple[str, ...] = (
|
|
30
|
+
"soyu_appeal_decision",
|
|
31
|
+
"soyu_cassation",
|
|
32
|
+
)
|
|
33
|
+
#: КАС: решение -> апелляция (ч.1 ст.298) -> кассация (ч.2 ст.318).
|
|
34
|
+
CHAIN_ORDER_KAS: Tuple[str, ...] = (
|
|
35
|
+
"kas_appeal_decision",
|
|
36
|
+
"kas_cassation",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
CHAIN_ORDER_BY_JURISDICTION: Dict[str, Tuple[str, ...]] = {
|
|
40
|
+
"arbitration": CHAIN_ORDER_ARBITRATION,
|
|
41
|
+
"general_jurisdiction": CHAIN_ORDER_GENERAL,
|
|
42
|
+
"kas": CHAIN_ORDER_KAS,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#: Механическая метка стадии (не норма): поле выводится из порядка ключей.
|
|
46
|
+
STAGE_BY_KEY: Dict[str, str] = {
|
|
47
|
+
"as_appeal_decision": "appeal",
|
|
48
|
+
"as_cassation_district": "cassation_district",
|
|
49
|
+
"soyu_appeal_decision": "appeal",
|
|
50
|
+
"soyu_cassation": "cassation",
|
|
51
|
+
"kas_appeal_decision": "appeal",
|
|
52
|
+
"kas_cassation": "cassation",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#: Инстанции, перечисляемые при неизвестной instance дела.
|
|
56
|
+
KNOWN_INSTANCES: Tuple[str, ...] = ("apk", "gpk", "kas")
|
|
57
|
+
|
|
58
|
+
CHAIN_DISCLAIMER = (
|
|
59
|
+
"Цепочка инстанций справочная: порядок точек — процессуальная механика "
|
|
60
|
+
"sud-cli, а не норма; сроки и основания берутся из VERIFIED "
|
|
61
|
+
"deadline_matrix, даты без якоря не выдумываются. Окончательный порядок "
|
|
62
|
+
"эскалации определяет суд по тексту закона."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
UNKNOWN_INSTANCE_NOTE = (
|
|
66
|
+
"instance дела не задана (case field instance отсутствует) — показаны "
|
|
67
|
+
"цепочки всех юрисдикций матрицы; выберите instance и повторите"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
ANCHOR_HINT = "deadlines anchor <case_id> --key {key} --event-date <YYYY-MM-DD>"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def chain_keys_for_jurisdiction(jurisdiction: str, matrix: dict) -> List[str]:
|
|
74
|
+
"""Порядок цепочки, отфильтрованный по фактическим ключам матрицы.
|
|
75
|
+
|
|
76
|
+
Ключа нет в матрице (или он не той юрисдикции) — в цепочку не попадает:
|
|
77
|
+
выдумывать точку движок не имеет права.
|
|
78
|
+
"""
|
|
79
|
+
order = CHAIN_ORDER_BY_JURISDICTION.get(jurisdiction, ())
|
|
80
|
+
out = []
|
|
81
|
+
for key in order:
|
|
82
|
+
entry = matrix.get(key) or {}
|
|
83
|
+
if entry.get("jurisdiction") == jurisdiction:
|
|
84
|
+
out.append(key)
|
|
85
|
+
return out
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def missing_chain_keys(jurisdiction: str, matrix: dict) -> List[str]:
|
|
89
|
+
"""Ключи порядка, которых нет в матрице этой юрисдикции (пусто = норма)."""
|
|
90
|
+
order = CHAIN_ORDER_BY_JURISDICTION.get(jurisdiction, ())
|
|
91
|
+
present = set(chain_keys_for_jurisdiction(jurisdiction, matrix))
|
|
92
|
+
return [k for k in order if k not in present]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _point_blockers(nxt: Optional[dict]) -> List[str]:
|
|
96
|
+
"""Что блокирует переход дальше — только по фактам о самой точке цели.
|
|
97
|
+
|
|
98
|
+
Здесь нет утверждений «по порядку инстанций следующая дата наступает после
|
|
99
|
+
предыдущей»: матрица не хранит дату решения апелляции, и выводить её из
|
|
100
|
+
соседней точки означало бы выдумать базу. Перечисляются ровно отсутствующие
|
|
101
|
+
данные цели (якорь, состояние, due_at).
|
|
102
|
+
"""
|
|
103
|
+
blocks: List[str] = []
|
|
104
|
+
if nxt is None:
|
|
105
|
+
return blocks
|
|
106
|
+
if nxt.get("needs_anchor"):
|
|
107
|
+
blocks.append(
|
|
108
|
+
f"{nxt['key']}: база MISSING — срок не вычислен, нужен якорь "
|
|
109
|
+
f"({ANCHOR_HINT.format(key=nxt['key'])})"
|
|
110
|
+
)
|
|
111
|
+
if nxt.get("state") is None:
|
|
112
|
+
blocks.append(f"{nxt['key']}: состояние срока не зафиксировано")
|
|
113
|
+
if nxt.get("due_at") is None and not nxt.get("needs_anchor"):
|
|
114
|
+
# база есть, а due_at нет: движок не смог вычислить срок (duration=null,
|
|
115
|
+
# отказ календаря) — это отдельный факт, а не «нужен якорь»
|
|
116
|
+
blocks.append(f"{nxt['key']}: due_at не вычислен движком при наличии базы")
|
|
117
|
+
return blocks
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _ordered_points(instance: str, matrix: dict, points_by_key: dict) -> List[dict]:
|
|
121
|
+
jurisdiction = INSTANCE_TO_JURISDICTION.get(instance)
|
|
122
|
+
chain = chain_keys_for_jurisdiction(jurisdiction, matrix) if jurisdiction else []
|
|
123
|
+
ordered = []
|
|
124
|
+
for i, key in enumerate(chain):
|
|
125
|
+
point = points_by_key.get(key)
|
|
126
|
+
if point is None:
|
|
127
|
+
continue
|
|
128
|
+
nxt = points_by_key.get(chain[i + 1]) if i + 1 < len(chain) else None
|
|
129
|
+
enriched = {
|
|
130
|
+
"key": key,
|
|
131
|
+
"stage": STAGE_BY_KEY.get(key),
|
|
132
|
+
"norm": point.get("norm"),
|
|
133
|
+
"base_event": point.get("base_event"),
|
|
134
|
+
"due_at": point.get("due_at"),
|
|
135
|
+
"state": point.get("state"),
|
|
136
|
+
"needs_anchor": bool(point.get("needs_anchor")),
|
|
137
|
+
"is_last": nxt is None,
|
|
138
|
+
}
|
|
139
|
+
if point.get("warning_basis"):
|
|
140
|
+
enriched["warning_basis"] = point["warning_basis"]
|
|
141
|
+
enriched["blocks_next"] = _point_blockers(nxt)
|
|
142
|
+
ordered.append(enriched)
|
|
143
|
+
return ordered
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
#: Ключи, которые НЕ входят в цепочку эскалации, и почему. Формулировка —
|
|
147
|
+
#: описание трассы, а не норма: движок не утверждает, «как по закону», он
|
|
148
|
+
#: показывает, что этот ключ матрицы не является следующим звеном цепочки.
|
|
149
|
+
_OFF_CHAIN_REASON_MARKERS = (
|
|
150
|
+
("court_order", "трасса судебного приказа: отдельное производство, не эскалация решения"),
|
|
151
|
+
("simplified", "трасса упрощённого производства: своя апелляция, вне цепочки решения"),
|
|
152
|
+
("ruling", "обжалование определения: отдельная трасса, не звено цепочки по решению"),
|
|
153
|
+
("cure", "cure-точка (left without movement): duration=null, вне эскалации"),
|
|
154
|
+
("shortened_special", "специальный сокращённый срок: duration=null, назначает суд"),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def off_chain_keys(jurisdiction: str, matrix: dict) -> List[dict]:
|
|
159
|
+
"""Ключи матрицы этой юрисдикции, оставшиеся вне цепочки, с причиной.
|
|
160
|
+
|
|
161
|
+
Нужно, чтобы «только ключи цепочки» не читалось как «остальное потеряно»:
|
|
162
|
+
список полный, порядок — по ключу, причина — из морфологии ключа и duration.
|
|
163
|
+
"""
|
|
164
|
+
in_chain = set(chain_keys_for_jurisdiction(jurisdiction, matrix))
|
|
165
|
+
out = []
|
|
166
|
+
for key in sorted(matrix):
|
|
167
|
+
entry = matrix.get(key) or {}
|
|
168
|
+
if entry.get("jurisdiction") != jurisdiction or key in in_chain:
|
|
169
|
+
continue
|
|
170
|
+
reason = "не является звеном цепочки эскалации решения"
|
|
171
|
+
for marker, text in _OFF_CHAIN_REASON_MARKERS:
|
|
172
|
+
if marker in key:
|
|
173
|
+
reason = text
|
|
174
|
+
break
|
|
175
|
+
if entry.get("duration") is None and "duration=null" not in reason:
|
|
176
|
+
reason = f"{reason}; duration=null — срок назначает суд"
|
|
177
|
+
out.append({"key": key, "norm": entry.get("legal_basis"), "reason": reason})
|
|
178
|
+
return out
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def build_chain(
|
|
182
|
+
state_dir: Any,
|
|
183
|
+
case_id: str,
|
|
184
|
+
instance_override: Optional[str] = None,
|
|
185
|
+
rules: Optional[dict] = None,
|
|
186
|
+
) -> Dict[str, Any]:
|
|
187
|
+
"""Упорядоченная цепочка инстанций дела (или всех юрисдикций при unknown)."""
|
|
188
|
+
instance, points = build_matrix(
|
|
189
|
+
state_dir, case_id, instance_override=instance_override, rules=rules
|
|
190
|
+
)
|
|
191
|
+
from .legal_engine import load_rules
|
|
192
|
+
|
|
193
|
+
matrix = (rules or load_rules()).get("deadline_matrix", {}) or {}
|
|
194
|
+
by_key = {p["key"]: p for p in points}
|
|
195
|
+
|
|
196
|
+
payload: Dict[str, Any] = {
|
|
197
|
+
"case_id": case_id,
|
|
198
|
+
"instance": instance,
|
|
199
|
+
"chain": [],
|
|
200
|
+
"chains_by_jurisdiction": [],
|
|
201
|
+
"missing_from_matrix": [],
|
|
202
|
+
"off_chain_keys": [],
|
|
203
|
+
"disclaimer": CHAIN_DISCLAIMER,
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if instance in INSTANCE_TO_JURISDICTION:
|
|
207
|
+
jurisdiction = INSTANCE_TO_JURISDICTION[instance]
|
|
208
|
+
payload["jurisdiction"] = jurisdiction
|
|
209
|
+
payload["chain"] = _ordered_points(instance, matrix, by_key)
|
|
210
|
+
payload["missing_from_matrix"] = missing_chain_keys(jurisdiction, matrix)
|
|
211
|
+
payload["off_chain_keys"] = off_chain_keys(jurisdiction, matrix)
|
|
212
|
+
else:
|
|
213
|
+
# unknown instance: цепочка каждой юрисдикции матрицы, с пометкой.
|
|
214
|
+
payload["note"] = UNKNOWN_INSTANCE_NOTE
|
|
215
|
+
for inst in KNOWN_INSTANCES:
|
|
216
|
+
juris = INSTANCE_TO_JURISDICTION[inst]
|
|
217
|
+
payload["chains_by_jurisdiction"].append(
|
|
218
|
+
{
|
|
219
|
+
"instance": inst,
|
|
220
|
+
"jurisdiction": juris,
|
|
221
|
+
"chain": _ordered_points(inst, matrix, by_key),
|
|
222
|
+
"missing_from_matrix": missing_chain_keys(juris, matrix),
|
|
223
|
+
"off_chain_keys": off_chain_keys(juris, matrix),
|
|
224
|
+
}
|
|
225
|
+
)
|
|
226
|
+
return payload
|