Commit 896fcc0700b027acebed531bcf18efa7ec2a1c02
1 parent
a7a750fa
Exists in
master
and in
1 other branch
Integrated recommender and new web form; changed section about; changes frontpage layout;
Showing
6 changed files
with
184 additions
and
124 deletions
Show diff stats
src/web/server.py
... | ... | @@ -6,6 +6,7 @@ import tempfile |
6 | 6 | import sys |
7 | 7 | import simplejson as json |
8 | 8 | import apt |
9 | +import re | |
9 | 10 | |
10 | 11 | sys.path.insert(0,"../") |
11 | 12 | |
... | ... | @@ -15,44 +16,24 @@ from user import * |
15 | 16 | |
16 | 17 | import urllib |
17 | 18 | |
18 | -class RequestForm(form.Form): | |
19 | - def __init__(self): | |
20 | - form.Form.__init__(self, \ | |
21 | - form.File("pkgs_file", size=35, description="Upload file"), | |
22 | - form.Textarea("pkgs_list", description="Packages", | |
23 | - rows="4", cols="52"), | |
24 | - form.Dropdown('limit', [(5, '05'), (10, '10'), (20, '20')], | |
25 | - description = "Limit"), | |
26 | - form.Checkbox("strategy_cb", value=1, checked=False, | |
27 | - description="Content-based"), | |
28 | - form.Checkbox("strategy_col", value=1, checked=False, | |
29 | - description="Collaborative"), | |
30 | - form.Checkbox("strategy_hybrid", value="True", checked=False, | |
31 | - description="Hybrid"), | |
32 | - form.Checkbox("strategy_hybrid_plus", value="True", checked=False, | |
33 | - description="Hybrid plus"), | |
34 | - validators = [form.Validator("No packages list provided.", | |
35 | - lambda f: f.has_key("pkgs_list") | | |
36 | - f.has_key("pkgs_file") ), | |
37 | - form.Validator("No strategy selected.", | |
38 | - lambda f: f.has_key("strategy_cb") | | |
39 | - f.has_key("startegy_col") | | |
40 | - f.has_key("strategy_hybrid") | | |
41 | - f.has_key("strategy_hybrid_plus")) ]) | |
42 | - | |
43 | 19 | class FeedbackForm(form.Form): |
44 | - def __init__(self,strategies): | |
45 | - desc_dict = {"cta": "Content-based", "col": "Collaborative", | |
46 | - "hybrid": "Hybrib", "hybrid_plus": "Hybrid Plus"} | |
20 | + def __init__(self,selected_strategies): | |
21 | + desc_dict = {"cb": "Content-based", "cbt": "Content-based", | |
22 | + "cbd": "Content-based", "col": "Collaborative", | |
23 | + "hybrid": "Hybrib"} | |
47 | 24 | fields = [] |
48 | - for strategy in strategies: | |
25 | + for strategy in selected_strategies: | |
49 | 26 | fields.append(form.Radio(desc_dict[strategy], |
50 | 27 | [('1','1 '),('2','2 '),('3','3'),('4','4 '),('5','5')])) |
51 | 28 | form.Form.__init__(self, *fields, validators = []) |
52 | 29 | |
53 | 30 | class Index: |
54 | 31 | def GET(self): |
55 | - return render.index("/apprec", "post", RequestForm()) | |
32 | + return render.index() | |
33 | + | |
34 | +class About: | |
35 | + def GET(self): | |
36 | + return render.about() | |
56 | 37 | |
57 | 38 | class Thanks: |
58 | 39 | def POST(self): |
... | ... | @@ -95,52 +76,108 @@ class Package: |
95 | 76 | subtags = [] |
96 | 77 | return debtags |
97 | 78 | |
79 | +class Request: | |
80 | + def __init__(self,web_input,user_id): | |
81 | + self.user_id = user_id | |
82 | + self.storage = web_input | |
83 | + | |
84 | + self.pkgs_list = [] | |
85 | + if web_input.has_key('pkgs_list'): | |
86 | + self.pkgs_list = web_input['pkgs_list'].encode('utf8').split() | |
87 | + print self.pkgs_list | |
88 | + print web_input['pkgs_file'] | |
89 | + if web_input['pkgs_file']: | |
90 | + f = open(outputdir + "/packages_list", "wb") | |
91 | + lines = web_input['pkgs_file'].file.readlines() | |
92 | + print lines | |
93 | + if lines[0].startswith('POPULARITY-CONTEST'): | |
94 | + del lines[0] | |
95 | + del lines[-1] | |
96 | + package_name_field = 2 | |
97 | + else: | |
98 | + package_name_field = 0 | |
99 | + for line in lines: | |
100 | + self.pkgs_list.append(line.split()[package_name_field]) | |
101 | + f.write(line) | |
102 | + f.close() | |
103 | + | |
104 | + if web_input.has_key('limit'): | |
105 | + self.limit = int(web_input['limit']) | |
106 | + if web_input.has_key('profile_size'): | |
107 | + self.profile_size = int(web_input['profile_size']) | |
108 | + if web_input.has_key('weight'): | |
109 | + self.weight = web_input['weight'].encode('utf8') | |
110 | + | |
111 | + self.selected_strategies = [] | |
112 | + if web_input.has_key('strategy'): | |
113 | + if web_input['strategy'].encode('utf8') == "content": | |
114 | + if (web_input.has_key('tag') and web_input.has_key('desc')): | |
115 | + self.selected_strategies.append("cb") | |
116 | + elif web_input.has_key('desc'): | |
117 | + self.selected_strategies.append("cbd") | |
118 | + else: | |
119 | + self.selected_strategies.append("cbt") | |
120 | + if web_input['strategy'].encode('utf8') == "collab": | |
121 | + self.selected_strategies.append("col") | |
122 | + if web_input['strategy'].encode('utf8') == "hybrid": | |
123 | + self.selected_strategies.append("hybrid") | |
124 | + | |
125 | + if web_input.has_key('cluster'): | |
126 | + self.cluster = bool(web_input.has_key['cluster'].encode('utf8')) | |
127 | + | |
128 | + if web_input.has_key('neighbours'): | |
129 | + self.neighbours = int(web_input['neighbours']) | |
130 | + | |
131 | + self.profiles_set = set() | |
132 | + if web_input.has_key('profile_desktop'): | |
133 | + self.profiles = self.profiles.add("desktop") | |
134 | + if web_input.has_key('profile_admin'): | |
135 | + self.profiles = self.profiles.add("admin") | |
136 | + if web_input.has_key('profile_devel'): | |
137 | + self.profiles = self.profiles.add("devel") | |
138 | + if web_input.has_key('profile_science'): | |
139 | + self.profiles = self.profiles.add("science") | |
140 | + if web_input.has_key('profile_arts'): | |
141 | + self.profiles = self.profiles.add("arts") | |
142 | + | |
143 | + def __str__(self): | |
144 | + return self.storage | |
145 | + | |
146 | + def validates(self): | |
147 | + self.errors = [] | |
148 | + if (not self.pkgs_list or not self.selected_strategies): | |
149 | + self.errors.append("No upload file or packages list was provided.") | |
150 | + return False | |
151 | + if not self.selected_strategies: | |
152 | + self.errors.append("No strategy was selected.") | |
153 | + return False | |
154 | + return True | |
155 | + | |
156 | + def get_strategy_details(self): | |
157 | + return self.selected_strategies[0] | |
98 | 158 | |
99 | 159 | class AppRecommender: |
100 | 160 | def POST(self): |
161 | + print "post",web.input() | |
101 | 162 | outputdir = tempfile.mkdtemp(prefix='',dir='./submissions/') |
102 | 163 | user_id = outputdir.lstrip('./submissions/') |
103 | - request = RequestForm() | |
104 | - request_info = web.input(pkgs_file={}) | |
105 | - if not request.validates(request_info): | |
106 | - return render.error(request) | |
164 | + print web.input(pkgs_file={}) | |
165 | + request = Request(web.input(pkgs_file={}),user_id) | |
166 | + if not request.validates(): | |
167 | + return render.error(request.errors) | |
107 | 168 | else: |
108 | - user_pkgs_list = [] | |
109 | - if request_info.has_key('pkgs_list'): | |
110 | - user_pkgs_list = request_info['pkgs_list'].encode('utf8').split() | |
111 | - print user_pkgs_list | |
112 | - | |
113 | - if request_info['pkgs_file'].value: | |
114 | - f = open(outputdir + "/packages_list", "wb") | |
115 | - lines = request_info['pkgs_file'].file.readlines() | |
116 | - print lines | |
117 | - if lines[0].startswith('POPULARITY-CONTEST'): | |
118 | - del lines[0] | |
119 | - del lines[-1] | |
120 | - package_name_field = 2 | |
121 | - else: | |
122 | - package_name_field = 0 | |
123 | - for line in lines: | |
124 | - user_pkgs_list.append(line.split()[package_name_field]) | |
125 | - f.write(line) | |
126 | - f.close() | |
127 | - | |
128 | - strategies = [] | |
129 | - if request_info.has_key('strategy_cb'): strategies.append("cta") | |
130 | - ### Colaborative strategies can not go online yet | |
131 | - if request_info.has_key('strategy_col'): strategies.append("col") | |
132 | - if request_info.has_key('strategy_hybrid'): | |
133 | - strategies.append("hybrid") | |
134 | - if request_info.has_key('strategy_hybrid_plus'): | |
135 | - strategies.append("hybrid_plus") | |
136 | - recommends = self._recommends(user_id,user_pkgs_list,strategies, int(request_info['limit'])) | |
169 | + recommendation = self._recommends(request) | |
137 | 170 | ### Getting package summary (short description) ### |
138 | 171 | pkg_summaries = {} |
139 | 172 | cache = apt.Cache() |
140 | - for strategy, result in recommends.items(): | |
173 | + for strategy, result in recommendation.items(): | |
141 | 174 | for pkg in result: |
142 | - pkg_summaries[pkg] = cache[pkg].candidate.summary | |
143 | - return render.apprec(recommends, pkg_summaries, FeedbackForm(strategies)) | |
175 | + try: | |
176 | + pkg_summaries[pkg] = cache[pkg].candidate.summary | |
177 | + except: | |
178 | + pkg_summaries[pkg] = "" | |
179 | + return render.apprec(recommendation, pkg_summaries, | |
180 | + FeedbackForm(request.selected_strategies),request) | |
144 | 181 | |
145 | 182 | # parsing json from screenshots - can be usefull in the future... |
146 | 183 | # def _packages_attrs(self, recommends): #recommends is result of _recommends() |
... | ... | @@ -156,18 +193,16 @@ class AppRecommender: |
156 | 193 | # recommended_pkgs_attrs[pkg_attrs_dict['name']] = pkg_attrs_dict |
157 | 194 | # return recommended_pkgs_attrs |
158 | 195 | |
159 | - def _recommends(self,user_id,user_pkgs_list,strategies,limit): | |
160 | - user = User(dict.fromkeys(user_pkgs_list,1),user_id) | |
196 | + def _recommends(self,request): | |
197 | + user = User(dict.fromkeys(request.pkgs_list,1),request.user_id) | |
161 | 198 | user.maximal_pkg_profile() |
162 | 199 | cfg = Config() |
163 | 200 | rec = Recommender(cfg) |
164 | 201 | results = dict() |
165 | - for strategy in strategies: | |
166 | - ### Colaborative strategies can not go online yet | |
167 | - #eval("rec."+strategy+"(cfg)") | |
168 | - rec.cta(cfg) | |
169 | - prediction = rec.get_recommendation(user).get_prediction(limit) | |
170 | - results[rec.strategy.description] = \ | |
202 | + for strategy_str in request.selected_strategies: | |
203 | + rec.set_strategy(strategy_str) | |
204 | + prediction = rec.get_recommendation(user).get_prediction(request.limit) | |
205 | + results[strategy_str] = \ | |
171 | 206 | [result[0] for result in prediction] |
172 | 207 | return results |
173 | 208 | |
... | ... | @@ -181,9 +216,10 @@ def add_global_hook(): |
181 | 216 | render = web.template.render('templates/', base='layout') |
182 | 217 | render_plain = web.template.render('templates/') |
183 | 218 | |
184 | -urls = ('/', 'Index', | |
185 | - '/apprec', 'AppRecommender', | |
219 | +urls = ('/', 'Index', | |
220 | + '/apprec', 'AppRecommender', | |
186 | 221 | '/thanks', 'Thanks', |
222 | + '/about', 'About', | |
187 | 223 | '/package/(.*)', 'Package' |
188 | 224 | ) |
189 | 225 | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +$var title: About | |
2 | +$var mod = 'about'; | |
3 | + | |
4 | +<div class="graybox"> | |
5 | + <h1>What is this?</h1> | |
6 | +<p>AppRecommender is a project in development that aims to provide solutions | |
7 | +for application recommendation at the GNU/Linux world. It was initially thought | |
8 | +as a Debian package recommender, but considering the multi-distro effort in | |
9 | +providing platform independent solutions, it should also follow this | |
10 | +principle.</p> | |
11 | +</div> | |
12 | + | |
13 | +<div class="align-right"><img alt="AppRecommender logo" src="/static/images/AppRecommender-logo.jpg" width="320" /></div> | |
14 | + | ... | ... |
src/web/templates/apprec.html
1 | -$def with (recommends, pkg_summaries, form) | |
1 | +$def with (recommends, pkg_summaries, form, request) | |
2 | 2 | $var title: Feedback |
3 | 3 | $var mod = 'feedback'; |
4 | 4 | |
5 | - | |
6 | 5 | <script type="application/x-javascript"> |
7 | 6 | $$(document).ready(function() { |
8 | 7 | inithandlers(); |
... | ... | @@ -12,18 +11,19 @@ $$(document).ready(function() { |
12 | 11 | </script> |
13 | 12 | |
14 | 13 | <div class="graybox"> |
15 | - <h1>Results per strategy</h1> | |
16 | -<ul class="toc"> | |
14 | + <h1>Details of recommendation strategy</h1> | |
15 | + $request.get_strategy_details() | |
16 | +<!--<ul class="toc"> | |
17 | 17 | $for strategy, result in recommends.items(): |
18 | 18 | <li><a href="#$strategy">$strategy</a></li> |
19 | 19 | </ul> |
20 | -</div> | |
20 | +</div>--> | |
21 | 21 | |
22 | -<form action="/thanks" method="post"> | |
22 | +<form action="/thanks" method="post" enctype="multipart/form-data"> | |
23 | 23 | <table> |
24 | 24 | <tbody> |
25 | 25 | $for strategy, result in recommends.items(): |
26 | - <h2><a name="$strategy" id="$strategy">$strategy</a></h2> | |
26 | + <!--<h2><a name="$strategy" id="$strategy">$strategy</a></h2>--> | |
27 | 27 | <tr> |
28 | 28 | $ count = 0 |
29 | 29 | $for pkg in result: | ... | ... |
src/web/templates/error.html
1 | -$def with (form) | |
1 | +$def with (error_msgs) | |
2 | 2 | $var title: Error |
3 | 3 | $var mod = 'error'; |
4 | 4 | |
... | ... | @@ -11,10 +11,10 @@ $var mod = 'error'; |
11 | 11 | |
12 | 12 | Your request could not be proccessed due to the following error(s): |
13 | 13 | |
14 | -<ul> | |
15 | -$for v in form.validators: | |
16 | - <b><li>$v.msg</li></b> | |
17 | -</ul> | |
14 | +<p><ul> | |
15 | +$for e in error_msgs: | |
16 | + <b><li>$e</li></b> | |
17 | +</ul></p> | |
18 | 18 | |
19 | 19 | <p><a href="/">Go back</a> and try again. </p> |
20 | 20 | <p>If you believe it is a bug, please report to <a | ... | ... |
src/web/templates/index.html
1 | -$def with (action, method, form) | |
2 | 1 | $var title: Home |
3 | 2 | $var mod = 'index'; |
4 | 3 | |
5 | 4 | <div class="graybox"> |
6 | - <h1>What is this?</h1> | |
7 | -<p>AppRecommender is a project in development that aims to provide solutions | |
8 | -for application recommendation at the GNU/Linux world. It was initially thought | |
9 | -as a Debian package recommender, but considering the multi-distro effort in | |
10 | -providing platform independent solutions, it should also follow this | |
11 | -principle.</p> | |
12 | -</div> | |
5 | +<h1>You might also like these packages...</h1> | |
13 | 6 | |
14 | -<div class="align-right"><img alt="AppRecommender logo" src="/static/images/AppRecommender-logo.jpg" width="320" /></div> | |
7 | +<p>Provide a list of packages or upload a popcon submission file and you'll get | |
8 | +a list of suggested packages automatically computed by AppRecommender. You can | |
9 | +customize the recommender setup or let it randomly choose a setup for you.</p> | |
15 | 10 | |
11 | +<p>Please fill in the form that follows the recommendation results. Your | |
12 | +feedback is very much appreciated!</p> | |
16 | 13 | |
14 | +<p>Enjoy it :)</p> | |
15 | +</div> | |
17 | 16 | |
17 | +<!--<div class="align-right"><img alt="AppRecommender logo" | |
18 | +src="/static/images/AppRecommender-logo.jpg" width="320" /></div>--> | |
18 | 19 | |
19 | -<form action="" name="weboptions"> | |
20 | +<form action="apprec" enctype="multipart/form-data" method="post" name="weboptions"> | |
20 | 21 | <p> |
22 | + <label>Upload file:<input type="file" id="pkgs_file" name="pkgs_file" size="35"/></label><br /> | |
23 | + <label>Packages list:<textarea rows="2" cols="40" name="pkgs_list" id="pkgs_list"></textarea><label><br /> | |
21 | 24 | <label>Profile size: <input type="text" name="profile_size" value="10"></label><br /> |
22 | - <label>Recommendations: <input type="text" name="recommendation_size" value="10"></label><br /> | |
23 | - <label>Weighting scheme:<br /><input type="radio" name="scheme" value="BM25">BM25</label><br /> | |
24 | - <label><input type="radio" name="scheme" value="trad"></label>Traditional<br /> | |
25 | - <label>Strategy: <br /><input type="radio" name="strategy" value="content"> Content-based</label><br /> | |
26 | - <label><input type="radio" name="strategy" value="collab"> Collaborative</label><br /> | |
27 | - <label><input type="radio" name="strategy" value="hybrid"> Hybrid</label><br /> | |
25 | + <label>Recommendation size: <input type="text" name="limit" value="10"></label><br /> | |
26 | + <label>Weighting scheme:<br /> | |
27 | + <input type="radio" name="weight" value="BM25" checked >BM25<br /> | |
28 | + <input type="radio" name="weight" value="trad">Traditional<br /> | |
29 | + </label> | |
30 | + <label>Strategy: <br /> | |
31 | + <input type="radio" name="strategy" value="content" checked> Content-based<br /> | |
32 | + <input type="radio" name="strategy" value="collab"> Collaborative<br /> | |
33 | + <input type="radio" name="strategy" value="hybrid"> Hybrid<br /> | |
34 | + </label> | |
28 | 35 | <!-- <label>Password:<input type="password" name="pass"></label> --> |
29 | - <label style="margin-bottom: 1em; padding-bottom: 1em; border-bottom: 3px silver groove;"><input type="hidden" class="DEPENDS ON strategy BEING content OR strategy BEING hybrid"></label> | |
30 | - <label><input type="checkbox" name="ssh" class="DEPENDS ON strategy BEING content OR strategy BEING hybrid"> Tag</label> | |
31 | - <label><input type="checkbox" name="iis" class="DEPENDS ON strategy BEING content OR strategy BEING hybrid"> Description</label> | |
32 | - <br /><label><input type="checkbox" name="asp" class="DEPENDS ON strategy BEING collab OR strategy BEING hybrid"> Clustering</label><br /> | |
33 | - <label>Neighbours: <input type="text" name="neighbours" class="DEPENDS ON strategy BEING collab OR strategy BEING hybrid" value="50"></label><br /> | |
34 | - | |
35 | - <label>Personal profile: <br /><input type="checkbox" name="user_desktop" checked class="DEPENDS ON strategy BEING hybrid">Desktop</label> | |
36 | - <label><input type="checkbox" name="neighbours" class="DEPENDS ON strategy BEING hybrid">Admin</label> | |
37 | - <label><input type="checkbox" name="neighbours" class="DEPENDS ON strategy BEING hybrid">Devel</label> | |
38 | - <label><input type="checkbox" name="neighbours" class="DEPENDS ON strategy BEING hybrid">Science</label> | |
39 | - <label><input type="checkbox" name="neighbours" class="DEPENDS ON strategy BEING hybrid">Arts</label> | |
36 | + <!-- <label style="margin-bottom: 1em; padding-bottom: 1em; border-bottom: 3px silver groove;"><input type="hidden" class="DEPENDS ON strategy BEING content OR strategy BEING hybrid"></label> --> | |
37 | + </p><p> | |
38 | + <label>Content attributes: | |
39 | + <input type="radio" name="content" value="tag" class="DEPENDS ON strategy BEING content OR strategy BEING hybrid" checked> tag | |
40 | + <input type="radio" name="content" value="desc" class="DEPENDS ON strategy BEING content OR strategy BEING hybrid"> description | |
41 | + <input type="radio" name="content" value="full" class="DEPENDS ON strategy BEING content OR strategy BEING hybrid"> both | |
42 | + </label><br /> | |
43 | + <label>Clustering | |
44 | + <input type="radio" name="cluster" value="True" class="DEPENDS ON strategy BEING collab OR strategy BEING hybrid" checked> Yes | |
45 | + <input type="radio" name="cluster" value="False" class="DEPENDS ON strategy BEING collab OR strategy BEING hybrid"> No | |
46 | + </label><br /> | |
47 | + <label>Neighbours: | |
48 | + <input type="text" name="neighbours" class="DEPENDS ON strategy BEING collab OR strategy BEING hybrid" value="50"> | |
49 | + </label><br /> | |
50 | + <label>Personal profile: | |
51 | + <input type="checkbox" name="profile_desktop" class="DEPENDS ON strategy BEING hybrid" checked>Desktop | |
52 | + <input type="checkbox" name="profile_admin" class="DEPENDS ON strategy BEING hybrid">Admin | |
53 | + <input type="checkbox" name="profile_devel" class="DEPENDS ON strategy BEING hybrid">Devel | |
54 | + <input type="checkbox" name="profile_science" class="DEPENDS ON strategy BEING hybrid">Science | |
55 | + <input type="checkbox" name="profile_arts" class="DEPENDS ON strategy BEING hybrid">Arts | |
56 | + </label> | |
40 | 57 | </p> |
41 | 58 | <input type="submit" /> |
42 | 59 | </form> |
43 | 60 | |
44 | -<!-- | |
45 | -<form action="$action" enctype="multipart/form-data" method="$method"> | |
46 | -$:form.render() | |
47 | -<br /> | |
48 | -<input type="submit" /> | |
49 | -</form> | |
50 | ---> | ... | ... |
src/web/templates/layout.html
1 | 1 | $def with (content) |
2 | -$ url_base = "http://localhost:8080/" | |
2 | +$ url_base = "http://localhost:8080" | |
3 | 3 | |
4 | 4 | <!DOCTYPE html> |
5 | 5 | <html lang="en"><head> |
... | ... | @@ -133,7 +133,7 @@ function hideMesg(){ |
133 | 133 | <div id="nav"> |
134 | 134 | <a href="$url_base">Home</a> |
135 | 135 | | |
136 | - <a href="http://www.ime.usp.br/~tassia/apprec.html">About</a> | |
136 | + <a href="$url_base/about">About</a> | |
137 | 137 | | |
138 | 138 | <a href="http://github.com/tassia/AppRecommender">Devel</a> |
139 | 139 | </div> | ... | ... |