This is a Secret Santa game CLI tool.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: added seed option to allow deterministic draws

+30 -11
+5 -2
README.md
··· 1 1 # Secret Santa CLI 2 2 3 - This is a Secret Santa game CLI tool. It takes a `.yaml` file to configure the game, setting up the participants and the exclusions. 3 + This is a Secret Santa gane CLI tool. It takes a `.yaml` file to configure the game, setting up the participants and the exclusions. 4 4 5 5 Then, uses [Mailgun API](https://documentation.mailgun.com/docs/mailgun/user-manual/get-started/) to send the results of the draw via email. 6 6 ··· 54 54 55 55 - `--dry`: 56 56 Optional argument, to simulate the draw and not sending the results via 57 - email. 57 + email. 58 + 59 + - `--seed`: 60 + Optional argument, to define a string as the seed for the random module.
+11 -1
src/secretsanta/cli.py
··· 23 23 action="store_true", 24 24 help="Simulates the game, and doesn't sends the result.", 25 25 ) 26 + parser.add_argument( 27 + "--seed", 28 + type=str, 29 + help="Explicit the seed for the random choices.", 30 + default=None, 31 + ) 26 32 args = parser.parse_args() 27 33 config_file = Path(args.config) 28 34 if not config_file.is_file(): ··· 31 37 # creates the game 32 38 game = Game.create(config_file=config_file) 33 39 # runs the draw 34 - draw = Draw(participants=game.participants(), exclusions=game.exclusions) 40 + draw = Draw( 41 + participants=game.participants(), 42 + exclusions=game.exclusions, 43 + seed=args.seed, 44 + ) 35 45 draw.run() 36 46 # notify the results 37 47 notify(game=game, draw=draw, dry=args.dry)
+10 -7
src/secretsanta/draws.py
··· 20 20 solution: list[tuple[str, str]] 21 21 22 22 # available participants 23 - available: set[str] 23 + available: list[str] 24 24 25 25 # list of pair of participants that can't be assigned 26 26 exclusions: list[tuple[str, str]] ··· 29 29 self, 30 30 participants: list[str], 31 31 exclusions: list[tuple[str, str]] | None = None, 32 + seed: str | None = None, 32 33 ): 33 34 """Initializes the draw.""" 34 35 self.participants = participants 35 36 self.solution = [] 36 - self.available = set(participants) 37 + self.available = participants[:] 37 38 self.exclusions = exclusions or [] 39 + if seed: 40 + random.seed(seed) 38 41 39 42 def __len__(self) -> int: 40 43 """The len of the draw is the len of the current solution.""" ··· 59 62 def pick(self) -> str: 60 63 """Selects the next possible participant.""" 61 64 if not self.solution: 62 - selected = random.choice(list(self.available)) 65 + selected = random.choice(self.available) 63 66 self.available.remove(selected) 64 67 else: 65 68 selected = self.solution[-1][1] # select the last node in the solution ··· 74 77 def rollback(self, candidate: tuple[str, str]) -> None: 75 78 """Undoes the candidate.""" 76 79 self.solution.remove(candidate) 77 - if self.available: 78 - self.available.add(candidate[1]) 80 + if self.available and candidate[1] not in self.available: 81 + self.available.append(candidate[1]) 79 82 80 83 def choices(self) -> list[str]: 81 84 """Shuffle list of choices.""" 82 - choices = list(self.available) 85 + choices = self.available[:] 83 86 random.shuffle(choices) 84 87 return choices 85 88 ··· 122 125 def reset(self) -> None: 123 126 """Restores the draw to the initial status.""" 124 127 self.solution = [] 125 - self.available = set(self.participants) 128 + self.available = self.participants[:] 126 129 127 130 def run(self) -> None: 128 131 """Executes the backtrack algorithm until gets a result."""
+4 -1
src/secretsanta/templates/notification.html
··· 2 2 3 3 <p>Tienes que regalar a: <strong>{{ to_name }}</strong>.</p> 4 4 5 - <p>🎁 El Amigo Invisible 🎁</p> 5 + <p>Recuerda que hemos establecido un presupuesto de entre 20 y 25 euros.</p> 6 + 7 + <p><strong>πŸŽ„ Β‘Feliz Navidad! πŸŽ„</strong></p> 6 8 9 + <p>πŸŽ… El Amigo Invisible πŸŽ…</p>