···11-"""The file __main__.py marks the main entry point for the application when running it
22-via runpy.
33-"""
11+"""The entry point for the application when running it via runpy."""
4253if __name__ == "__main__":
64 from .cli import app
+1-2
src/secretsanta/cli.py
···101011111212def app() -> None:
1313- """Main app function, entry point for the CLI, using argparse."""
1414-1313+ """Create main app function, entry point for the CLI, using argparse."""
1514 # loads game config
1615 parser = argparse.ArgumentParser(description="Secret Santa CLI tool.")
1716 parser.add_argument(
+21-13
src/secretsanta/draws.py
···9910101111class Draw:
1212- """This class represents the secret santa draw, handling the participants as a list
1313- of strings.
1212+ """This class represents the secret santa draw.
1313+1414+ Handles the participants as a list of strings.
1415 """
15161617 # list of all the participants
···3132 exclusions: list[tuple[str, str]] | None = None,
3233 seed: str | None = None,
3334 ):
3434- """Initializes the draw."""
3535+ """Initialize the draw."""
3536 self.participants = participants
3637 self.solution = []
3738 self.available = participants[:]
3839 self.exclusions = exclusions or []
4040+ print(self.exclusions)
3941 if seed:
4042 random.seed(seed)
41434244 def __len__(self) -> int:
4343- """The len of the draw is the len of the current solution."""
4545+ """Get the length of the draw.
4646+4747+ This is the length of the current solution.
4848+ """
4449 return len(self.solution)
45504651 def is_complete(self) -> bool:
4747- """Draw is complete when the solution covers all the nodes, except for the
4848- transition that completes the cycle.
5252+ """Check if draw is complete.
5353+5454+ It is when the solution covers all the nodes, except for the transition that
5555+ completes the cycle.
4956 """
5057 return len(self.solution) == len(self.participants) - 1
51585259 def is_valid(self, transition: tuple[str, str]) -> bool:
5353- """A transition is valid if it is not in the exclusion list and not in the
5454- current solution.
6060+ """Check if A transition is valid.
6161+6262+ If it is not in the exclusion list and not in the current solution.
5563 """
5664 return transition not in self.exclusions and transition not in self.solution
57655866 def closing_transition(self) -> tuple[str, str]:
5959- """Creates the closing transition."""
6767+ """Create the closing transition."""
6068 return (self.solution[-1][1], self.solution[0][0])
61696270 def pick(self) -> str:
6363- """Selects the next possible participant."""
7171+ """Select the next possible participant."""
6472 if not self.solution:
6573 selected = random.choice(self.available)
6674 self.available.remove(selected)
···6977 return selected
70787179 def add(self, candidate: tuple[str, str]) -> None:
7272- """Adds the candidate to the solution."""
8080+ """Add the candidate to the solution."""
7381 self.solution.append(candidate)
7482 if self.available:
7583 self.available.remove(candidate[1])
···8795 return choices
88968997 def backtrack(self) -> bool:
9090- """Executes a backtrack algorithm to find a solution to the draw (a solution)."""
9898+ """Execute a backtrack algorithm to find a solution to the draw."""
9199 if self.is_complete():
92100 candidate = self.closing_transition()
93101 if self.is_valid(candidate):
···128136 self.available = self.participants[:]
129137130138 def run(self) -> None:
131131- """Executes the backtrack algorithm until gets a result."""
139139+ """Execute the backtrack algorithm until gets a result."""
132140 for _ in range(settings.limit):
133141 if self.backtrack():
134142 break
+6-11
src/secretsanta/models.py
···33from pathlib import Path
4455import yaml
66-77-try:
88- from yaml import CLoader as Loader
99-except ImportError:
1010- from yaml import Loader
1111-1212-1313-from pydantic import BaseModel
66+from pydantic import BaseModel, EmailStr
77+from yaml import CLoader as Loader
1481591610class Player(BaseModel):
1711 """A player is a participant in the secret santa draw."""
18121913 name: str # the player is identified with the name
2020- email: str
1414+ email: EmailStr
21152216 def __str__(self):
1717+ """Use the name as string representation of the Player."""
2318 return self.name
24192520 def __repr__(self):
2121+ """Use the name as representation of the class."""
2622 return self.name
27232824···44404541 @classmethod
4642 def create(cls, config_file: Path) -> "Game":
4747- """Creates the game using the provided config file."""
4343+ """Create the game using the provided config file."""
4844 assert config_file.is_file(), f"The file {config_file} does not't exists."
49455046 with config_file.open() as file:
···7268 name=secret_santa_config["name"],
7369 players=players,
7470 exclusions=exclusions,
7575- template=secret_santa_config.get("template"),
7671 )
77727873 # load notification if defined
+1-3
src/secretsanta/notifications.py
···1919 players: tuple[Player, Player],
2020 dry: bool = False,
2121) -> None:
2222- """Sends a single notification."""
2323-2222+ """Send a single notification."""
2423 # from player 0 to player 1
2524 _from = players[0]
2625 _to = players[1]
···53525453def notify(game: Game, draw: Draw, dry: bool = False) -> None:
5554 """Notify the result of the draw in the game."""
5656-5755 # load template
5856 if game.notification_template:
5957 environment = Environment()