Commit ca3ad95878bbeb76f00b3a39cd52ee60a7fba2e7
1 parent
b6c8c0a6
Exists in
master
and in
1 other branch
AppRecommender server refactoring.
Showing
1 changed file
with
73 additions
and
246 deletions
Show diff stats
src/web/server.py
1 | 1 | #!/usr/bin/env python |
2 | 2 | |
3 | +import os | |
3 | 4 | import web |
4 | 5 | from web import form |
5 | 6 | import tempfile |
... | ... | @@ -7,263 +8,84 @@ import sys |
7 | 8 | import simplejson as json |
8 | 9 | import apt |
9 | 10 | import re |
11 | +import socket | |
10 | 12 | |
11 | -sys.path.insert(0,"../") | |
13 | +sys.path.insert(0,"/var/www/AppRecommender/src/") | |
12 | 14 | |
13 | -from config import * | |
15 | +from config import Config | |
14 | 16 | from recommender import * |
15 | 17 | from user import * |
18 | +from data import DebianPackage | |
16 | 19 | |
17 | 20 | import urllib |
18 | 21 | |
19 | -#class FeedbackForm(form.Form): | |
20 | -# def __init__(self,selected_strategies): | |
21 | -# desc_dict = {"cb": "Content-based", "cbt": "Content-based", | |
22 | -# "cbd": "Content-based", "col": "Collaborative", | |
23 | -# "hybrid": "Hybrib"} | |
24 | -# fields = [] | |
25 | -# for strategy in selected_strategies: | |
26 | -# fields.append(form.Radio(desc_dict[strategy], | |
27 | -# [('1','1 '),('2','2 '),('3','3'),('4','4 '),('5','5')])) | |
28 | -# form.Form.__init__(self, *fields, validators = []) | |
22 | +# avoid "RuntimeError: maximum recursion depth exceeded" | |
23 | +sys.setrecursionlimit(50000) | |
24 | + | |
25 | +class Fake: | |
26 | + def GET(self): | |
27 | + return render_plain.fake() | |
29 | 28 | |
30 | 29 | class Index: |
31 | 30 | def GET(self): |
32 | - if Config().survey_mode: | |
33 | - return render.survey_index() | |
34 | - else: | |
35 | 31 | return render.index() |
36 | 32 | |
37 | 33 | class About: |
38 | 34 | def GET(self): |
39 | 35 | return render.about() |
40 | 36 | |
41 | -#class Support: | |
42 | -# def GET(self): | |
43 | -# return render.support() | |
44 | - | |
45 | -class Thanks: | |
46 | - def POST(self): | |
47 | - return render.thanks() | |
48 | - | |
49 | 37 | class Package: |
50 | - def GET(self, pkg): | |
51 | - result = self.get_details_from_dde(pkg) | |
52 | - return render_plain.package(result) | |
53 | - | |
54 | - def get_details_from_dde(self, pkg): | |
55 | - json_source = Config().dde_url % pkg | |
56 | - json_data = json.load(urllib.urlopen(json_source)) | |
57 | - # parse tags | |
58 | - tags = self._debtags_list_to_dict(json_data['r']['tag']) | |
59 | - json_data['r']['tag'] = tags | |
60 | - # format long description | |
61 | - json_data['r']['long_description'] = json_data['r']['long_description'].replace(' .\n','').replace('\n','<br />') | |
62 | - return json_data['r'] | |
63 | - | |
64 | - def _debtags_list_to_dict(self, debtags_list): | |
65 | - """ in: | |
66 | - ['use::editing', | |
67 | - 'works-with-format::gif', | |
68 | - 'works-with-format::jpg', | |
69 | - 'works-with-format::pdf'] | |
70 | - out: | |
71 | - {'use': [editing], | |
72 | - 'works-with-format': ['gif', 'jpg', 'pdf']' | |
73 | - } | |
74 | - """ | |
75 | - debtags = {} | |
76 | - subtags = [] | |
77 | - for tag in debtags_list: | |
78 | - match = re.search(r'^(.*)::(.*)$', tag) | |
79 | - if not match: | |
80 | - log.error("Could not parse debtags format from tag: %s", tag) | |
81 | - facet, subtag = match.groups() | |
82 | - subtags.append(subtag) | |
83 | - if facet not in debtags: | |
84 | - debtags[facet] = subtags | |
85 | - else: | |
86 | - debtags[facet].append(subtag) | |
87 | - subtags = [] | |
88 | - return debtags | |
89 | - | |
90 | -class Request: | |
91 | - def __init__(self,web_input): | |
92 | - submissions_dir = "./submissions/" | |
93 | - if not os.path.exists(submissions_dir): | |
94 | - os.makedirs(submissions_dir) | |
95 | - outputdir = tempfile.mkdtemp(prefix='',dir=submissions_dir) | |
96 | - user_id = outputdir.lstrip(submissions_dir) | |
97 | - self.user_id = user_id | |
98 | - self.storage = web_input | |
99 | - | |
100 | - self.pkgs_list = [] | |
101 | - if web_input.has_key('pkgs_list'): | |
102 | - self.pkgs_list = web_input['pkgs_list'].encode('utf8').split() | |
103 | - if web_input['pkgs_file'].value: | |
104 | - f = open(outputdir + "/packages_list", "wb") | |
105 | - lines = web_input['pkgs_file'].file.readlines() | |
106 | - if lines[0].startswith('POPULARITY-CONTEST'): | |
107 | - del lines[0] | |
108 | - del lines[-1] | |
109 | - package_name_field = 2 | |
110 | - else: | |
111 | - package_name_field = 0 | |
112 | - for line in lines: | |
113 | - self.pkgs_list.append(line.split()[package_name_field]) | |
114 | - f.write(line) | |
115 | - f.close() | |
116 | - | |
117 | - if web_input.has_key('limit'): | |
118 | - self.limit = int(web_input['limit']) | |
119 | - if web_input.has_key('profile_size'): | |
120 | - self.profile_size = int(web_input['profile_size']) | |
121 | - if web_input.has_key('weight'): | |
122 | - self.weight = web_input['weight'].encode('utf8') | |
123 | - | |
124 | - self.selected_strategies = [] | |
125 | - if web_input.has_key('strategy'): | |
126 | - if (web_input['strategy'].encode('utf8') == "content" or | |
127 | - web_input['strategy'].encode('utf8') == "hybrid"): | |
128 | - if (web_input.has_key('tag') and web_input.has_key('desc')): | |
129 | - self.selected_strategies.append("cb") | |
130 | - elif web_input.has_key('desc'): | |
131 | - self.selected_strategies.append("cbd") | |
132 | - else: | |
133 | - self.selected_strategies.append("cbt") | |
134 | - if (web_input['strategy'].encode('utf8') == "collab" or | |
135 | - web_input['strategy'].encode('utf8') == "hybrid"): | |
136 | - self.selected_strategies.append("col") | |
137 | - | |
138 | - if web_input.has_key('cluster'): | |
139 | - self.cluster = web_input['cluster'].encode('utf8') | |
140 | - | |
141 | - if web_input.has_key('neighbours'): | |
142 | - self.neighbours = int(web_input['neighbours']) | |
143 | - | |
144 | - self.profiles_set = set() | |
145 | - if web_input.has_key('profile_desktop'): | |
146 | - self.profiles_set.add("desktop") | |
147 | - if web_input.has_key('profile_admin'): | |
148 | - self.profiles_set.add("admin") | |
149 | - if web_input.has_key('profile_devel'): | |
150 | - self.profiles_set.add("devel") | |
151 | - if web_input.has_key('profile_science'): | |
152 | - self.profiles_set.add("science") | |
153 | - if web_input.has_key('profile_arts'): | |
154 | - self.profiles_set.add("arts") | |
155 | - | |
156 | - def __str__(self): | |
157 | - return self.storage | |
158 | - | |
159 | - def validates(self): | |
160 | - self.errors = [] | |
161 | - if not self.pkgs_list: | |
162 | - self.errors.append("No upload file or packages list was provided.") | |
163 | - if not self.selected_strategies: | |
164 | - self.errors.append("No strategy was selected.") | |
165 | - if self.errors: | |
166 | - return False | |
167 | - return True | |
168 | - | |
169 | - def get_details(self): | |
170 | - details = {} | |
171 | - details['User id'] = self.user_id | |
172 | - if len(self.pkgs_list)>10: | |
173 | - details['Packages list'] = (" ".join(self.pkgs_list[:5]) | |
174 | - +" ... ("+str(len(self.pkgs_list)-10) | |
175 | - +" more)") | |
176 | - else: | |
177 | - details['Packages list'] = " ".join(self.pkgs_list) | |
178 | - | |
179 | - if self.storage.has_key('pkgs_list') and self.storage['pkgs_file']: | |
180 | - details['User profile'] = "from upload file and packages list" | |
181 | - elif self.storage.has_key('pkgs_list'): | |
182 | - details['User profile'] = "from packages list" | |
183 | - else: | |
184 | - details['User profile'] = "from upload file" | |
185 | - | |
186 | - if self.storage.has_key('limit'): | |
187 | - details['Recommendation size'] = self.limit | |
188 | - if self.storage.has_key('profile_size'): | |
189 | - details['Profile size'] = self.profile_size | |
190 | - if self.storage.has_key('weight'): | |
191 | - if self.weight == "trad": | |
192 | - details['weight'] = "traditional" | |
193 | - else: | |
194 | - details['weight'] = "BM25" | |
195 | - | |
196 | - if self.profiles_set: | |
197 | - details['Personal profile'] = " ".join(list(self.profiles_set)) | |
198 | - | |
199 | - if len(self.selected_strategies) > 1: details['strategy'] = "Hybrid" | |
200 | - for strategy in self.selected_strategies: | |
201 | - if strategy == "col": | |
202 | - if not details.has_key('strategy'): | |
203 | - details['Strategy'] = "Collaborative" | |
204 | - if self.storage.has_key('cluster'): | |
205 | - details['Cluster'] = self.cluster | |
206 | - if self.storage.has_key('neighbours'): | |
207 | - details['Neighbours'] = self.neighbours | |
208 | - if not details.has_key('strategy'): | |
209 | - details['Strategy'] = "Collaborative" | |
210 | - else: | |
211 | - if not details.has_key('strategy'): | |
212 | - details['Strategy'] = "Content-based" | |
213 | - if "cb" in self.selected_strategies: | |
214 | - details['Content representation'] = "tags and descriptions" | |
215 | - elif "cbt" in self.selected_strategies: | |
216 | - details['Content representation'] = "packages tags" | |
217 | - elif "cbd" in self.selected_strategies: | |
218 | - details['Content representation'] = "packages descriptions" | |
219 | - | |
220 | - print details | |
221 | - return details | |
222 | - | |
223 | -#class RandomRequest(Request): | |
224 | -# def __init__(self): | |
225 | -# pass | |
226 | -# #self.storage = web.Storage() | |
38 | + def GET(self, pkg_name): | |
39 | + cfg = Config() | |
40 | + pkg = DebianPackage(pkg_name) | |
41 | + pkg.load_details() | |
42 | + return render_plain.package(pkg) | |
227 | 43 | |
228 | 44 | class AppRecommender: |
229 | 45 | def __init__(self): |
46 | + logging.info("Setting up AppRecommender...") | |
230 | 47 | self.cfg = Config() |
231 | 48 | self.rec = Recommender(self.cfg) |
49 | + self.requests_dir = "/var/www/AppRecommender/src/web/requests/" | |
50 | + if not os.path.exists(self.requests_dir): | |
51 | + os.makedirs(self.requests_dir) | |
232 | 52 | |
233 | 53 | def POST(self): |
234 | - request = Request(web.input(pkgs_file={})) | |
235 | - if not request.validates(): | |
236 | - return render.error(request.errors) | |
54 | + web_input = web.input(pkgs_file={}) | |
55 | + user_dir = tempfile.mkdtemp(prefix='',dir=self.requests_dir) | |
56 | + user_id = user_dir.split("/")[-1] | |
57 | + uploaded_file = os.path.join(user_dir,"uploaded_file") | |
58 | + if web_input['pkgs_file'].value: | |
59 | + lines = web_input['pkgs_file'].file.readlines() | |
60 | + with open(uploaded_file, "w") as uploaded: | |
61 | + uploaded.writelines(lines) | |
62 | + with open(uploaded_file) as uploaded: | |
63 | + if uploaded.readline().startswith('POPULARITY-CONTEST'): | |
64 | + user = PopconSystem(uploaded_file,user_id) | |
65 | + else: | |
66 | + user = PkgsListSystem(uploaded_file,user_id) | |
67 | + if len(user.pkg_profile) < 10: | |
68 | + return render.error(["Could not extract profile from uploaded file. It must have at least 10 applications."], | |
69 | + "/","RECOMMENDATION") | |
237 | 70 | else: |
238 | - recommendation = self._recommends(request) | |
239 | - ### Getting package summary (short description) ### | |
240 | - pkg_summaries = {} | |
241 | - pkg_details = [] | |
242 | - cache = apt.Cache() | |
243 | - for strategy, result in recommendation.items(): | |
244 | - for pkg in result: | |
245 | - try: | |
246 | - pkg_summaries[pkg] = cache[pkg].candidate.summary | |
247 | - pkg_details.append(Package().get_details_from_dde(pkg)) | |
248 | - except: | |
249 | - pkg_summaries[pkg] = "" | |
250 | - if Config().survey_mode: | |
251 | - return render.survey(recommendation, pkg_details, | |
252 | - FeedbackForm(request.selected_strategies)) | |
71 | + self.rec.set_strategy("knn_eset") | |
72 | + user.maximal_pkg_profile() | |
73 | + prediction = self.rec.get_recommendation(user,12).get_prediction() | |
74 | + logging.info("Prediction for user %s" % user.user_id) | |
75 | + logging.info(str(prediction)) | |
76 | + recommendation = [result[0] for result in prediction] | |
77 | + pkgs_details = [] | |
78 | + for pkg_name in recommendation: | |
79 | + logging.info("Getting details of package %s" % pkg_name) | |
80 | + pkg = DebianPackage(pkg_name) | |
81 | + pkg.load_summary() | |
82 | + pkgs_details.append(pkg) | |
83 | + if pkgs_details: | |
84 | + logging.info("Rendering recommendation...") | |
85 | + return render.apprec(pkgs_details) | |
253 | 86 | else: |
254 | - return render.apprec(recommendation, pkg_summaries, request) | |
87 | + return render.error(["No recommendation produced for the uploaded file."],"/","RECOMMENDATION") | |
255 | 88 | |
256 | - def _recommends(self,request): | |
257 | - user = User(dict.fromkeys(request.pkgs_list,1),request.user_id) | |
258 | - user.maximal_pkg_profile() | |
259 | - results = dict() | |
260 | - for strategy_str in request.selected_strategies: | |
261 | - self.rec.set_strategy(strategy_str) | |
262 | - prediction = self.rec.get_recommendation(user,10).get_prediction() | |
263 | - print prediction | |
264 | - results[strategy_str] = \ | |
265 | - [result[0] for result in prediction] | |
266 | - return results | |
267 | 89 | |
268 | 90 | # parsing json from screenshots - can be usefull in the future... |
269 | 91 | # def _packages_attrs(self, recommends): #recommends is result of _recommends() |
... | ... | @@ -279,28 +101,33 @@ class AppRecommender: |
279 | 101 | # recommended_pkgs_attrs[pkg_attrs_dict['name']] = pkg_attrs_dict |
280 | 102 | # return recommended_pkgs_attrs |
281 | 103 | |
282 | -def add_global_hook(): | |
283 | - g = web.storage({"counter": "1"}) | |
284 | - def _wrapper(handler): | |
285 | - web.ctx.globals = g | |
286 | - return handler() | |
287 | - return _wrapper | |
104 | +# to be used if it is not under apache | |
105 | +#def add_global_hook(): | |
106 | +# g = web.storage({"counter": "1"}) | |
107 | +# def _wrapper(handler): | |
108 | +# web.ctx.globals = g | |
109 | +# return handler() | |
110 | +# return _wrapper | |
288 | 111 | |
289 | -render = web.template.render('templates/', base='layout') | |
290 | -render_plain = web.template.render('templates/') | |
112 | +render = web.template.render('/var/www/AppRecommender/src/web/templates/', base='layout', globals={'hasattr':hasattr}) | |
113 | +render_plain = web.template.render('/var/www/AppRecommender/src/web/templates/', globals={'hasattr':hasattr}) | |
291 | 114 | |
292 | -urls = ('/', 'Index', | |
293 | - '/apprec', 'AppRecommender', | |
294 | - '/thanks', 'Thanks', | |
115 | +urls = ('/', 'Index', | |
116 | + '/index', 'Index', | |
117 | + '/apprec', 'AppRecommender', | |
295 | 118 | '/support', 'Support', |
296 | 119 | '/about', 'About', |
297 | - '/package/(.*)', 'Package' | |
120 | + '/package/(.*)', 'Package' | |
298 | 121 | ) |
299 | 122 | |
300 | 123 | web.webapi.internalerror = web.debugerror |
301 | 124 | |
302 | -if __name__ == "__main__": | |
303 | - apprec = web.application(urls, globals()) | |
304 | - apprec.add_processor(add_global_hook()) | |
305 | - apprec.run() | |
125 | +#if __name__ == "__main__": | |
126 | +cfg = Config() | |
127 | +app = web.application(urls, globals(), autoreload=False) | |
128 | +application = app.wsgifunc() | |
129 | + | |
130 | +# apprec = web.application(urls, globals()) | |
131 | +# apprec.add_processor(add_global_hook()) | |
132 | +# apprec.run() | |
306 | 133 | ... | ... |