Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

[bug] Adicionar validadores em todas as páginas de admin #76

Merged
merged 20 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
993a346
feat(validators): contest admin form validator
LuizaMaluf Nov 14, 2023
189b27e
feat(validators): contest status form validator
LuizaMaluf Nov 14, 2023
affb0a3
docs(remove-name): name removed from index.rst [introdução]
thegm445 Nov 10, 2023
dd6ab9d
docs(remove-name): name removed from README.md
thegm445 Nov 10, 2023
e068398
docs(sprint-03): add initial file for docs refering to the fourthsprint
thegm445 Nov 8, 2023
f586355
docs(sprint-03): add some issues that were in other milestones
thegm445 Nov 10, 2023
cb9fef5
docs(sprint-03): add reference on intro.rst
thegm445 Nov 10, 2023
9413abf
docs(sprint-03): brief sprint description has been rewritten
thegm445 Nov 14, 2023
a7c9d22
feat(LoginTemplate): add style to login page
HladczukLe Nov 13, 2023
f9c0ed6
feat(templates/registration): make the forms look better
bitterteriyaki Nov 15, 2023
8188fbc
style(templates/registration): add an extra space between the form an…
bitterteriyaki Nov 15, 2023
249f980
revert: revert gitignore change
bitterteriyaki Nov 15, 2023
89b15a0
feat(validators): contest status form validator tests
LuizaMaluf Nov 15, 2023
12c69c9
feat(validators): contest views validator
LuizaMaluf Nov 15, 2023
9996f17
feat(validators): contest views validator
LuizaMaluf Nov 15, 2023
e5ada46
fix(apps/contests): fix model validation return type
bitterteriyaki Nov 15, 2023
b8285b2
fix(apps/contests): fix redundant methods and tests
bitterteriyaki Nov 15, 2023
5298df5
docs(apps/contests): add some documentation for confusing methods/pro…
bitterteriyaki Nov 15, 2023
94b99cf
chore(coverage.py): do not report tests files
bitterteriyaki Nov 15, 2023
1d1de6d
docs(apps/contests): remove redundant documentation
bitterteriyaki Nov 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ programação competitiva.
| Gabriel Moura | 221008060 | [@thegm445](https://github.com/thegm445) |
| Luiza Maluf | 221008294 | [@LuizaMaluf](https://github.com/LuizaMaluf) |
| Letícia Hladczuk | 221039209 | [@HladczukLe](https://github.com/HladczukLe) |
| Esther Silva | 190106034 | [@EstherSousa](https://github.com/EstherSousa) |
| Gabriel Fernando | 222022162 | [@MMcLovin](https://github.com/MMcLovin) |

## Instalação
Expand Down
40 changes: 38 additions & 2 deletions apps/contests/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.core.exceptions import ValidationError
from django.db.models import (
BooleanField,
CharField,
Expand Down Expand Up @@ -33,12 +34,47 @@ def __str__(self) -> str:

@property
def status(self) -> ContestStatus:
"""
Retorna o status atual do contest baseado no horário atual.

.. list-table:: Status do contest
:header-rows: 1

* - Status
- Descrição
* - :attr:`ContestStatus.PENDING`
- Quando o contest ainda não começou.
* - :attr:`ContestStatus.RUNNING`
- Quando o contest está acontecendo.
* - :attr:`ContestStatus.FINISHED`
- Quando o contest já terminou.
* - :attr:`ContestStatus.CANCELLED`
- Quando o contest foi cancelado.

Returns
-------
:class:`ContestStatus`
O status atual do contest.
"""
if self.cancelled:
return ContestStatus.CANCELLED

if self.start_time > now():
elif self.start_time > now():
return ContestStatus.PENDING
elif self.end_time < now():
return ContestStatus.FINISHED
else:
return ContestStatus.RUNNING

def clean(self) -> None:
"""
Validação do contest. Verifica se o :attr:`start_time` é menor
que o :attr:`end_time`, ou seja, se o contest começa antes de
terminar. Se não for, lança um :class:`ValidationError`.

Raises
------
:class:`ValidationError`
Se o :attr:`start_time` for maior que o :attr:`end_time`.
"""
if self.start_time > self.end_time:
raise ValidationError("Start time must be before end time.")
49 changes: 34 additions & 15 deletions apps/contests/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import timedelta

from django.contrib.admin import AdminSite
from django.core.exceptions import ValidationError
from django.forms import CharField, Textarea
from django.test import TestCase
from django.urls import resolve, reverse
Expand All @@ -11,13 +12,9 @@
from apps.contests.models import Contest


class ContestTestCase(TestCase):
class ContestModelTestCase(TestCase):
def setUp(self) -> None:
self.contest = Contest(
title="Test Contest",
description="This is a test contest",
cancelled=False,
)
self.contest = Contest(title="Test", description="Test contest")

def test_status_pending(self) -> None:
self.contest.start_time = timezone.now() + timedelta(hours=1)
Expand All @@ -38,19 +35,41 @@ def test_status_cancelled(self) -> None:
self.contest.cancelled = True
self.assertEqual(self.contest.status, ContestStatus.CANCELLED)

def test_start_time_before_end_time(self) -> None:
self.contest.start_time = timezone.now()
self.contest.end_time = timezone.now() + timedelta(hours=1)

class ContestStatusTestCase(TestCase):
def test_pending(self) -> None:
self.assertEqual(ContestStatus.PENDING, "Pending")
try:
self.contest.clean()
except ValidationError:
self.fail("ValidationError raised unexpectedly.")

def test_running(self) -> None:
self.assertEqual(ContestStatus.RUNNING, "Running")
def test_start_time_after_end_time(self) -> None:
self.contest.start_time = timezone.now() + timedelta(hours=1)
self.contest.end_time = timezone.now()

expected = ["Start time must be before end time."]

with self.assertRaises(ValidationError) as context:
self.contest.clean()

def test_finished(self) -> None:
self.assertEqual(ContestStatus.FINISHED, "Finished")
self.assertEqual(context.exception.messages, expected)

def test_cancelled(self) -> None:
self.assertEqual(ContestStatus.CANCELLED, "Cancelled")

class ContestStatusTestCase(TestCase):
def test_statuses_values(self) -> None:
self.assertEqual(ContestStatus.PENDING.value, "Pending")
self.assertEqual(ContestStatus.RUNNING.value, "Running")
self.assertEqual(ContestStatus.FINISHED.value, "Finished")
self.assertEqual(ContestStatus.CANCELLED.value, "Cancelled")

def test_statuses_choices_length(self) -> None:
"""
Devemos ter 4 opções de status de contests: Pending, Running,
Finished, Cancelled. Qualquer mudança nesse número deve ser
refletida aqui.
"""
self.assertEqual(len(ContestStatus), 4)


class ContestModelFormTestCase(TestCase):
Expand Down
11 changes: 7 additions & 4 deletions apps/contests/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Dict

from django.db.models.query import QuerySet
from django.views import generic
Expand All @@ -19,12 +19,15 @@ class IndexView(IndexViewBase):
context_object_name = "contests"

def get_queryset(self) -> QuerySet[Contest]:
return Contest._default_manager.order_by("start_time")[:5]
return Contest._default_manager.order_by("-start_time")[:5]

def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
ctx = super().get_context_data(**kwargs)
# Serve para separar os concursos que estão com status pendente
# ou em andamento dos concursos que já aconteceram ou que foram
# cancelados. Precisamos separar para que o template possa
# exibir os contests de forma diferente.
ctx["valid_statuses"] = (ContestStatus.PENDING, ContestStatus.RUNNING)

return ctx


Expand Down
1 change: 0 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ Integrantes
Gabriel Moura 221008060 `@thegm445 <https://github.com/thegm445>`_
Luiza Maluf 221008294 `@LuizaMaluf <https://github.com/LuizaMaluf>`_
Letícia Hladczuk 221039209 `@HladczukLe <https://github.com/HladczukLe>`_
Esther Silva 190106034 `@EstherSousa <https://github.com/EstherSousa>`_
Gabriel Fernando 222022162 `@MMcLovin <https://github.com/MMcLovin>`_
================ ========= ======================================================

Expand Down
1 change: 1 addition & 0 deletions docs/source/scrum/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ Sprints
sprints/sprint-0
sprints/sprint-1
sprints/sprint-2
sprints/sprint-3
20 changes: 20 additions & 0 deletions docs/source/scrum/sprints/sprint-3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Sprint 3
========

:bdg-info:`04/11/2023`

Resumo
------

Esta sprint sucedeu a primeira release e começou a segunda fase da disciplina.
Fase que é focada mais na implementação, portanto o objetivo desta sprint foi
melhorar a aparência da página de registro de usuário e iniciar o
desenvolvimento dos testes unitários (requisito presente no plano de ensino).

Changelog
----------

- `Começar o desenvolvimento de testes unitários (#30) <https://github.com/unb-mds/2023-2-Squad06/issues/30>`_
- `Desenvolvimento dos testes (#57) <https://github.com/unb-mds/2023-2-Squad06/pull/57>`_
- `Criar view de registro de usuário (#29) <https://github.com/unb-mds/2023-2-Squad06/issues/29>`_
- `Adicionar view de registro (#54) <https://github.com/unb-mds/2023-2-Squad06/pull/54>`_
34 changes: 33 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ psycopg2 = "^2.9.7"
django-environ = "^0.11.2"
django-guardian = "^2.4.0"
django-bootstrap-v5 = "^1.0.11"
django-crispy-forms = "^2.1"
crispy-bootstrap5 = "^2023.10"

[tool.poetry.group.dev.dependencies]
pre-commit = "^3.4.0"
Expand Down
10 changes: 10 additions & 0 deletions server/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
THIRD_PARTY_APPS = [
"guardian",
"bootstrap5",
"crispy_forms",
"crispy_bootstrap5",
]

LOCAL_APPS = [
Expand Down Expand Up @@ -153,3 +155,11 @@
#####################

ANONYMOUS_USER_NAME = None

##################
# Crispy Forms #
##################

CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"

CRISPY_TEMPLATE_PACK = "bootstrap5"
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ max-complexity = 5
# These files can't be tested, so we exclude them from coverage.
# We also exclude the manage.py file, since it's not a part of the app.
omit =
apps/*/tests.py
server/asgi.py
server/wsgi.py
server/settings/__init__.py
Expand Down
52 changes: 28 additions & 24 deletions templates/registration/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,40 @@
{% block title %}Login{% endblock title %}

{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}

{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% if user.is_authenticated %}
<p>
Your account doesn't have access to this page. To proceed, please login
with an account that has access.
</p>
{% else %}
<p>
Please login to see this page.
</p>
{% endif %}
{% endif %}

{% load crispy_forms_tags %}

<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>

<input type="submit" value="login">
<input type="hidden" name="next" value="{{ next }}">

<div class="row justify-content-center">
<div class="col-md-9">
<div class="card">
<div class="card-header">
Sign in to your account
</div>
<div class="card-body">
{{ form|crispy }}

<input type="submit" class="btn btn-primary" style="width: 100%; margin-top: 25px;" value="Login">
<input type="hidden" name="next" value="{{ next }}">
</div>
</div>
</div>
</div>
</form>

{# Assumes you set up the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>
{% endblock %}
Loading