Improve topic sorting: add popular sorting by publications and authors count

This commit is contained in:
2025-06-02 02:56:11 +03:00
parent baca19a4d5
commit 3327976586
113 changed files with 7238 additions and 3739 deletions

View File

@@ -1,28 +1,118 @@
from decimal import Decimal
from json import JSONEncoder
"""
JSON encoders and utilities
"""
import datetime
import decimal
from typing import Any, Union
import orjson
class CustomJSONEncoder(JSONEncoder):
def default_json_encoder(obj: Any) -> Any:
"""
Расширенный JSON энкодер с поддержкой сериализации объектов SQLAlchemy.
Default JSON encoder для объектов, которые не поддерживаются стандартным JSON
Примеры:
>>> import json
>>> from decimal import Decimal
>>> from orm.topic import Topic
>>> json.dumps(Decimal("10.50"), cls=CustomJSONEncoder)
'"10.50"'
>>> topic = Topic(id=1, slug="test")
>>> json.dumps(topic, cls=CustomJSONEncoder)
'{"id": 1, "slug": "test", ...}'
Args:
obj: Объект для сериализации
Returns:
Сериализуемое представление объекта
Raises:
TypeError: Если объект не может быть сериализован
"""
if hasattr(obj, "dict") and callable(obj.dict):
return obj.dict()
if hasattr(obj, "__dict__"):
return obj.__dict__
if isinstance(obj, (datetime.datetime, datetime.date, datetime.time)):
return obj.isoformat()
if isinstance(obj, decimal.Decimal):
return float(obj)
if hasattr(obj, "__json__"):
return obj.__json__()
msg = f"Object of type {type(obj)} is not JSON serializable"
raise TypeError(msg)
def default(self, obj):
if isinstance(obj, Decimal):
return str(obj)
# Проверяем, есть ли у объекта метод dict() (как у моделей SQLAlchemy)
if hasattr(obj, "dict") and callable(obj.dict):
return obj.dict()
def orjson_dumps(obj: Any, **kwargs: Any) -> bytes:
"""
Сериализует объект в JSON с помощью orjson
return super().default(obj)
Args:
obj: Объект для сериализации
**kwargs: Дополнительные параметры для orjson.dumps
Returns:
bytes: JSON в виде байтов
"""
# Используем правильную константу для orjson
option_flags = orjson.OPT_SERIALIZE_DATACLASS
if kwargs.get("indent"):
option_flags |= orjson.OPT_INDENT_2
return orjson.dumps(obj, default=default_json_encoder, option=option_flags)
def orjson_loads(data: Union[str, bytes]) -> Any:
"""
Десериализует JSON с помощью orjson
Args:
data: JSON данные в виде строки или байтов
Returns:
Десериализованный объект
"""
return orjson.loads(data)
class JSONEncoder:
"""Кастомный JSON кодировщик на основе orjson"""
@staticmethod
def encode(obj: Any) -> str:
"""Encode object to JSON string"""
return orjson_dumps(obj).decode("utf-8")
@staticmethod
def decode(data: Union[str, bytes]) -> Any:
"""Decode JSON string to object"""
return orjson_loads(data)
# Создаем экземпляр для обратной совместимости
CustomJSONEncoder = JSONEncoder()
def fast_json_dumps(obj: Any, indent: bool = False) -> str:
"""
Быстрая сериализация JSON
Args:
obj: Объект для сериализации
indent: Форматировать с отступами
Returns:
JSON строка
"""
return orjson_dumps(obj, indent=indent).decode("utf-8")
def fast_json_loads(data: Union[str, bytes]) -> Any:
"""
Быстрая десериализация JSON
Args:
data: JSON данные
Returns:
Десериализованный объект
"""
return orjson_loads(data)
# Экспортируем для удобства
dumps = fast_json_dumps
loads = fast_json_loads