]> gitweb.pimeys.fr Git - today.git/blob - today_server.py
[server] Meilleur débugging des exceptions
[today.git] / today_server.py
1 #!/usr/bin/python
2 # -*- encoding: utf-8 -*-
3
4 """ Codé par 20-100
5 script qui affiche des trucs à penser, des J-n des conneries
6 or that kind of stuff.
7
8 Partie serveur, prévue pour chercher périodiquement les trucs non lus
9 et répondre à un check.
10
11 """
12
13 import re
14 import BeautifulSoup
15 from lxml import etree
16 import os
17 import sys
18 import urllib
19 import json
20 import traceback
21 import inspect
22 import pprint
23 os.chdir('/home/vincent/scripts/today/')
24 sys.path.append("/home/vincent/scripts/dtc/")
25 import dtc
26
27 #: Fichier où sont stockés les derniers IDs des trucs
28 store_published_file = "lasts_published"
29
30 #: Fichier où est stockée une copie de la liste des derniers IDs *lus*
31 store_seen_file = "lasts_sync"
32
33 #: Afficher du garbage pour débuguer
34 DEBUG = False
35
36 def last_dtc():
37 """Vérifie si il y a des quotes DTC non lues"""
38 return dtc.last_inserted()
39
40 def last_xkcd():
41 p = urllib.urlopen("http://xkcd.com")
42 t = p.read()
43 current_id = int(re.findall("Permanent link to this comic: http://xkcd.com/(.*?)/", t)[0])
44 return current_id
45
46 def last_xantah():
47 p = urllib.urlopen("http://www.adoprixtoxis.com/lite/download/xantah_downloads.php")
48 t = p.read()
49 ids = re.findall("""<div class="top">Xantah (.*?)</div>""", t)
50 ids = [int(i) for i in ids]
51 return max(ids)
52
53 def last_visiteur():
54 p = urllib.urlopen("http://www.levisiteurdufutur.com/episodes.html")
55 t = p.read()
56 # On parse
57 soup = BeautifulSoup.BeautifulSoup(t)
58 # On récupère les différentes saisons
59 saisons = soup.findAll("div", attrs={"id" : "episodes_list"})
60 nsaisons = len(saisons)
61 # La saison en cours est la première dans la liste
62 episodes = saisons[0].findAll("div", attrs={"class" : "thumbCaption"})
63 nepisodes = len(episodes)
64 return nsaisons * 100 + nepisodes
65
66 def parse_youtube(username):
67 """Récupère les vidéos d'une chaîne Youtube"""
68 link = "https://gdata.youtube.com/feeds/api/users/%s/uploads?start-index=1&max-results=50" % (username,)
69 entries = []
70 while link:
71 p = urllib.urlopen(link)
72 t = p.read()
73 x = etree.fromstring(t)
74 # lxml ne supporte pas les namespaces vides dans les requêtes XPath
75 ns = x.nsmap
76 ns["default"] = ns[None]
77 ns.pop(None)
78 # Il y a potentiellement une suite
79 nextlinks = x.xpath("//default:link[@rel='next']", namespaces=ns)
80 if nextlinks:
81 link = nextlinks[0].attrib["href"]
82 else:
83 link = False
84 localentries = x.xpath("//default:entry", namespaces=ns)
85 entries.extend(localentries)
86 titles = [e.xpath(".//default:title", namespaces=ns)[0].text for e in entries]
87 return titles
88
89 def get_season_episode(title):
90 """Récupère les numéros de la saison et de l'épisode. Crash si ne trouve pas."""
91 ep = int(re.findall("ep([0-9]*)", title)[0])
92 saison = int(re.findall("s([0-9]*)", title)[0])
93 return saison, ep
94
95 def last_noob_warpzone():
96 global last_nw
97 # GRUIK
98 if "last_nw" in globals().keys():
99 return last_nw
100 titles = parse_youtube("Funglisoft")
101 noobs = [t.lower().strip() for t in titles if t.lower().strip().startswith("noob")]
102 warpzones = [t.lower().strip() for t in titles if t.lower().strip().startswith("warpzone project")]
103 lasts = []
104 for serie in [noobs, warpzones]:
105 # Les titres sont dans l'ordre antichronologique, on s'arrête donc au premier qu'on comprend
106 for titre in serie:
107 if "noob le film" in titre or "making of" in titre or "noob versus rct" == titre:
108 continue
109 try:
110 if DEBUG:
111 print titre
112 saison, ep = get_season_episode(titre)
113 except ValueError:
114 pass
115 lasts.append([saison, ep])
116 del saison, ep
117 break
118 last_noob = lasts[0][0]*100 + lasts[0][1]
119 last_warp = lasts[1][0]*100 + lasts[1][1]
120 last_nw = [last_noob, last_warp]
121 return last_nw
122
123 def last_noob():
124 return last_noob_warpzone()[0]
125 def last_warpzone():
126 return last_noob_warpzone()[1]
127
128 def last_hugo():
129 titles = parse_youtube("HugoToutSeul")
130 return len(titles)
131
132 def last_norman():
133 titles = parse_youtube("NormanFaitDesVideos")
134 return len(titles)
135
136 def last_cyprien():
137 titles = parse_youtube("MonsieurDream")
138 return len(titles)
139
140 def last_grenier():
141 titles = parse_youtube("joueurdugrenier")
142 return len(titles)
143
144 def last_jl8():
145 rss = urllib.urlopen("http://limbero.org/jl8/rss/")
146 t = rss.read()
147 x = etree.fromstring(t)
148 links = x.xpath("//link")
149 maxnum = links[1].text.split("/")[-1]
150 maxnum = int(maxnum)
151 return maxnum
152
153 def get_file():
154 """Récupère la liste des derniers ids de chaque truc, stockée dans le fichier."""
155 f = open(store_published_file)
156 news = json.load(f)
157 f.close()
158 return news
159
160 def update_file(news):
161 """Met à jour la liste des derniers ids dans le fichier."""
162 f = open(store_published_file, 'w')
163 json.dump(news, f)
164 f.close()
165
166 FETCHS = {
167 "xkcd" : last_xkcd,
168 "dtc" : last_dtc,
169 "xantah" : last_xantah,
170 "visiteur" : last_visiteur,
171 "noob" : last_noob,
172 "warpzone" : last_warpzone,
173 "hugo" : last_hugo,
174 "norman" : last_norman,
175 "cyprien" : last_cyprien,
176 "grenier" : last_grenier,
177 "dc" : last_jl8,
178 }
179
180 def fetch_all():
181 """Va chercher sur les différents sites les nouveaux trucs."""
182 news = {}
183 for (k, f) in FETCHS.iteritems():
184 try:
185 news[k] = f()
186 except Exception as e:
187 errmsg = "Erreur à la récupération de %s :\n" % k
188 errmsg += traceback.format_exc()
189 # On dumpe le contenu local de la mémoire au moment de l'exception
190 fobj = inspect.trace()[-1][0]
191 # On fait un peu de ménage
192 d = {k:v for (k,v) in fobj.f_locals.items() if not k.startswith("_")}
193 errmsg += "\nContexte : %s\n\n" % (pprint.pformat(d))
194 print errmsg
195 return news
196
197 def sync():
198 """Reçoit une requête de synchronisation."""
199 # On récupère où en est le client sur stdin
200 t = sys.stdin.read()
201 on_client = json.loads(t)
202 # On récupère où en est le serveur dans le fichier idoine
203 if os.path.isfile(store_seen_file):
204 on_server = json.load(open(store_seen_file))
205 else:
206 on_server = {}
207 # On garde le maximum
208 for k in set(on_client.keys() + on_server.keys()):
209 on_server[k] = max(on_client.get(k, 0), on_server.get(k, 0))
210 # On enregistre ce nouveau dico
211 json.dump(on_server, open(store_seen_file, "w"))
212 # On envoie au client ce nouveau dico
213 print json.dumps(on_server)
214
215 if __name__ == "__main__":
216 if "--debug" in sys.argv or "--verbose" in sys.argv:
217 DEBUG = True
218 if sys.argv[1] == "check":
219 news = fetch_all()
220 if "--init" in sys.argv:
221 olds = news
222 else:
223 olds = get_file()
224 olds.update(news)
225 update_file(olds)
226 elif sys.argv[1] == "whatsup":
227 news = get_file()
228 print json.dumps(news)
229 elif sys.argv[1] == "sync":
230 sync()