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.
Files changed (74) hide show
  1. sud_cli/__init__.py +3 -0
  2. sud_cli/__main__.py +4 -0
  3. sud_cli/_wm.py +118 -0
  4. sud_cli/agenda.py +90 -0
  5. sud_cli/autofill.py +192 -0
  6. sud_cli/autofill_markers.json +56 -0
  7. sud_cli/backup.py +256 -0
  8. sud_cli/calendar_ics.py +191 -0
  9. sud_cli/case_diff.py +165 -0
  10. sud_cli/case_search_bridge.py +1322 -0
  11. sud_cli/chain.py +226 -0
  12. sud_cli/cli.py +3721 -0
  13. sud_cli/completeness.py +105 -0
  14. sud_cli/config.py +20 -0
  15. sud_cli/conflict.py +210 -0
  16. sud_cli/conflicts.py +207 -0
  17. sud_cli/court_alerts.py +156 -0
  18. sud_cli/court_source.py +108 -0
  19. sud_cli/data/completeness_rules.json +18 -0
  20. sud_cli/data/key_rates.json +59 -0
  21. sud_cli/data/npa_historical_editions.json +19 -0
  22. sud_cli/db.py +2072 -0
  23. sud_cli/deadline_matrix.py +99 -0
  24. sud_cli/deadline_state.py +25 -0
  25. sud_cli/deadlines.py +8 -0
  26. sud_cli/deadlines_state.py +1 -0
  27. sud_cli/deferral.py +5 -0
  28. sud_cli/efrsb_manual.py +85 -0
  29. sud_cli/efrsb_public.py +210 -0
  30. sud_cli/egrul_nalog.py +150 -0
  31. sud_cli/explain.py +190 -0
  32. sud_cli/fees_extra.py +104 -0
  33. sud_cli/forms.py +204 -0
  34. sud_cli/fssp_manual.py +91 -0
  35. sud_cli/fssp_semi.py +285 -0
  36. sud_cli/fssp_stats.py +105 -0
  37. sud_cli/government_transfers.json +8 -0
  38. sud_cli/holidays_rf.json +62 -0
  39. sud_cli/idempotency.py +78 -0
  40. sud_cli/importing.py +575 -0
  41. sud_cli/interest.py +144 -0
  42. sud_cli/jurisdiction.py +177 -0
  43. sud_cli/jurisdiction_rules.json +265 -0
  44. sud_cli/legal_deadlines.py +278 -0
  45. sud_cli/legal_engine.py +558 -0
  46. sud_cli/legal_rules.json +1058 -0
  47. sud_cli/limitation.py +70 -0
  48. sud_cli/mcp_server.py +1594 -0
  49. sud_cli/metrics.py +142 -0
  50. sud_cli/npa_as_of.py +21 -0
  51. sud_cli/output.py +89 -0
  52. sud_cli/payment.py +24 -0
  53. sud_cli/pdn.py +259 -0
  54. sud_cli/plenum_corpus.py +134 -0
  55. sud_cli/plenums_seed.json +45 -0
  56. sud_cli/practice_status.py +136 -0
  57. sud_cli/pravo_publication.py +361 -0
  58. sud_cli/pretrial.py +151 -0
  59. sud_cli/production_calendar.py +233 -0
  60. sud_cli/providers.py +95 -0
  61. sud_cli/readiness.py +234 -0
  62. sud_cli/regional_holidays.py +37 -0
  63. sud_cli/reminders.py +92 -0
  64. sud_cli/reports.py +349 -0
  65. sud_cli/sud_netguard.py +943 -0
  66. sud_cli/timeline_audit.py +80 -0
  67. sud_cli/verify.py +201 -0
  68. sud_cli-0.1.1.dist-info/METADATA +119 -0
  69. sud_cli-0.1.1.dist-info/RECORD +74 -0
  70. sud_cli-0.1.1.dist-info/WHEEL +4 -0
  71. sud_cli-0.1.1.dist-info/entry_points.txt +2 -0
  72. sud_cli-0.1.1.dist-info/licenses/AUTHORS +5 -0
  73. sud_cli-0.1.1.dist-info/licenses/LICENSE.md +661 -0
  74. 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