More changes for python3 and improved logging/debugging. This ~works.
[kiosk.git] / chooser.py
1 import os
2 import random
3 import re
4 import sys
5 import time
6 import glob
7 import constants
8 import trigger
9
10 class chooser(object):
11     """Base class of a thing that chooses pages"""
12     def get_page_list(self):
13         now = time.time()
14         valid_filename = re.compile("([^_]+)_(\d+)_([^\.]+)\.html")
15         filenames = []
16         pages = [ f for f in os.listdir(constants.pages_dir)
17                   if os.path.isfile(os.path.join(constants.pages_dir, f))]
18         for page in pages:
19             result = re.match(valid_filename, page)
20             if result != None:
21                 print(('chooser: candidate page: "%s"' % page))
22                 if (result.group(3) != "none"):
23                     freshness_requirement = int(result.group(3))
24                     last_modified = int(os.path.getmtime(
25                         os.path.join(constants.pages_dir, page)))
26                     age = (now - last_modified)
27                     if (age > freshness_requirement):
28                         print(('chooser: "%s" is too old.' % page))
29                         continue
30                 filenames.append(page)
31         return filenames
32
33     def choose_next_page(self):
34         pass
35
36 class weighted_random_chooser(chooser):
37     """Chooser that does it via weighted RNG"""
38     def __init__(self):
39         self.last_choice = ""
40         self.valid_filename = re.compile("([^_]+)_(\d+)_([^\.]+)\.html")
41         self.pages = None
42         self.count = 0
43
44     def choose_next_page(self):
45         if (self.pages == None or
46             self.count % 100 == 0):
47             self.pages = self.get_page_list()
48
49         total_weight = 0
50         weights = []
51         for page in self.pages:
52             result = re.match(self.valid_filename, page)
53             if result != None:
54                 weight = int(result.group(2))
55                 weights.append(weight)
56                 total_weight += weight
57
58         if (total_weight <= 0):
59             raise error
60
61         while True:
62             pick = random.randrange(0, total_weight - 1)
63             so_far = 0
64             for x in range(0, len(weights)):
65                 so_far += weights[x]
66                 if (so_far > pick and
67                     self.pages[x] != self.last_choice):
68                     self.last_choice = self.pages[x]
69                     self.count += 1
70                     return self.pages[x]
71
72 class weighted_random_chooser_with_triggers(weighted_random_chooser):
73     """Same as WRC but has trigger events"""
74     def __init__(self, trigger_list):
75         weighted_random_chooser.__init__(self)
76         self.trigger_list = trigger_list
77         self.page_queue = set(())
78
79     def check_for_triggers(self):
80         triggered = False
81         for t in self.trigger_list:
82             x = t.get_triggered_page_list()
83             if x != None and len(x) > 0:
84                 for y in x:
85                     self.page_queue.add(y)
86                     triggered = True
87         return triggered
88
89     def choose_next_page(self):
90         if (self.pages == None or
91             self.count % 100 == 0):
92             self.pages = self.get_page_list()
93
94         triggered = self.check_for_triggers()
95
96         # First try to satisfy from the page queue
97         if (len(self.page_queue) > 0):
98             print("Pulling page from queue")
99             page = None
100             priority = None
101             for t in self.page_queue:
102                 if priority == None or t[1] > priority:
103                     page = t[0]
104                     priority = t[1]
105             self.page_queue.remove((page, priority))
106             return page, triggered
107
108         # Fall back on weighted random choice.
109         else:
110             return weighted_random_chooser.choose_next_page(self), False
111
112 class rotating_chooser(chooser):
113     """Chooser that does it in a rotation"""
114     def __init__(self):
115         self.valid_filename = re.compile("([^_]+)_(\d+)_([^\.]+)\.html")
116         self.pages = None
117         self.current = 0
118         self.count = 0
119
120     def choose_next_page(self):
121         if (self.pages == None or
122             self.count % 100 == 0):
123             self.pages = self.get_page_list()
124
125         if len(self.pages) == 0:
126             raise error
127
128         if (self.current >= len(self.pages)):
129             self.current = 0
130
131         page = self.pages[self.current]
132         self.current += 1
133         self.count += 1
134         return page
135
136 #x = weighted_random_chooser_with_triggers(None)