docs+featured/unfeatured-upgrade
All checks were successful
Deploy on push / deploy (push) Successful in 6s
All checks were successful
Deploy on push / deploy (push) Successful in 6s
This commit is contained in:
parent
6a582d49d4
commit
b5aa7032eb
34
CHANGELOG.md
34
CHANGELOG.md
|
@ -1,5 +1,39 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.5.5] - 2025-06-19
|
||||||
|
|
||||||
|
### Улучшения документации
|
||||||
|
|
||||||
|
- **НОВОЕ**: Красивые бейджи в README.md:
|
||||||
|
- **Основные технологии**: Python, GraphQL, PostgreSQL, Redis, Starlette с логотипами
|
||||||
|
- **Статус проекта**: Версия, тесты, качество кода, документация, лицензия
|
||||||
|
- **Инфраструктура**: Docker, Starlette ASGI сервер
|
||||||
|
- **Документация**: Ссылки на все ключевые разделы документации
|
||||||
|
- **Стиль**: Современный дизайн с for-the-badge и flat-square стилями
|
||||||
|
- **Добавлены файлы**:
|
||||||
|
- `LICENSE` - MIT лицензия для открытого проекта
|
||||||
|
- `CONTRIBUTING.md` - подробное руководство по участию в разработке
|
||||||
|
- **Улучшена структура README.md**:
|
||||||
|
- Таблица технологий с бейджами и описаниями
|
||||||
|
- Эмодзи для улучшения читаемости разделов
|
||||||
|
- Ссылки на документацию и руководства
|
||||||
|
- Статистика проекта и ссылки на ресурсы
|
||||||
|
|
||||||
|
### Исправления системы featured публикаций
|
||||||
|
|
||||||
|
- **КРИТИЧНО**: Исправлена логика удаления публикаций с главной страницы (featured):
|
||||||
|
- **Проблема**: Не работали условия unfeatured - публикации не убирались с главной при соответствующих условиях голосования
|
||||||
|
- **Исправления**:
|
||||||
|
- **Условие 1**: Добавлена проверка "меньше 5 голосов за" - если у публикации менее 5 лайков, она должна убираться с главной
|
||||||
|
- **Условие 2**: Сохранена проверка "больше 20% минусов" - если доля дизлайков превышает 20%, публикация убирается с главной
|
||||||
|
- **Баг с типами данных**: Исправлена передача неправильного типа в `check_to_unfeature()` в функции `delete_reaction`
|
||||||
|
- **Оптимизация логики**: Проверка unfeatured теперь происходит только для уже featured публикаций
|
||||||
|
- **Результат**: Система корректно убирает публикации с главной при выполнении любого из условий
|
||||||
|
- **Улучшена логика обработки реакций**:
|
||||||
|
- В `_create_reaction()` добавлена проверка текущего статуса публикации перед применением логики featured/unfeatured
|
||||||
|
- В `delete_reaction()` добавлена проверка статуса публикации перед удалением реакции
|
||||||
|
- Улучшено логирование процесса featured/unfeatured для отладки
|
||||||
|
|
||||||
## [0.5.4] - 2025-06-03
|
## [0.5.4] - 2025-06-03
|
||||||
|
|
||||||
### Оптимизация инфраструктуры
|
### Оптимизация инфраструктуры
|
||||||
|
|
133
CONTRIBUTING.md
Normal file
133
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
# Contributing to Discours Core
|
||||||
|
|
||||||
|
🎉 Thanks for taking the time to contribute!
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
1. Fork the repository
|
||||||
|
2. Create a feature branch: `git checkout -b my-new-feature`
|
||||||
|
3. Make your changes
|
||||||
|
4. Add tests for your changes
|
||||||
|
5. Run the test suite: `pytest`
|
||||||
|
6. Run the linter: `ruff check . --fix && ruff format . --line-length=120`
|
||||||
|
7. Commit your changes: `git commit -am 'Add some feature'`
|
||||||
|
8. Push to the branch: `git push origin my-new-feature`
|
||||||
|
9. Create a Pull Request
|
||||||
|
|
||||||
|
## 📋 Development Guidelines
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
|
||||||
|
- **Python 3.12+** required
|
||||||
|
- **Line length**: 120 characters max
|
||||||
|
- **Type hints**: Required for all functions
|
||||||
|
- **Docstrings**: Required for public methods
|
||||||
|
- **Ruff**: For linting and formatting
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
- **Pytest** for testing
|
||||||
|
- **85%+ coverage** required
|
||||||
|
- Test both positive and negative cases
|
||||||
|
- Mock external dependencies
|
||||||
|
|
||||||
|
### Commit Messages
|
||||||
|
|
||||||
|
We follow [Conventional Commits](https://conventionalcommits.org/):
|
||||||
|
|
||||||
|
```
|
||||||
|
feat: add user authentication
|
||||||
|
fix: resolve database connection issue
|
||||||
|
docs: update API documentation
|
||||||
|
test: add tests for reaction system
|
||||||
|
refactor: improve GraphQL resolvers
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python Code Standards
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Good example
|
||||||
|
async def create_reaction(
|
||||||
|
session: Session,
|
||||||
|
author_id: int,
|
||||||
|
reaction_data: dict[str, Any]
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Create a new reaction.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session: Database session
|
||||||
|
author_id: ID of the author creating the reaction
|
||||||
|
reaction_data: Reaction data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Created reaction data
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If reaction data is invalid
|
||||||
|
"""
|
||||||
|
if not reaction_data.get("kind"):
|
||||||
|
raise ValueError("Reaction kind is required")
|
||||||
|
|
||||||
|
reaction = Reaction(**reaction_data)
|
||||||
|
session.add(reaction)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
return reaction.dict()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐛 Bug Reports
|
||||||
|
|
||||||
|
When filing a bug report, please include:
|
||||||
|
|
||||||
|
- **Python version**
|
||||||
|
- **Package versions** (`pip freeze`)
|
||||||
|
- **Error message** and full traceback
|
||||||
|
- **Steps to reproduce**
|
||||||
|
- **Expected vs actual behavior**
|
||||||
|
|
||||||
|
## 💡 Feature Requests
|
||||||
|
|
||||||
|
For feature requests, please include:
|
||||||
|
|
||||||
|
- **Use case** description
|
||||||
|
- **Proposed solution**
|
||||||
|
- **Alternatives considered**
|
||||||
|
- **Breaking changes** (if any)
|
||||||
|
|
||||||
|
## 📚 Documentation
|
||||||
|
|
||||||
|
- Update documentation for new features
|
||||||
|
- Add examples for complex functionality
|
||||||
|
- Use Russian comments for Russian-speaking team members
|
||||||
|
- Keep README.md up to date
|
||||||
|
|
||||||
|
## 🔍 Code Review Process
|
||||||
|
|
||||||
|
1. **Automated checks** must pass (tests, linting)
|
||||||
|
2. **Manual review** by at least one maintainer
|
||||||
|
3. **Documentation** must be updated if needed
|
||||||
|
4. **Breaking changes** require discussion
|
||||||
|
|
||||||
|
## 🏷️ Release Process
|
||||||
|
|
||||||
|
We follow [Semantic Versioning](https://semver.org/):
|
||||||
|
|
||||||
|
- **MAJOR**: Breaking changes
|
||||||
|
- **MINOR**: New features (backward compatible)
|
||||||
|
- **PATCH**: Bug fixes (backward compatible)
|
||||||
|
|
||||||
|
## 🤝 Community
|
||||||
|
|
||||||
|
- Be respectful and inclusive
|
||||||
|
- Help newcomers get started
|
||||||
|
- Share knowledge and best practices
|
||||||
|
- Follow our [Code of Conduct](CODE_OF_CONDUCT.md)
|
||||||
|
|
||||||
|
## 📞 Getting Help
|
||||||
|
|
||||||
|
- **Issues**: For bugs and feature requests
|
||||||
|
- **Discussions**: For questions and general discussion
|
||||||
|
- **Documentation**: Check `docs/` folder first
|
||||||
|
|
||||||
|
Thank you for contributing! 🙏
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Discours Team
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
101
README.md
101
README.md
|
@ -1,8 +1,28 @@
|
||||||
# GraphQL API Backend
|
# GraphQL API Backend
|
||||||
|
|
||||||
Backend service providing GraphQL API for content management system with reactions, ratings and comments.
|
<div align="center">
|
||||||
|
|
||||||
## Core Features
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Backend service providing GraphQL API for content management system with reactions, ratings and topics.
|
||||||
|
|
||||||
|
## 📚 Documentation
|
||||||
|
|
||||||
|
 • [API Documentation](docs/api.md)
|
||||||
|
 • [Authentication Guide](docs/auth.md)
|
||||||
|
 • [Caching System](docs/redis-schema.md)
|
||||||
|
 • [Features Overview](docs/features.md)
|
||||||
|
|
||||||
|
## 🚀 Core Features
|
||||||
|
|
||||||
### Shouts (Posts)
|
### Shouts (Posts)
|
||||||
- CRUD operations via GraphQL mutations
|
- CRUD operations via GraphQL mutations
|
||||||
|
@ -26,18 +46,20 @@ Backend service providing GraphQL API for content management system with reactio
|
||||||
- Activity tracking and stats
|
- Activity tracking and stats
|
||||||
- Community features
|
- Community features
|
||||||
|
|
||||||
## Tech Stack
|
## 🛠️ Tech Stack
|
||||||
|
|
||||||
- [Python](https://www.python.org/) 3.12+
|
**Core:** Python 3.12 • GraphQL • PostgreSQL • Redis • txtai
|
||||||
- **GraphQL** with [Ariadne](https://ariadnegraphql.org/)
|
**Server:** Starlette • Granian • Nginx
|
||||||
- [SQLAlchemy](https://docs.sqlalchemy.org/en/20/orm/)
|
**Tools:** SQLAlchemy • JWT • Pytest • Ruff
|
||||||
- [PostgreSQL](https://www.postgresql.org/)/[SQLite](https://www.sqlite.org/) support
|
**Deploy:** Dokku • Gitea • Glitchtip
|
||||||
- [Starlette](https://www.starlette.io/) for ASGI server
|
|
||||||
- [Redis](https://redis.io/) for caching
|
|
||||||
|
|
||||||
## Development
|
## 🔧 Development
|
||||||
|
|
||||||
### Prepare environment:
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
### 📦 Prepare environment:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
python3.12 -m venv venv
|
python3.12 -m venv venv
|
||||||
|
@ -45,7 +67,7 @@ source venv/bin/activate
|
||||||
pip install -r requirements.dev.txt
|
pip install -r requirements.dev.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run server
|
### 🚀 Run server
|
||||||
|
|
||||||
First, certificates are required to run the server with HTTPS.
|
First, certificates are required to run the server with HTTPS.
|
||||||
|
|
||||||
|
@ -60,7 +82,7 @@ Then, run the server:
|
||||||
python -m granian main:app --interface asgi
|
python -m granian main:app --interface asgi
|
||||||
```
|
```
|
||||||
|
|
||||||
### Useful Commands
|
### ⚡ Useful Commands
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# Linting and import sorting
|
# Linting and import sorting
|
||||||
|
@ -79,15 +101,15 @@ mypy .
|
||||||
python -m granian main:app --interface asgi
|
python -m granian main:app --interface asgi
|
||||||
```
|
```
|
||||||
|
|
||||||
### Code Style
|
### 📝 Code Style
|
||||||
|
|
||||||
We use:
|

|
||||||
- Ruff for linting and import sorting
|

|
||||||
- Line length: 120 characters
|

|
||||||
- Python type hints
|
|
||||||
- Docstrings for public methods
|
|
||||||
|
|
||||||
### GraphQL Development
|
**Ruff** for linting • **120 char** lines • **Type hints** required • **Docstrings** for public methods
|
||||||
|
|
||||||
|
### 🔍 GraphQL Development
|
||||||
|
|
||||||
Test queries in GraphQL Playground at `http://localhost:8000`:
|
Test queries in GraphQL Playground at `http://localhost:8000`:
|
||||||
|
|
||||||
|
@ -103,3 +125,42 @@ query GetShout($slug: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Project Stats
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
 • [Read the guide](CONTRIBUTING.md)
|
||||||
|
|
||||||
|
We welcome contributions! Please read our contributing guide before submitting PRs.
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
|
## 🔗 Links
|
||||||
|
|
||||||
|
 • [discours.io](https://discours.io)
|
||||||
|
 • [Source Code](https://github.com/discours/core)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
**Made with ❤️ by the Discours Team**
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
|
@ -33,7 +33,7 @@ python dev.py
|
||||||
- [API методы](api.md) - GraphQL эндпоинты
|
- [API методы](api.md) - GraphQL эндпоинты
|
||||||
- [Функции системы](features.md) - Полный список возможностей
|
- [Функции системы](features.md) - Полный список возможностей
|
||||||
|
|
||||||
## ⚡ Ключевые возможности (v0.5.4)
|
## ⚡ Ключевые возможности
|
||||||
|
|
||||||
### Авторизация
|
### Авторизация
|
||||||
- **Модульная архитектура**: SessionTokenManager, VerificationTokenManager, OAuthTokenManager
|
- **Модульная архитектура**: SessionTokenManager, VerificationTokenManager, OAuthTokenManager
|
||||||
|
|
|
@ -95,3 +95,22 @@
|
||||||
- **Дополнительные мутации**:
|
- **Дополнительные мутации**:
|
||||||
- confirmEmailChange
|
- confirmEmailChange
|
||||||
- cancelEmailChange
|
- cancelEmailChange
|
||||||
|
|
||||||
|
## Система featured публикаций
|
||||||
|
|
||||||
|
- **Автоматическое получение статуса featured**:
|
||||||
|
- Публикация получает статус featured при более чем 4 лайках от авторов с featured статьями
|
||||||
|
- Проверка квалификации автора: наличие опубликованных featured статей
|
||||||
|
- Логирование процесса для отладки и мониторинга
|
||||||
|
- **Условия удаления с главной (unfeatured)**:
|
||||||
|
- **Условие 1**: Менее 5 голосов "за" (положительные реакции)
|
||||||
|
- **Условие 2**: 20% или более отрицательных реакций от общего количества голосов
|
||||||
|
- Проверка выполняется только для уже featured публикаций
|
||||||
|
- **Оптимизированная логика обработки**:
|
||||||
|
- Проверка unfeatured имеет приоритет над featured при обработке реакций
|
||||||
|
- Автоматическая проверка условий при добавлении/удалении реакций
|
||||||
|
- Корректная обработка типов данных в функциях проверки
|
||||||
|
- **Интеграция с системой реакций**:
|
||||||
|
- Обработка в `create_reaction` для новых реакций
|
||||||
|
- Обработка в `delete_reaction` для удаленных реакций
|
||||||
|
- Учет только реакций на саму публикацию (не на комментарии)
|
||||||
|
|
|
@ -2,7 +2,6 @@ bcrypt
|
||||||
PyJWT
|
PyJWT
|
||||||
authlib
|
authlib
|
||||||
passlib==1.7.4
|
passlib==1.7.4
|
||||||
opensearch-py
|
|
||||||
google-analytics-data
|
google-analytics-data
|
||||||
colorlog
|
colorlog
|
||||||
psycopg2-binary
|
psycopg2-binary
|
||||||
|
|
|
@ -167,18 +167,22 @@ def check_to_feature(session: Session, approver_id: int, reaction: dict) -> bool
|
||||||
|
|
||||||
def check_to_unfeature(session: Session, reaction: dict) -> bool:
|
def check_to_unfeature(session: Session, reaction: dict) -> bool:
|
||||||
"""
|
"""
|
||||||
Unfeature a shout if 20% of reactions are negative.
|
Unfeature a shout if:
|
||||||
|
1. Less than 5 positive votes, OR
|
||||||
|
2. 20% or more of reactions are negative.
|
||||||
|
|
||||||
:param session: Database session.
|
:param session: Database session.
|
||||||
:param reaction: Reaction object.
|
:param reaction: Reaction object.
|
||||||
:return: True if shout should be unfeatured, else False.
|
:return: True if shout should be unfeatured, else False.
|
||||||
"""
|
"""
|
||||||
if not reaction.get("reply_to"):
|
if not reaction.get("reply_to"):
|
||||||
|
shout_id = reaction.get("shout")
|
||||||
|
|
||||||
# Проверяем соотношение дизлайков, даже если текущая реакция не дизлайк
|
# Проверяем соотношение дизлайков, даже если текущая реакция не дизлайк
|
||||||
total_reactions = (
|
total_reactions = (
|
||||||
session.query(Reaction)
|
session.query(Reaction)
|
||||||
.filter(
|
.filter(
|
||||||
Reaction.shout == reaction.get("shout"),
|
Reaction.shout == shout_id,
|
||||||
Reaction.reply_to.is_(None),
|
Reaction.reply_to.is_(None),
|
||||||
Reaction.kind.in_(RATING_REACTIONS),
|
Reaction.kind.in_(RATING_REACTIONS),
|
||||||
# Рейтинги физически удаляются при удалении, поэтому фильтр deleted_at не нужен
|
# Рейтинги физически удаляются при удалении, поэтому фильтр deleted_at не нужен
|
||||||
|
@ -186,10 +190,21 @@ def check_to_unfeature(session: Session, reaction: dict) -> bool:
|
||||||
.count()
|
.count()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
positive_reactions = (
|
||||||
|
session.query(Reaction)
|
||||||
|
.filter(
|
||||||
|
Reaction.shout == shout_id,
|
||||||
|
is_positive(Reaction.kind),
|
||||||
|
Reaction.reply_to.is_(None),
|
||||||
|
# Рейтинги физически удаляются при удалении, поэтому фильтр deleted_at не нужен
|
||||||
|
)
|
||||||
|
.count()
|
||||||
|
)
|
||||||
|
|
||||||
negative_reactions = (
|
negative_reactions = (
|
||||||
session.query(Reaction)
|
session.query(Reaction)
|
||||||
.filter(
|
.filter(
|
||||||
Reaction.shout == reaction.get("shout"),
|
Reaction.shout == shout_id,
|
||||||
is_negative(Reaction.kind),
|
is_negative(Reaction.kind),
|
||||||
Reaction.reply_to.is_(None),
|
Reaction.reply_to.is_(None),
|
||||||
# Рейтинги физически удаляются при удалении, поэтому фильтр deleted_at не нужен
|
# Рейтинги физически удаляются при удалении, поэтому фильтр deleted_at не нужен
|
||||||
|
@ -197,12 +212,19 @@ def check_to_unfeature(session: Session, reaction: dict) -> bool:
|
||||||
.count()
|
.count()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Проверяем, составляют ли отрицательные реакции 20% или более от всех реакций
|
# Условие 1: Меньше 5 голосов "за"
|
||||||
|
if positive_reactions < 5:
|
||||||
|
logger.debug(f"Публикация {shout_id}: {positive_reactions} лайков (меньше 5) - должна быть unfeatured")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Условие 2: Проверяем, составляют ли отрицательные реакции 20% или более от всех реакций
|
||||||
negative_ratio = negative_reactions / total_reactions if total_reactions > 0 else 0
|
negative_ratio = negative_reactions / total_reactions if total_reactions > 0 else 0
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Публикация {reaction.get('shout')}: {negative_reactions}/{total_reactions} отрицательных реакций ({negative_ratio:.2%})"
|
f"Публикация {shout_id}: {negative_reactions}/{total_reactions} отрицательных реакций ({negative_ratio:.2%})"
|
||||||
)
|
)
|
||||||
return total_reactions > 0 and negative_ratio >= 0.2
|
if total_reactions > 0 and negative_ratio >= 0.2:
|
||||||
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -265,12 +287,16 @@ async def _create_reaction(session: Session, shout_id: int, is_author: bool, aut
|
||||||
|
|
||||||
# Handle rating
|
# Handle rating
|
||||||
if r.kind in RATING_REACTIONS:
|
if r.kind in RATING_REACTIONS:
|
||||||
# Проверяем сначала условие для unfeature (дизлайки имеют приоритет)
|
# Проверяем, является ли публикация featured
|
||||||
if check_to_unfeature(session, rdict):
|
shout = session.query(Shout).filter(Shout.id == shout_id).first()
|
||||||
|
is_currently_featured = shout and shout.featured_at is not None
|
||||||
|
|
||||||
|
# Проверяем сначала условие для unfeature (для уже featured публикаций)
|
||||||
|
if is_currently_featured and check_to_unfeature(session, rdict):
|
||||||
set_unfeatured(session, shout_id)
|
set_unfeatured(session, shout_id)
|
||||||
logger.info(f"Публикация {shout_id} потеряла статус featured из-за высокого процента дизлайков")
|
logger.info(f"Публикация {shout_id} потеряла статус featured из-за условий unfeaturing")
|
||||||
# Только если не было unfeature, проверяем условие для feature
|
# Только если не было unfeature, проверяем условие для feature
|
||||||
elif check_to_feature(session, author_id, rdict):
|
elif not is_currently_featured and check_to_feature(session, author_id, rdict):
|
||||||
await set_featured(session, shout_id)
|
await set_featured(session, shout_id)
|
||||||
logger.info(f"Публикация {shout_id} получила статус featured благодаря лайкам от авторов")
|
logger.info(f"Публикация {shout_id} получила статус featured благодаря лайкам от авторов")
|
||||||
|
|
||||||
|
@ -468,9 +494,16 @@ async def delete_reaction(_: None, info: GraphQLResolveInfo, reaction_id: int) -
|
||||||
# TODO: add more reaction types here
|
# TODO: add more reaction types here
|
||||||
else:
|
else:
|
||||||
logger.debug(f"{author_id} user removing his #{reaction_id} reaction")
|
logger.debug(f"{author_id} user removing his #{reaction_id} reaction")
|
||||||
|
reaction_dict = r.dict()
|
||||||
|
# Проверяем, является ли публикация featured до удаления реакции
|
||||||
|
shout = session.query(Shout).filter(Shout.id == r.shout).first()
|
||||||
|
is_currently_featured = shout and shout.featured_at is not None
|
||||||
|
|
||||||
session.delete(r)
|
session.delete(r)
|
||||||
session.commit()
|
session.commit()
|
||||||
if check_to_unfeature(session, r):
|
|
||||||
|
# Проверяем условие unfeatured только для уже featured публикаций
|
||||||
|
if is_currently_featured and check_to_unfeature(session, reaction_dict):
|
||||||
set_unfeatured(session, r.shout)
|
set_unfeatured(session, r.shout)
|
||||||
|
|
||||||
reaction_dict = r.dict()
|
reaction_dict = r.dict()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user