1import re
2
3def matcher(regex, string):
4 """Convert regex match into string."""
5 match = re.search(regex, string)
6 if match is None:
7 return string
8 start, end = match.span()
9 return string[start:end]
10
11
12class Kid(type):
13 """Kid metaclass"""
14
15 def __new__(cls, name, bases, attr_dict):
16 if '__call__' not in attr_dict.keys():
17 raise NotImplementedError
18 attr_dict['years_celebrated'] = 0
19 attr_dict['__init__'] = lambda self: kids.append(self)
20 return type.__new__(cls, name, bases, attr_dict)
21
22
23class Singleton(type):
24 """A metaclass that makes classes singletons."""
25 created_instances = {}
26 def __call__(cls, *args, **kwargs):
27 if cls not in cls.created_instances:
28 cls.created_instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
29 return cls.created_instances[cls]
30
31
32class PresentIterator:
33 def __init__(self, presents):
34 self.presents = presents
35 self.index = 0
36
37 def __next__(self):
38 self.index += 1
39 try:
40 return self.presents[self.index - 1]
41 except IndexError as exc:
42 self.index = 0
43 raise StopIteration from exc
44
45
46kids = [] # global list containing objects with Kid metaclass
47
48class Santa(metaclass=Singleton):
49 """Festive singleton class that gives presents (or not)."""
50 kid_presents = {} # each kid has a list of presents
51
52 def __call__(self, kid, letter):
53 # regex to read the wish from the letter
54 wish = matcher(r'[\'\"](([a-z])*([A-z])*(\d)*( )*)*[\'\"]', letter)
55 wish = wish[1:-1] # trim the quotation marks
56 if id(kid) in self.kid_presents:
57 self.kid_presents[id(kid)].append(wish)
58 else:
59 self.kid_presents[id(kid)] = [wish]
60
61 def __matmul__(self, letter):
62 kid_id = matcher(r'(\n)(\s)*(\d)+(\s)*', letter) # with whitespace characters
63 kid_id = int(matcher(r'\d+', kid_id)) # digits only here
64 wish = matcher(r'[\'\"](([a-z])*([A-z])*(\d)*( )*)*[\'\"]', letter)
65 wish = wish[1:-1] # trim the quotation marks
66
67 if kid_id in self.kid_presents:
68 self.kid_presents[kid_id].append(wish)
69 else:
70 self.kid_presents[kid_id] = [wish]
71
72 def get_presents_list(self):
73 extracted_presents = []
74 for present_list in self.kid_presents.values():
75 extracted_presents.extend(present_list)
76 return extracted_presents
77
78 def __iter__(self):
79 presents = self.get_presents_list()
80 return PresentIterator(presents)
81
82 def xmas(self):
83 if not any(self.kid_presents.values()):
84 for kid in kids:
85 kid.years_celebrated += 1
86 return
87
88 presents_count = {}
89 presents = self.get_presents_list()
90
91 for present in presents:
92 if present in presents_count:
93 presents_count[present] += 1
94 else:
95 presents_count[present] = 1
96
97 most_wanted_present = str(max(zip(presents_count.values(), presents_count.keys()))[1])
98
99 kids_copy = kids[:]
100 for kid in kids_copy:
101 if id(kid) not in self.kid_presents:
102 self.kid_presents[id(kid)] = [most_wanted_present]
103 if len(self.kid_presents[id(kid)]) == 0:
104 self.kid_presents[id(kid)].append(most_wanted_present)
105
106 kid.years_celebrated += 1
107 if kid.years_celebrated > 5:
108 kids.remove(kid)
109 self.kid_presents.pop(id(kid))
110 continue
111
112 # give present to a kid
113 size = len(self.kid_presents[id(kid)])
114 kid(self.kid_presents[id(kid)][size - 1]) # gets the last present from the kid's list
115 self.kid_presents[id(kid)].remove(self.kid_presents[id(kid)][size - 1])
...FF..F.....FEE..E.
======================================================================
ERROR: test_xmass_no_wishes (test.TestSanta.test_xmass_no_wishes)
Test a Christmas with no wishes.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 151, in test_xmass_no_wishes
self.assertEqual(kid1.SECRET_PRESENT, None)
^^^^^^^^^^^^^^^^^^^
AttributeError: 'KidClass1' object has no attribute 'SECRET_PRESENT'
======================================================================
ERROR: test_xmass_no_wishes_but_naughty_kids (test.TestSanta.test_xmass_no_wishes_but_naughty_kids)
Test a Christmas with no wishes, but naughty kids present.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 163, in test_xmass_no_wishes_but_naughty_kids
self.assertEqual(kid1.SECRET_PRESENT, None)
^^^^^^^^^^^^^^^^^^^
AttributeError: 'KidClass1' object has no attribute 'SECRET_PRESENT'
======================================================================
ERROR: test_xmass_years_5_and_over (test.TestSanta.test_xmass_years_5_and_over)
Test with passing years with kid aged 5 and over.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 213, in test_xmass_years_5_and_over
self.assertEqual(kid1.SECRET_PRESENT, None)
^^^^^^^^^^^^^^^^^^^
AttributeError: 'KidClass1' object has no attribute 'SECRET_PRESENT'
======================================================================
FAIL: test_call_and_mail_same_kid (test.TestSanta.test_call_and_mail_same_kid)
Test that calls and mails work for the same kid.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 71, in test_call_and_mail_same_kid
self.assertEqual(list(self.santa), ['toy1'])
AssertionError: Lists differ: ['toy1', 'toy1'] != ['toy1']
First list contains 1 additional elements.
First extra element 1:
'toy1'
- ['toy1', 'toy1']
+ ['toy1']
======================================================================
FAIL: test_iterable (test.TestSanta.test_iterable)
Ensure Santa can be iterated multiple times including overwriting presents.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 129, in test_iterable
self.assertEqual(list(self.santa), ['something', 'something else'])
AssertionError: Lists differ: ['something', 'something not used', 'something else'] != ['something', 'something else']
First differing element 1:
'something not used'
'something else'
First list contains 1 additional elements.
First extra element 2:
'something else'
- ['something', 'something not used', 'something else']
+ ['something', 'something else']
======================================================================
FAIL: test_santa_gift_order (test.TestSanta.test_santa_gift_order)
Test ordering of the Santa iterator.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 260, in test_santa_gift_order
self.assertEqual(list(self.santa), ["toy2v2", "toy3", "toy1"])
AssertionError: Lists differ: ['toy2', 'toy2v2', 'toy3', 'toy1'] != ['toy2v2', 'toy3', 'toy1']
First differing element 0:
'toy2'
'toy2v2'
First list contains 1 additional elements.
First extra element 3:
'toy1'
- ['toy2', 'toy2v2', 'toy3', 'toy1']
? --------
+ ['toy2v2', 'toy3', 'toy1']
======================================================================
FAIL: test_xmass_naughty (test.TestSanta.test_xmass_naughty)
Test a Christmas with naughty kids.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 227, in test_xmass_naughty
self.assertEqual(kid1.SECRET_PRESENT, 'coal')
AssertionError: 'sirenie' != 'coal'
- sirenie
+ coal
----------------------------------------------------------------------
Ran 20 tests in 0.040s
FAILED (failures=4, errors=3)
20.12.2024 10:24
20.12.2024 10:25
20.12.2024 10:30
20.12.2024 10:26
20.12.2024 10:25
20.12.2024 10:27
20.12.2024 10:29
20.12.2024 10:29