From da69b023a255c915b006b712473bb04258d7a7ce Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Wed, 19 Jun 2024 18:43:59 -0300 Subject: [PATCH 01/10] =?UTF-8?q?chore:=20substitui=20arquivo=20de=20confi?= =?UTF-8?q?gura=C3=A7=C3=B5es=20setup.cfg=20por=20pyproject.toml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 35 +++++++++++++++++++++++++++++++++++ setup.cfg | 21 --------------------- 2 files changed, 35 insertions(+), 21 deletions(-) create mode 100644 pyproject.toml delete mode 100755 setup.cfg diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9ef9738 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,35 @@ +[tool.black] +line-length = 120 + +[tool.isort] +profile = "black" + +[tool.flake8] +max-line-length = 120 +max-complexity = 4 +exclude = ["migrations", "venv"] + +[tool.pytest.ini_options] +python_files = ["test_*.py", "tests.py"] +norecursedirs = ["venv", "old_tests"] +addopts = """ +--ds=botscontabeis.settings --no-migrations --reuse-db +--pdbcls=IPython.terminal.debugger:TerminalPdb +--color=yes --quiet --numprocesses=auto +--cov --cov-report=html --no-cov-on-fail --maxfail=1 --cov-fail-under=80 --cov-branch +""" + +[tool.coverage.run] +branch = true +source = ["."] +omit = [ + "*/tests/*", + "*/migrations/*", + "venv/*", + "static/*", + "manage.py", + "requirements.txt", +] + +[tool.ipdb] +context=5 diff --git a/setup.cfg b/setup.cfg deleted file mode 100755 index 659290b..0000000 --- a/setup.cfg +++ /dev/null @@ -1,21 +0,0 @@ -[flake8] -max-line-length = 120 -exclude = migrations, venv -ignore = -# docstring - D102,D106,D101,D103,D100,D104,D105,D401, -# strings longas - W503,W504,E501 - -max-complexity = 7 -extend-ignore = E203 - -[pycodestyle] -max-line-length = 120 -exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules - -[black] -line-length = 120 - -[isort] -profile = black From d8563c5bb7ddf66f8488f077f8a6625ca3729dee Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Fri, 21 Jun 2024 15:31:16 -0300 Subject: [PATCH 02/10] chore: add scripts for dump and restore db --- scripts/dump_railway_db.sh | 53 ++++++++++++++++++++++++++++++++ scripts/restore_local_db.sh | 60 +++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100755 scripts/dump_railway_db.sh create mode 100755 scripts/restore_local_db.sh diff --git a/scripts/dump_railway_db.sh b/scripts/dump_railway_db.sh new file mode 100755 index 0000000..96f3282 --- /dev/null +++ b/scripts/dump_railway_db.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +echo """ +===================================================== ATENÇÃO =================================================== + +Certifique-se de ter carregado as variáveis de ambiente do projeto Railway no shell antes de executar este script. +Isso pode ser feito através dos comandos: + +$ railway login -p +$ railway link +$ railway shell + +Caso as variáveis de ambiente de desenvolvimento estiverem carregadas, o dump trará os dados do banco local. +""" + +# Função para verificar se uma variável de ambiente está definida +check_env_var() { + local var_name=$1 + local var_value=$(eval echo \$$var_name) + + if [ -z "$var_value" ]; then + echo "Erro: A variável de ambiente $var_name não está definida." + exit 1 + fi +} + +# Verificar variáveis de ambiente +check_env_var "DB_HOST" +check_env_var "DB_NAME" +check_env_var "DB_USER" +check_env_var "DB_PASSWORD" +check_env_var "DB_PORT" + +# Diretório onde o dump será salvo +LOCAL_DUMP_DIR="../dumps" +DUMP_FILE_NAME="dump_railway_$(date +%Y%m%d%H%M%S).tar" + +# Criar o diretório local se não existir +mkdir -p $LOCAL_DUMP_DIR + +# Executa o pg_dump no servidor remoto e salva o dump localmente +PGPASSWORD=$DB_PASSWORD pg_dump -h $DB_HOST -d $DB_NAME -U $DB_USER -p $DB_PORT -F t -n public > $LOCAL_DUMP_DIR/$DUMP_FILE_NAME + +# Verifica se o dump foi bem-sucedido +if [ $? -eq 0 ]; then + echo """ +Dump do banco de dados realizado com sucesso e salvo em $LOCAL_DUMP_DIR/$DUMP_FILE_NAME +""" +else + echo """ +Ocorreu um erro ao realizar o dump do banco de dados +""" +fi diff --git a/scripts/restore_local_db.sh b/scripts/restore_local_db.sh new file mode 100755 index 0000000..50e19b8 --- /dev/null +++ b/scripts/restore_local_db.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Verifica se um arquivo foi passado como parâmetro +if [ -z "$1" ]; then + echo "Erro: Por favor, forneça o nome do arquivo de dump a ser restaurado." + exit 1 +fi + +DUMP_FILE_NAME=$1 + +echo """ +===================================================== ATENÇÃO =================================================== + +Certifique-se de ter carregado as variáveis de ambiente do projeto local no shell antes de executar este script. +Isso pode ser feito através dos comandos: + +$ source .env +""" + +# Diretório onde o dump está salvo +LOCAL_DUMP_DIR="../dumps" +DUMP_FILE_PATH="$LOCAL_DUMP_DIR/$DUMP_FILE_NAME" + +# Verifica se o arquivo de dump existe +if [ ! -f "$DUMP_FILE_PATH" ]; then + echo "Erro: O arquivo de dump $DUMP_FILE_PATH não existe." + exit 1 +fi + +# Função para verificar se uma variável de ambiente está definida +check_env_var() { + local var_name=$1 + local var_value=$(eval echo \$$var_name) + + if [ -z "$var_value" ]; then + echo "Erro: A variável de ambiente $var_name não está definida." + exit 1 + fi +} + +# Verificar variáveis de ambiente +check_env_var "DB_HOST" +check_env_var "DB_NAME" +check_env_var "DB_USER" +check_env_var "DB_PASSWORD" +check_env_var "DB_PORT" + +# Executa o pg_restore no servidor remoto utilizando o dump fornecido +PGPASSWORD=$DB_PASSWORD pg_restore -h $DB_HOST -d $DB_NAME -U $DB_USER -p $DB_PORT -F t -c "$DUMP_FILE_PATH" + +# Verifica se o restore foi bem-sucedido +if [ $? -eq 0 ]; then + echo """ +Restore do banco de dados realizado com sucesso utilizando o dump $DUMP_FILE_PATH +""" +else + echo """ +Ocorreu um erro ao realizar o restore do banco de dados +""" +fi From c04989d6954e1125261248b4c4dcc38c31b86644 Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Fri, 21 Jun 2024 15:31:29 -0300 Subject: [PATCH 03/10] chore: update gitgnore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 088b4d3..b768b62 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,8 @@ dmypy.json # ssh keys *.pem + + +# Database backups + +dumps/* From 788429eeabf6214b367b0b2dc31122a9e26376a4 Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Fri, 21 Jun 2024 15:31:50 -0300 Subject: [PATCH 04/10] chore: update pyproject flake8 settings --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9ef9738..e4b51dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,8 +6,9 @@ profile = "black" [tool.flake8] max-line-length = 120 -max-complexity = 4 +max-complexity = 7 exclude = ["migrations", "venv"] +ignore = ["E501"] [tool.pytest.ini_options] python_files = ["test_*.py", "tests.py"] From 8be230d577e35b691ecc5a7d571748f962595588 Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Fri, 21 Jun 2024 15:32:07 -0300 Subject: [PATCH 05/10] build: update requirements.txt --- requirements.txt | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4f8511c..7d03d85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ amqp==5.1.1 -asgiref==3.5.2 +asgiref==3.8.1 asttokens==2.4.1 async-timeout==4.0.2 billiard==3.6.4.0 -black==23.3.0 +black==24.4.2 Brotli==1.0.9 celery==5.2.7 -certifi==2022.12.7 +certifi==2024.2.2 cffi==1.15.1 charset-normalizer==3.1.0 click==8.1.3 @@ -17,24 +17,30 @@ colorama==0.4.6 coverage==6.5.0 cron-descriptor==1.2.35 decorator==5.1.1 -Django==4.1.3 -django-celery-beat==2.5.0 +distlib==0.3.8 +Django==5.0.6 +django-celery-beat==2.6.0 django-celery-results==2.5.0 django-coverage-plugin==3.0.0 django-debug-toolbar==4.0.0 django-filter==22.1 +django-redis==5.4.0 django-timezone-field==5.0 djangorestframework==3.14.0 dnspython==2.2.1 eventlet==0.33.2 +execnet==2.1.1 executing==2.0.1 -flake8==6.1.0 +filelock==3.13.4 +flake8==7.1.0 +Flake8-pyproject==1.2.3 flower==1.2.0 gevent==22.10.2 greenlet==2.0.1 gunicorn==20.1.0 humanize==4.6.0 idna==3.4 +iniconfig==2.0.0 install==1.3.5 ipdb==0.13.13 ipython==8.20.0 @@ -43,23 +49,29 @@ kombu==5.2.4 Markdown==3.4.1 matplotlib-inline==0.1.6 mccabe==0.7.0 -model-mommy==2.0.0 +model-bakery==1.18.1 mypy-extensions==0.4.3 onesignal-python-api==1.0.2 packaging==23.1 parso==0.8.3 pathspec==0.10.1 pexpect==4.9.0 -platformdirs==2.5.3 +pipenv==2023.12.1 +platformdirs==4.2.0 +pluggy==1.5.0 prometheus-client==0.16.0 prompt-toolkit==3.0.43 psycopg2-binary==2.9.6 ptyprocess==0.7.0 pure-eval==0.2.2 -pycodestyle==2.11.1 +pycodestyle==2.12.0 pycparser==2.21 -pyflakes==3.1.0 +pyflakes==3.2.0 Pygments==2.17.2 +pytest==8.2.2 +pytest-cov==5.0.0 +pytest-django==4.8.0 +pytest-xdist==3.6.1 python-crontab==2.7.1 python-dateutil==2.8.2 python-decouple==3.7 @@ -75,6 +87,7 @@ traitlets==5.14.1 tzdata==2023.3 urllib3==1.26.14 vine==5.0.0 +virtualenv==20.25.1 wcwidth==0.2.6 whitenoise==6.3.0 zope.event==4.6 From cd41e09b4c79499025d23f90aa5ab045a9193b99 Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Fri, 21 Jun 2024 15:32:27 -0300 Subject: [PATCH 06/10] feat: create constants.py --- core/constants.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 core/constants.py diff --git a/core/constants.py b/core/constants.py new file mode 100644 index 0000000..48130f2 --- /dev/null +++ b/core/constants.py @@ -0,0 +1 @@ +SEGUNDOS_24_HORAS = 60 * 60 * 24 From 7483b1825751673b65f64c1ecdbba52c640d7dba Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Fri, 21 Jun 2024 15:33:47 -0300 Subject: [PATCH 07/10] feat: setup redis cache --- palpiteiros/settings.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/palpiteiros/settings.py b/palpiteiros/settings.py index 8f9156d..d14c314 100644 --- a/palpiteiros/settings.py +++ b/palpiteiros/settings.py @@ -125,8 +125,8 @@ LANGUAGE_CODE = "pt-br" USE_I18N = True -TIME_ZONE = config("TIME_ZONE") -USE_TZ = config("USE_TZ", cast=bool) +TIME_ZONE = config("TIME_ZONE", default="America/Sao_Paulo") +USE_TZ = config("USE_TZ", default=1, cast=bool) # Static files (CSS, JavaScript, Images) @@ -154,9 +154,7 @@ # Email -EMAIL_BACKEND = config( - "EMAIL_BACKEND", default="django.core.mail.backends.smtp.EmailBackend" -) +EMAIL_BACKEND = config("EMAIL_BACKEND", default="django.core.mail.backends.smtp.EmailBackend") EMAIL_HOST = config("EMAIL_HOST", default="smtp.gmail.com") EMAIL_HOST_USER = config("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = config("EMAIL_HOST_PASSWORD") @@ -191,3 +189,16 @@ FOOTBALL_API_KEY = config("FOOTBALL_API_KEY") FOOTBALL_API_HOST = "v3.football.api-sports.io" FOOTBALL_API_RATE_LIMIT_TIME = 7 + + +# Cache + +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": config("CACHE_LOCATION"), + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + }, + } +} From 2f046b8c768d98419997efa03302c854b357c586 Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Fri, 21 Jun 2024 15:34:30 -0300 Subject: [PATCH 08/10] feat: caching get method of RankingView --- core/views.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/core/views.py b/core/views.py index b8a9b5a..c5eeeaa 100644 --- a/core/views.py +++ b/core/views.py @@ -3,11 +3,14 @@ from django.forms import CheckboxSelectMultiple, modelform_factory from django.shortcuts import get_object_or_404, render from django.utils import timezone +from django.utils.decorators import method_decorator from django.utils.text import slugify from django.views import generic +from django.views.decorators.cache import cache_page from core.helpers import redirect_with_msg +from . import constants from .forms import GuesserEditForm, GuessForm, RankingPeriodForm, UserEditForm from .models import Guess, GuessPool from .viewmixins import GuessPoolMembershipMixin @@ -23,11 +26,7 @@ def get_context_data(self, **kwargs): involved_pools = guesser.get_involved_pools() pools_as_guesser = guesser.pools.all() for pool in involved_pools: - pool.is_pending = ( - pool.has_pending_match(self.request.user.guesser) - if pool in pools_as_guesser - else False - ) + pool.is_pending = pool.has_pending_match(self.request.user.guesser) if pool in pools_as_guesser else False context["display_subtitle"] = any([pool.is_pending for pool in involved_pools]) context["pools"] = involved_pools @@ -234,10 +233,7 @@ def get(self, request, *args, **kwargs): return redirect_with_msg( self.request, "error", - "Nenhum bolão público cadastrado..." - + " Que tal criar um agora mesmo?" - + " Basta clicar em Criar bolão" - + " e configurar como quiser 😎", + "Nenhum bolão público cadastrado... Que tal criar um agora mesmo? Basta clicar em Criar bolão e configurar como quiser 😎", "long", ) return super().get(request, *args, **kwargs) @@ -253,10 +249,7 @@ def dispatch(self, request, *args, **kwargs): return redirect_with_msg( self.request, "error", - "Você não está cadastrado como palpiteiro." - + " Acesse Gerenciar bolão" - + " e marque seu usuário como Palpiteiro" - + " para ter acesso à esta ação.", + "Você não está cadastrado como palpiteiro. Acesse Gerenciar bolão e marque seu usuário como Palpiteiro para ter acesso à esta ação.", "long", self.pool, ) @@ -372,6 +365,7 @@ def post(self, *args, **kwargs): class RankingView(LoginRequiredMixin, GuessPoolMembershipMixin, generic.TemplateView): template_name = "core/ranking.html" + @method_decorator(cache_page(constants.SEGUNDOS_24_HORAS)) def get(self, *args, **kwargs): context = self.get_context_data(**kwargs) if not context["guessers"].exists(): @@ -400,8 +394,6 @@ def get_context_data(self, **kwargs): week = int(form.cleaned_data["semana"]) context["period_form"] = form - context["guessers"] = self.pool.get_guessers_with_score_and_guesses( - month, year, week - ) + context["guessers"] = self.pool.get_guessers_with_score_and_guesses(month, year, week) return context From aa9c57548b07cd0c34ab5c7f1fcfb0209dfe925e Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Fri, 21 Jun 2024 15:37:13 -0300 Subject: [PATCH 09/10] feat: improve command that creates and updates matches to clear cache after executions --- .../commands/create_and_update_matches.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/core/management/commands/create_and_update_matches.py b/core/management/commands/create_and_update_matches.py index 3b0f8b9..4814aa3 100644 --- a/core/management/commands/create_and_update_matches.py +++ b/core/management/commands/create_and_update_matches.py @@ -1,10 +1,11 @@ +from django.core.cache import cache from django.core.management.base import BaseCommand, CommandParser from core.models import Competition class Command(BaseCommand): - help = "For each registered competition, send a request to the Football API to create new matches and update those already registered." + help = "For each registered competition, uses the Football API to create new matches and update those already registered." def add_arguments(self, parser: CommandParser) -> None: parser.add_argument( @@ -25,13 +26,17 @@ def handle(self, *args, **options): competitions = Competition.objects.all() if not competitions.exists(): self.stdout.write("No competitions registered yet.") + return - for competition in competitions: - created_matches, updated_matches = competition.create_and_update_matches( - days_from, days_ahead - ) - self.stdout.write( - f"{len(created_matches)} matches created and {len(updated_matches)} updated matches for {competition}..." - ) + for comp in competitions: + try: + created, updated = comp.create_and_update_matches(days_from, days_ahead) + self.stdout.write(f"{len(created)} matches created and {len(updated)} updated matches for {comp}...") + + except Exception as e: + self.stderr.write(f"Error when updating {comp}: {e}") + + self.stdout.write("Cleaning cache data...") + cache.clear() self.stdout.write("Competitions update done.") From 0149d13c2ccf4eafb7b11ec9dfbc2b03cafacb5c Mon Sep 17 00:00:00 2001 From: Elder Santos Date: Fri, 21 Jun 2024 20:37:19 -0300 Subject: [PATCH 10/10] style: format code with black --- core/models.py | 90 +++++++++++--------------------------------------- 1 file changed, 20 insertions(+), 70 deletions(-) diff --git a/core/models.py b/core/models.py index d8a8da2..4d79bcf 100644 --- a/core/models.py +++ b/core/models.py @@ -36,11 +36,7 @@ def __str__(self) -> str: return self.name def logo_url(self): - return ( - f"https://media.api-sports.io/football/teams/{self.data_source_id}.png" - if self.data_source_id - else "" - ) + return f"https://media.api-sports.io/football/teams/{self.data_source_id}.png" if self.data_source_id else "" class Competition(models.Model): @@ -75,11 +71,7 @@ def get_teams(self): params = {"league": self.real_data_source_id, "season": self.season} # TODO: tratar requests.exceptions.ConnectionError: - response = ( - requests.get(source_url, headers=headers, params=params) - .json() - .get("response") - ) + response = requests.get(source_url, headers=headers, params=params).json().get("response") sleep(settings.FOOTBALL_API_RATE_LIMIT_TIME) teams = [] @@ -114,9 +106,7 @@ def create_and_update_matches(self, days_from: int | None, days_ahead: int | Non "season": self.season, "from": str(from_), "to": str(to), - "status": "-".join( - [Match.NOT_STARTED, *Match.IN_PROGRESS_AND_FINISHED_STATUS] - ), + "status": "-".join([Match.NOT_STARTED, *Match.IN_PROGRESS_AND_FINISHED_STATUS]), } # TODO: tratar requests.exceptions.ConnectionError: @@ -245,9 +235,7 @@ def update_matches(self, days_from: int | None, days_ahead: int | None): away_goals = data["goals"]["away"] match = ( - Match.objects.exclude(status__in=Match.FINISHED_STATUS) - .filter(data_source_id=data_source_id) - .first() + Match.objects.exclude(status__in=Match.FINISHED_STATUS).filter(data_source_id=data_source_id).first() ) if match is None: @@ -266,9 +254,7 @@ def get_with_matches_on_period(cls, from_: date, to: date): """Returns competitions which that have matches on period""" matches_on_period = Match.get_happen_on_period(from_, to) - competitions_with_matches_on_period = matches_on_period.values( - "competition" - ).distinct() + competitions_with_matches_on_period = matches_on_period.values("competition").distinct() return cls.objects.filter(id__in=competitions_with_matches_on_period) @@ -371,18 +357,12 @@ def update_status( ): self.status = new_status - match_time_limit = self.date_time + timezone.timedelta( - minutes=match_time_limit_minutes - ) + match_time_limit = self.date_time + timezone.timedelta(minutes=match_time_limit_minutes) match_broke_limit_time = timezone.now() >= match_time_limit REGULAR_MATCH_DURATION = 90 - if ( - match_broke_limit_time - and elapsed_time >= REGULAR_MATCH_DURATION - and self.status == Match.SECOND_HALF - ): + if match_broke_limit_time and elapsed_time >= REGULAR_MATCH_DURATION and self.status == Match.SECOND_HALF: self.status = Match.FINISHED_STATUS def is_finished(self) -> bool: @@ -412,14 +392,8 @@ def result_str(self): description="Aberta para palpites?", ) def open_to_guesses(self): - return ( - self.date_time - > timezone.now() - + timezone.timedelta(minutes=self.MINUTES_BEFORE_START_MATCH) - ) and ( - self.date_time - <= timezone.now() - + timezone.timedelta(hours=self.HOURS_BEFORE_OPEN_TO_GUESSES) + return (self.date_time > timezone.now() + timezone.timedelta(minutes=self.MINUTES_BEFORE_START_MATCH)) and ( + self.date_time <= timezone.now() + timezone.timedelta(hours=self.HOURS_BEFORE_OPEN_TO_GUESSES) ) def get_pools(self): @@ -475,11 +449,7 @@ def get_involved_pools(self): def get_who_should_be_notified_by_email(cls): """Returns guessers that should be notified by email""" - return ( - cls.objects.exclude(user__email="") - .exclude(pools__isnull=True) - .exclude(receive_notifications=False) - ) + return cls.objects.exclude(user__email="").exclude(pools__isnull=True).exclude(receive_notifications=False) def get_involved_pools_with_new_matches(self): """Returns pools with new matches that this guesser is involved @@ -529,9 +499,7 @@ class Meta: def __str__(self) -> str: return ( - f"{self.match.home_team.name} {self.home_goals}" - + " x " - + f"{self.away_goals} {self.match.away_team.name}" + f"{self.match.home_team.name} {self.home_goals}" + " x " + f"{self.away_goals} {self.match.away_team.name}" ) def get_score(self) -> int: @@ -563,16 +531,10 @@ def _evaluate(self): ACERTO_VISITANTE_VENCEDOR = (self.home_goals < self.away_goals) and ( self.match.home_goals < self.match.away_goals ) - ACERTO_EMPATE = (self.home_goals == self.away_goals) and ( - self.match.home_goals == self.match.away_goals - ) + ACERTO_EMPATE = (self.home_goals == self.away_goals) and (self.match.home_goals == self.match.away_goals) ACERTO_PARCIAL = ACERTO_MANDANTE_VENCEDOR or ACERTO_VISITANTE_VENCEDOR - ACERTO_PARCIAL_COM_GOLS = ACERTO_PARCIAL and ( - ACERTO_DE_GOLS_MANDANTE or ACERTO_DE_GOLS_VISITANTE - ) - ACERTO_SOMENTE_GOLS = ( - ACERTO_DE_GOLS_MANDANTE or ACERTO_DE_GOLS_VISITANTE - ) and not ACERTO_PARCIAL + ACERTO_PARCIAL_COM_GOLS = ACERTO_PARCIAL and (ACERTO_DE_GOLS_MANDANTE or ACERTO_DE_GOLS_VISITANTE) + ACERTO_SOMENTE_GOLS = (ACERTO_DE_GOLS_MANDANTE or ACERTO_DE_GOLS_VISITANTE) and not ACERTO_PARCIAL ACERTO_CRAVADO = ACERTO_DE_GOLS_MANDANTE and ACERTO_DE_GOLS_VISITANTE PONTUACAO_ACERTO_CRAVADO = 10 @@ -705,10 +667,8 @@ def get_open_matches(self): """Returns matches open to guesses""" return self.get_matches().filter( - date_time__gt=timezone.now() - + timezone.timedelta(minutes=self.minutes_before_start_match), - date_time__lte=timezone.now() - + timezone.timedelta(hours=self.hours_before_open_to_guesses), + date_time__gt=timezone.now() + timezone.timedelta(minutes=self.minutes_before_start_match), + date_time__lte=timezone.now() + timezone.timedelta(hours=self.hours_before_open_to_guesses), ) @classmethod @@ -738,9 +698,7 @@ def add_guess_to_pools(self, guess: Guess, for_all_pools: bool): if for_all_pools: pools_with_the_match = match.get_pools() pools_with_the_guesser = guesser.pools.all() - pools_with_match_and_guesser = pools_with_the_match.intersection( - pools_with_the_guesser - ) + pools_with_match_and_guesser = pools_with_the_match.intersection(pools_with_the_guesser) for pool in pools_with_match_and_guesser: self._replace_guess_in_pool(guess, pool) @@ -804,9 +762,7 @@ def get_guessers_with_score_and_guesses( guessers = self.get_guessers_with_match_scores(matches) for guesser in guessers: - guesser.matches_and_guesses = self._get_guesses_per_matches( - guesser, matches - ) + guesser.matches_and_guesses = self._get_guesses_per_matches(guesser, matches) return guessers @@ -838,11 +794,7 @@ def _assemble_datetime_period(self, month: int, year: int, week: int): else: # período semanal (ano, mes e semanas recebidos) start = timezone.now().fromisocalendar(year, week, 1) - end = ( - timezone.now() - .fromisocalendar(year, week, 7) - .replace(hour=23, minute=59, second=59) - ) + end = timezone.now().fromisocalendar(year, week, 7).replace(hour=23, minute=59, second=59) return start, end @@ -862,9 +814,7 @@ def get_finished_or_in_progress_matches_on_period( ) def get_guessers_with_match_scores(self, matches: Iterable[Match]): - guesses_of_this_pool_in_the_period = Q(guesses__match__in=matches) & Q( - guesses__in=self.guesses.all() - ) + guesses_of_this_pool_in_the_period = Q(guesses__match__in=matches) & Q(guesses__in=self.guesses.all()) sum_expr = Sum( "guesses__score", filter=guesses_of_this_pool_in_the_period,