33ccfc05ca2203917bab3ceabc53259ed7c42a12
[kiosk.git] / stranger_renderer.py
1 from bs4 import BeautifulSoup
2 import datetime
3 import file_writer
4 import grab_bag
5 import http.client
6 import page_builder
7 import profanity_filter
8 import random
9 import re
10 import renderer
11
12 class stranger_events_renderer(renderer.debuggable_abstaining_renderer):
13     def __init__(self, name_to_timeout_dict):
14         super(stranger_events_renderer, self).__init__(name_to_timeout_dict, True)
15         self.feed_site = "everout.thestranger.com"
16         self.events = grab_bag.grab_bag()
17
18     def debug_prefix(self):
19         return "stranger"
20
21     def periodic_render(self, key):
22         self.debug_print("called for action %s" % key)
23         if key == "Fetch Events":
24             return self.fetch_events()
25         elif key == "Shuffle Events":
26             return self.shuffle_events()
27         else:
28             raise error("Unknown operaiton")
29
30     def get_style(self):
31         return """
32 <STYLE>
33 .calendar-post {
34   line-height: 96%;
35 }
36 .calendar-post-title a {
37   text-decoration: none;
38   color:black;
39   font-size:125%
40   line-height:104%;
41 }
42 .calendar-post-date {
43 }
44 .calendar-post-category {
45 }
46 .calendar-post-location {
47 }
48 .calendar-post-price {
49 }
50 .calendar-touch-link {
51 }
52 .calendar-category {
53   background-color:lightyellow;
54   color:black;
55   border:none;
56   font-size:50%;
57   padding:1px;
58 }
59 .calendar-post-price-mobile {
60   visibility: hidden;
61 }
62 .img-responsive {
63   float: left;
64   margin: 10px 10px 10px 10px;
65 }
66 </STYLE>"""
67
68     def shuffle_events(self):
69         layout = page_builder.page_builder()
70         layout.set_layout(page_builder.page_builder.LAYOUT_FOUR_ITEMS)
71         layout.set_title("Stranger Events")
72         layout.set_style(self.get_style())
73         subset = self.events.subset(4)
74         if subset is None:
75             self.debug_print("Not enough events to build page.")
76             return False
77
78         for msg in subset:
79             layout.add_item(msg)
80         f = file_writer.file_writer('stranger-events_2_none.html')
81         layout.render_html(f)
82         f.close()
83         return True
84
85     def fetch_events(self):
86         self.events.clear()
87         feed_uris = [
88             "/events/?page=1&picks=true",
89             "/events/?page=2&picks=true",
90             "/events/?page=3&picks=true",
91         ]
92         now = datetime.datetime.now()
93         ts = now + datetime.timedelta(1)
94         tomorrow = datetime.datetime.strftime(ts, "%Y-%m-%d")
95         feed_uris.append("/events/?start-date=%s&picks=true" % tomorrow)
96         delta = 5 - now.weekday()
97         if delta <= 0:
98             delta += 7
99         if delta > 1:
100             ts = now + datetime.timedelta(delta)
101             next_sat = datetime.datetime.strftime(ts, "%Y-%m-%d")
102             feed_uris.append("/events/?start-date=%s&page=1&picks=true" % next_sat)
103             feed_uris.append("/events/?start-date=%s&page=2&picks=true" % next_sat)
104         delta += 1
105         if delta > 1:
106             ts = now + datetime.timedelta(delta)
107             next_sun = datetime.datetime.strftime(ts, "%Y-%m-%d")
108             feed_uris.append("/events/?start-date=%s&page=1&picks=true" % next_sun)
109             feed_uris.append("/events/?start-date=%s&page=2&picks=true" % next_sun)
110
111         for uri in feed_uris:
112             try:
113                 self.debug_print("fetching '%s'" % uri)
114                 self.conn = http.client.HTTPSConnection(self.feed_site)
115                 self.conn.request(
116                     "GET",
117                     uri,
118                     None,
119                     {"Accept-Charset": "utf-8"})
120                 response = self.conn.getresponse()
121                 if response.status != 200:
122                     self.debug_print("Connection failed, status %d" % (
123                         response.status))
124                     continue
125                 raw = response.read()
126             except:
127                 self.debug_print("Exception talking to the stranger, ignoring.")
128                 continue
129
130             soup = BeautifulSoup(raw, "html.parser")
131             filter = profanity_filter.profanity_filter()
132             for x in soup.find_all('div', class_='row event list-item mb-3 py-3'):
133                 text = x.get_text();
134                 if (filter.contains_bad_words(text)):
135                     continue
136                 raw = str(x)
137                 raw = raw.replace('src="/',
138                                   'align="left" src="https://www.thestranger.com/')
139                 raw = raw.replace('href="/',
140                                   'href="https://www.thestranger.com/')
141                 raw = raw.replace('FREE', 'Free')
142                 raw = raw.replace('Save Event', '')
143                 raw = re.sub('^\s*$', '', raw, 0, re.MULTILINE)
144                 raw = re.sub('\n+', '\n', raw)
145                 raw = re.sub('<span[^<>]*class="calendar-post-ticket"[^<>]*>.*</#span>', '', raw, 0, re.DOTALL | re.IGNORECASE)
146                 self.events.add(raw.encode('utf-8'))
147             self.debug_print("fetched %d events so far." % self.events.size())
148         return self.events.size() > 0
149
150 # Test
151 #x = stranger_events_renderer({"Test", 123})
152 #x.periodic_render("Fetch Events")
153 #x.periodic_render("Shuffle Events")