feat: ж/д слой + cant_swim в скоринг + профили вне модели

Определение специфических рекомендаций (матрица профилей §8):

1. Ж/д слой (закрыт мёртвый railway ×2.5 у РАС):
   - /api/v1/water/{case_id} отдаёт railway=rail как LineString
     (без service/industrial/military веток), кэш общий v2;
   - railway_warning «перекрыть/проверить немедленно» по профилям;
   - SearchMap: Polyline слой ж/д (тёмно-красный), счётчики 💧/🚂.

2. cant_swim → профиль не_умеет_плавать (water ×3.0, без изменения
   радиуса, critical_warning «обследовать водоёмы НЕМЕДЛЕННО»):
   - раньше чекбокс влиял только на текст, в скоринге был пробел;
   - derive в analyze._derive_profiles — работает и для closed_cases.

3. unmodeled_profiles: ДЦП/слабое зрение/слух — честная пометка
   «вне поведенческой модели» с пояснением (vector_tasks B12:
   профили без аналога не выдавать за учтённые); блок на фронте
   в карточке здоровья.

Площадь воды: сферический эксцесс, проверен на квадрате 53° (744017 м²
vs 743272 точного). Тесты: 202 passed (новый test_cant_swim_profile).
This commit is contained in:
2026-09-09 13:12:55 +03:00
parent 98e8e58023
commit f20080305d
2069 changed files with 803865 additions and 97 deletions
@@ -0,0 +1,90 @@
# cyextension/util.pyx
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
from collections.abc import Mapping
from sqlalchemy import exc
cdef tuple _Empty_Tuple = ()
cdef inline bint _is_mapping_or_tuple(object value):
return isinstance(value, dict) or isinstance(value, tuple) or isinstance(value, Mapping)
cdef inline bint _is_mapping(object value):
return isinstance(value, dict) or isinstance(value, Mapping)
def _distill_params_20(object params):
if params is None:
return _Empty_Tuple
elif isinstance(params, list) or isinstance(params, tuple):
if params and not _is_mapping(params[0]):
raise exc.ArgumentError(
"List argument must consist only of dictionaries"
)
return params
elif _is_mapping(params):
return [params]
else:
raise exc.ArgumentError("mapping or list expected for parameters")
def _distill_raw_params(object params):
if params is None:
return _Empty_Tuple
elif isinstance(params, list):
if params and not _is_mapping_or_tuple(params[0]):
raise exc.ArgumentError(
"List argument must consist only of tuples or dictionaries"
)
return params
elif _is_mapping_or_tuple(params):
return [params]
else:
raise exc.ArgumentError("mapping or sequence expected for parameters")
cdef class prefix_anon_map(dict):
def __missing__(self, str key):
cdef str derived
cdef int anonymous_counter
cdef dict self_dict = self
derived = key.split(" ", 1)[1]
anonymous_counter = self_dict.get(derived, 1)
self_dict[derived] = anonymous_counter + 1
value = f"{derived}_{anonymous_counter}"
self_dict[key] = value
return value
cdef class cache_anon_map(dict):
cdef int _index
def __init__(self):
self._index = 0
def get_anon(self, obj):
cdef long long idself
cdef str id_
cdef dict self_dict = self
idself = id(obj)
if idself in self_dict:
return self_dict[idself], True
else:
id_ = self.__missing__(idself)
return id_, False
def __missing__(self, key):
cdef str val
cdef dict self_dict = self
self_dict[key] = val = str(self._index)
self._index += 1
return val