- Develop a small system and an HTTP API for a knockout tournament.
- In each round, competitors are randomly paired and compete against each other.
- The winners move on to the next round, competing against other winners until only two competitors remain.
- The last two competitors determine the winner and the second place in the final round.
- The losers of the penultimate round compete among themselves to determine the third and fourth places.
- If it is not possible to pair all competitors in the first round, some automatically move to the second round.
To run this project in a Docker container, eliminating the need to install dependencies locally, follow these steps:
First, ensure that Docker is installed on your system.
- Clone this repository to your computer.
- Run the Docker build command to create the container image:
docker build -t image-name .
- Now, you can run the Docker container created with the following command:
docker-compose up -d
- In your terminal, simply run this command in the same directory where the docker compose is located
docker-compose run --entrypoint="poetry run task test" {nome do app}This project uses Taskipy to simplify common development tasks, such as code formatting and checking. Following are the commands available in Taskipy:
To execute the commands, open a terminal in the root directory of the project and run the following command:
task format- This command will apply formatting to the Python code in the current directory and subdirectories, following the pep8 conventions. It is a good practice to run this command before submitting your code for review or when you wish to maintain consistent formatting.
task run
This command will start the execution of the project, which should be configured in the app.py file.
To view the test coverage of the project
task post_testFormat the code using blue and isort
task format
Runs ruff to check if we have any problems with the code and checks if we are in accordance with PEP-8
task lintThis project utilizes Continuous Integration (CI) within the repository. Any code committed to the repository must adhere to PEP-8 standards and maintain a minimum test coverage of 90% to pass.
|─ app
│ ├── app.py
│ ├── database.py
│ ├── __init__.py
│ ├── migrations
│ │ ├── __pycache__
│ │ └── versions
│ ├── models.py
│ ├── routes
│ │ ├── __init__.py
│ │ └── routes.py
│ ├── schemas.py
│ ├── settings.py
│ └── tests
├── docker-compose.yaml
├── dockerfile
├── entrypoint.sh
├── htmlcov
│ ├── coverage_html.js
│ ├── index.html
├── migrations
├── poetry.lock
├── pyproject.toml
├── README.md
├── tests
│ ├── conftest.py
│ ├── __init__.py
│ │ ├── conftest.pyc
│ │ ├── __init__.cpython-311.pyc
│ │ ├── test_db.pyc
│ │ └── test_routes.pyc
│ ├── test_db.py
│ └── test_routes.py
- Fastapi
- SQLAlchemy
- Docker
- Pydantic
- Taskipy
- Alembic
- iSort
- Ruff
- CI for test
- Endpoint:
/tournament - Method:
POST - Description: Create a new tournament.
- Rules:
-
No two championships can share the same name and dates.
-
The end date of a championship cannot precede its start date.
-
jsonCopy code
{
"name": "Example Tournament",
"date_start": "2024-01-29T12:00:00",
"date_end": "2024-02-05T18:00:00"
}
- Status Code:
201 Created - Response Body:
jsonCopy code
{
"id": "tournament_id",
"name": "Example Tournament",
"date_start": "2024-01-29T12:00:00",
"date_end": "2024-02-05T18:00:00"
}
-
Endpoint:
/tournament/{tournament_id}/competitor -
Method:
POST -
Description: Register competitors for a tournament.
-
Rules:
-
Competitors can only be registered for a championship once; the championship begins immediately after this.
-
A minimum of two competitors is allowed for registration.
-
jsonCopy code
{
"names": ["Competitor1", "Competitor2"]
}
- Status Code:
201 Created
-
Endpoint:
/tournament/{tournament_id}/match -
Method:
GET -
Description: Retrieve the list of matches for a tournament.
-
Rules: -
- Running the Route: This action generates and lists matches.
- Pending Matches: If there are pending matches, new matches for the next round will not be created.
- Three-Person Championship: In the case of a three-person championship, running the route will generate a placeholder match, guaranteeing a third place. A separate request is required to generate the final match where the winner can be declared.
- Status Code:
201 Created - Response Body:
jsonCopy code
{
"Round 1": [
{
"id": "match_id",
"competitor_1": "Competitor1",
"competitor_2": "Competitor2",
"state": "pending",
"id": 1,
"round": 2
}
]
}
-
Endpoint:
/tournament/{tournament_id}/match/{match_id} -
Method:
POST -
Description: Set the winner for a match.
-
Rules:
- Only one winner can be added at a time per round.
- The name must match their name in the tournament, including the last distinguishing characters.
jsonCopy code
{
"name": "Competitor1"
}
- Status Code:
201 Created
- Endpoint:
/tournament/{tournament_id}/result - Method:
GET - Description: Retrieve the results of a tournament.
jsonCopy code
{
[
"First": "competitor 1",
"Second": "competitor 2 -10",
]
}
- Status Code:
201 Created
The Tournament class represents a sports tournament in the database. It is designed to store information about different tournaments, including their name, start and end dates, participating competitors, number of matches, and active status.
id(int): Unique identifier for the tournament (Primary Key).name(str): Name of the tournament.date_start(datetime): Start date and time of the tournament.date_end(datetime): End date and time of the tournament.competitors(relationship): Relationship with the 'Competitor' class.number_matches(int, optional): Number of matches in the tournament (can be None).is_active(bool): Indicates whether the tournament is active or not.
-
create_tournament(session, **kwargs) -> TournamentCreates a new tournament.
session(Session): SQLAlchemy session.*kwargs: Additional tournament data.
Tournament: The created tournament.
ValueError: If an error occurs during tournament creation.
The Competitor class represents participants in a sports tournament. It is designed to store information about competitors, including their name, group, associated tournament, and status.
id (int): Unique identifier for the competitor (Primary Key).name (str): Name of the competitor.group (Enum): Competitor's group (e.g., 'group_1' or 'group_2').tournament_id (int): Foreign key referencing the associated tournament.tournament (relationship): Relationship with the 'Tournament' class.status (bool): Indicates the competitor's active status.
This function calculates the number of matches needed for a tournament based on the number of competitors.
Parameters:
number_competitors (int): Number of competitors in the tournament.
Returns:
int: Number of matches.
Creates competitors, validates the associated tournament, and adds competitors to groups based on the tournament's brackets.
Parameters:
names (list): List of competitor names.tournament_id (int): ID of the associated tournament.session (Session): SQLAlchemy session.
Raises:
ValueError: If the tournament ID is not found, the tournament has already started, or there are fewer than 2 competitors.
The Match class represents individual matches within a sports tournament. It manages the pairing of competitors, tracks the winner, and maintains the state of each match.
- id (int): Unique identifier for the match (Primary Key).
- competitor_1_id (int): Foreign key referencing the first competitor.
- competitor_2_id (int): Foreign key referencing the second competitor.
- winner_id (int): Foreign key referencing the winner.
- tournament_id (int): Foreign key referencing the associated tournament.
- competitor_1 (relationship): Relationship with the 'Competitor' class for the first competitor.
- competitor_2 (relationship): Relationship with the 'Competitor' class for the second competitor.
- winner (relationship): Relationship with the 'Competitor' class for the winner.
- tournament (relationship): Relationship with the 'Tournament' class.
- round (int): Round number of the match.
- state (Enum): State of the match ('pending' or 'finished').
Sets pairs for the matches based on the provided list of competitor names. If the list has an odd length, one competitor is placed alone in a tuple.
names (list):List of competitor names.
list[tuple[Competitor]]:Pairs of competitors for the matches.
Creates matches for a tournament, verifies if it is the last round, and adds competitors to their respective matches.
tournament_id (int):ID of the associated tournament.session (Session):SQLAlchemy session.
Lists all matches from a tournament in a dictionary format.
tournament_id (int):ID of the associated tournament.session (Session):SQLAlchemy session.
dict:Dictionary with rounds and match details.
Sets the winner of a match and updates the state of the competitors.
match_id (int):Unique identifier for the match.name (str):Name of the winner.session (Session):SQLAlchemy session.
Match:The updated match.
Fetches the finalists, determines the winner, 2nd place, fetches the semifinalists, and determines the 3rd and 4th places.
tournament (int):ID of the associated tournament.session (Session):SQLAlchemy session.
dict:Dictionary with information about the top four competitors.
Creates a consolation match for a tournament.
tournament_id (int):ID of the tournament.last_round (int):The last round number.session (Session):SQLAlchemy session.
Verifies if a consolation match should be created.
total (int):Total number of matches expected.matches (list[Match]):List of matches.
bool:True if a consolation match should be created, False otherwise.