Cleanup
[kiosk.git] / chooser.py
1 import datetime
2 import os
3 import random
4 import re
5 import sys
6 import time
7 import glob
8 import constants
9 import trigger
10
11
12 class chooser(object):
13     """Base class of a thing that chooses pages"""
14
15     def get_page_list(self):
16         now = time.time()
17         valid_filename = re.compile("([^_]+)_(\d+)_([^\.]+)\.html")
18         filenames = []
19         pages = [
20             f
21             for f in os.listdir(constants.pages_dir)
22             if os.path.isfile(os.path.join(constants.pages_dir, f))
23         ]
24         for page in pages:
25             result = re.match(valid_filename, page)
26             if result != None:
27                 print(('chooser: candidate page: "%s"' % page))
28                 if result.group(3) != "none":
29                     freshness_requirement = int(result.group(3))
30                     last_modified = int(
31                         os.path.getmtime(os.path.join(constants.pages_dir, page))
32                     )
33                     age = now - last_modified
34                     if age > freshness_requirement:
35                         print(('chooser: "%s" is too old.' % page))
36                         continue
37                 filenames.append(page)
38         return filenames
39
40     def choose_next_page(self):
41         pass
42
43
44 class weighted_random_chooser(chooser):
45     """Chooser that does it via weighted RNG."""
46
47     def dont_choose_page_twice_in_a_row_filter(self, choice):
48         if choice == self.last_choice:
49             return False
50         self.last_choice = choice
51         return True
52
53     def __init__(self, filter_list):
54         self.last_choice = ""
55         self.valid_filename = re.compile("([^_]+)_(\d+)_([^\.]+)\.html")
56         self.pages = None
57         self.count = 0
58         self.filter_list = filter_list
59         if filter_list is None:
60             self.filter_list = []
61         self.filter_list.append(self.dont_choose_page_twice_in_a_row_filter)
62
63     def choose_next_page(self):
64         if self.pages == None or self.count % 100 == 0:
65             self.pages = self.get_page_list()
66
67         total_weight = 0
68         weights = []
69         for page in self.pages:
70             result = re.match(self.valid_filename, page)
71             if result != None:
72                 weight = int(result.group(2))
73                 weights.append(weight)
74                 total_weight += weight
75         if total_weight <= 0:
76             raise error
77
78         while True:
79             random_pick = random.randrange(0, total_weight - 1)
80             so_far = 0
81             for x in range(0, len(weights)):
82                 so_far += weights[x]
83                 if so_far > random_pick:
84                     break
85             choice = self.pages[x]
86
87             # Allow filter list to suppress pages.
88             choice_is_filtered = False
89             for f in self.filter_list:
90                 if not f(choice):
91                     print("chooser: %s filtered by %s" % (choice, f.__name__))
92                     choice_is_filtered = True
93                     break
94             if choice_is_filtered:
95                 continue
96
97             # We're good...
98             self.count += 1
99             return choice
100
101
102 class weighted_random_chooser_with_triggers(weighted_random_chooser):
103     """Same as WRC but has trigger events"""
104
105     def __init__(self, trigger_list, filter_list):
106         weighted_random_chooser.__init__(self, filter_list)
107         self.trigger_list = trigger_list
108         if trigger_list is None:
109             self.trigger_list = []
110         self.page_queue = set(())
111
112     def check_for_triggers(self):
113         triggered = False
114         for t in self.trigger_list:
115             x = t.get_triggered_page_list()
116             if x != None and len(x) > 0:
117                 for y in x:
118                     self.page_queue.add(y)
119                     triggered = True
120         return triggered
121
122     def choose_next_page(self):
123         if self.pages == None or self.count % 100 == 0:
124             self.pages = self.get_page_list()
125
126         triggered = self.check_for_triggers()
127
128         # First try to satisfy from the page queue.
129         if len(self.page_queue) > 0:
130             print("chooser: Pulling page from queue...")
131             page = None
132             priority = None
133             for t in self.page_queue:
134                 if priority == None or t[1] > priority:
135                     page = t[0]
136                     priority = t[1]
137             self.page_queue.remove((page, priority))
138             return page, triggered
139
140         # Fall back on weighted random choice.
141         else:
142             return weighted_random_chooser.choose_next_page(self), False
143
144
145 class rotating_chooser(chooser):
146     """Chooser that does it in a rotation"""
147
148     def __init__(self):
149         self.valid_filename = re.compile("([^_]+)_(\d+)_([^\.]+)\.html")
150         self.pages = None
151         self.current = 0
152         self.count = 0
153
154     def choose_next_page(self):
155         if self.pages == None or self.count % 100 == 0:
156             self.pages = self.get_page_list()
157
158         if len(self.pages) == 0:
159             raise error
160
161         if self.current >= len(self.pages):
162             self.current = 0
163
164         page = self.pages[self.current]
165         self.current += 1
166         self.count += 1
167         return page
168
169
170 # Test
171 def filter_news_during_dinnertime(page):
172     now = datetime.datetime.now()
173     is_dinnertime = now.hour >= 17 and now.hour <= 20
174     return not is_dinnertime or not (
175         "cnn" in page
176         or "news" in page
177         or "mynorthwest" in page
178         or "seattle" in page
179         or "stranger" in page
180         or "twitter" in page
181         or "wsj" in page
182     )
183
184
185 # x = weighted_random_chooser_with_triggers([], [ filter_news_during_dinnertime ])
186 # print(x.choose_next_page())