Completed
Push — main ( c0c994...7dc7dc )
by Jochen
03:36
created

_generate_ticket_code_not_in()   A

Complexity

Conditions 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
nop 3
dl 0
loc 11
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
"""
2
byceps.services.ticketing.ticket_code_service
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2020 Jochen Kupperschmidt
6
:License: Modified BSD, see LICENSE for details.
7
"""
8
9 1
from random import sample
10 1
from typing import Set
11
12 1
from .transfer.models import TicketCode
13
14
15 1
def generate_ticket_codes(quantity: int) -> Set[TicketCode]:
16
    """Generate a number of ticket codes."""
17 1
    codes: Set[TicketCode] = set()
18
19 1
    for _ in range(quantity):
20 1
        code = _generate_ticket_code_not_in(codes)
21 1
        codes.add(code)
22
23
    # Check if the correct number of codes has been generated.
24 1
    _verify_total_matches(codes, quantity)
25
26 1
    return codes
27
28
29 1
def _generate_ticket_code_not_in(
30
    codes: Set[TicketCode], *, max_attempts: int = 4
31
) -> TicketCode:
32
    """Generate ticket codes and return the first one not in the set."""
33 1
    for _ in range(max_attempts):
34 1
        code = _generate_ticket_code()
35 1
        if code not in codes:
36 1
            return code
37
38 1
    raise TicketCodeGenerationFailed(
39
        f'Could not generate unique ticket code after {max_attempts} attempts.'
40
    )
41
42
43 1
_CODE_ALPHABET = 'BCDFGHJKLMNPQRSTVWXYZ'
44 1
_CODE_LENGTH = 5
45
46
47 1
def _generate_ticket_code() -> TicketCode:
48
    """Generate a ticket code.
49
50
    Generated codes are not necessarily unique!
51
    """
52 1
    return TicketCode(''.join(sample(_CODE_ALPHABET, _CODE_LENGTH)))
53
54
55 1
def _verify_total_matches(
56
    codes: Set[TicketCode], requested_quantity: int
57
) -> None:
58
    """Verify if the number of generated codes matches the number of
59
    requested codes.
60
61
    Raise an exception if they do not match.
62
    """
63 1
    actual_quantity = len(codes)
64 1
    if actual_quantity != requested_quantity:
65
        raise TicketCodeGenerationFailed(
66
            f'Number of generated ticket codes ({actual_quantity}) '
67
            f'does not match requested amount ({requested_quantity}).'
68
        )
69
70
71 1
class TicketCodeGenerationFailed(Exception):
72
    """Generating one or more unique ticket codes has failed."""
73