Commit 896fcc0700b027acebed531bcf18efa7ec2a1c02

Authored by Tássia Camões Araújo
1 parent a7a750fa
Exists in master and in 1 other branch add_vagrant

Integrated recommender and new web form; changed section about; changes frontpage layout;

src/web/server.py
@@ -6,6 +6,7 @@ import tempfile @@ -6,6 +6,7 @@ import tempfile
6 import sys 6 import sys
7 import simplejson as json 7 import simplejson as json
8 import apt 8 import apt
  9 +import re
9 10
10 sys.path.insert(0,"../") 11 sys.path.insert(0,"../")
11 12
@@ -15,44 +16,24 @@ from user import * @@ -15,44 +16,24 @@ from user import *
15 16
16 import urllib 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 class FeedbackForm(form.Form): 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 fields = [] 24 fields = []
48 - for strategy in strategies: 25 + for strategy in selected_strategies:
49 fields.append(form.Radio(desc_dict[strategy], 26 fields.append(form.Radio(desc_dict[strategy],
50 [('1','1 '),('2','2 '),('3','3'),('4','4 '),('5','5')])) 27 [('1','1 '),('2','2 '),('3','3'),('4','4 '),('5','5')]))
51 form.Form.__init__(self, *fields, validators = []) 28 form.Form.__init__(self, *fields, validators = [])
52 29
53 class Index: 30 class Index:
54 def GET(self): 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 class Thanks: 38 class Thanks:
58 def POST(self): 39 def POST(self):
@@ -95,52 +76,108 @@ class Package: @@ -95,52 +76,108 @@ class Package:
95 subtags = [] 76 subtags = []
96 return debtags 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 class AppRecommender: 159 class AppRecommender:
100 def POST(self): 160 def POST(self):
  161 + print "post",web.input()
101 outputdir = tempfile.mkdtemp(prefix='',dir='./submissions/') 162 outputdir = tempfile.mkdtemp(prefix='',dir='./submissions/')
102 user_id = outputdir.lstrip('./submissions/') 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 else: 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 ### Getting package summary (short description) ### 170 ### Getting package summary (short description) ###
138 pkg_summaries = {} 171 pkg_summaries = {}
139 cache = apt.Cache() 172 cache = apt.Cache()
140 - for strategy, result in recommends.items(): 173 + for strategy, result in recommendation.items():
141 for pkg in result: 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 # parsing json from screenshots - can be usefull in the future... 182 # parsing json from screenshots - can be usefull in the future...
146 # def _packages_attrs(self, recommends): #recommends is result of _recommends() 183 # def _packages_attrs(self, recommends): #recommends is result of _recommends()
@@ -156,18 +193,16 @@ class AppRecommender: @@ -156,18 +193,16 @@ class AppRecommender:
156 # recommended_pkgs_attrs[pkg_attrs_dict['name']] = pkg_attrs_dict 193 # recommended_pkgs_attrs[pkg_attrs_dict['name']] = pkg_attrs_dict
157 # return recommended_pkgs_attrs 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 user.maximal_pkg_profile() 198 user.maximal_pkg_profile()
162 cfg = Config() 199 cfg = Config()
163 rec = Recommender(cfg) 200 rec = Recommender(cfg)
164 results = dict() 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 [result[0] for result in prediction] 206 [result[0] for result in prediction]
172 return results 207 return results
173 208
@@ -181,9 +216,10 @@ def add_global_hook(): @@ -181,9 +216,10 @@ def add_global_hook():
181 render = web.template.render('templates/', base='layout') 216 render = web.template.render('templates/', base='layout')
182 render_plain = web.template.render('templates/') 217 render_plain = web.template.render('templates/')
183 218
184 -urls = ('/', 'Index',  
185 - '/apprec', 'AppRecommender', 219 +urls = ('/', 'Index',
  220 + '/apprec', 'AppRecommender',
186 '/thanks', 'Thanks', 221 '/thanks', 'Thanks',
  222 + '/about', 'About',
187 '/package/(.*)', 'Package' 223 '/package/(.*)', 'Package'
188 ) 224 )
189 225
src/web/templates/about.html 0 → 100644
@@ -0,0 +1,14 @@ @@ -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 $var title: Feedback 2 $var title: Feedback
3 $var mod = 'feedback'; 3 $var mod = 'feedback';
4 4
5 -  
6 <script type="application/x-javascript"> 5 <script type="application/x-javascript">
7 $$(document).ready(function() { 6 $$(document).ready(function() {
8 inithandlers(); 7 inithandlers();
@@ -12,18 +11,19 @@ $$(document).ready(function() { @@ -12,18 +11,19 @@ $$(document).ready(function() {
12 </script> 11 </script>
13 12
14 <div class="graybox"> 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 $for strategy, result in recommends.items(): 17 $for strategy, result in recommends.items():
18 <li><a href="#$strategy">$strategy</a></li> 18 <li><a href="#$strategy">$strategy</a></li>
19 </ul> 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 <table> 23 <table>
24 <tbody> 24 <tbody>
25 $for strategy, result in recommends.items(): 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 <tr> 27 <tr>
28 $ count = 0 28 $ count = 0
29 $for pkg in result: 29 $for pkg in result:
src/web/templates/error.html
1 -$def with (form) 1 +$def with (error_msgs)
2 $var title: Error 2 $var title: Error
3 $var mod = 'error'; 3 $var mod = 'error';
4 4
@@ -11,10 +11,10 @@ $var mod = &#39;error&#39;; @@ -11,10 +11,10 @@ $var mod = &#39;error&#39;;
11 11
12 Your request could not be proccessed due to the following error(s): 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 <p><a href="/">Go back</a> and try again. </p> 19 <p><a href="/">Go back</a> and try again. </p>
20 <p>If you believe it is a bug, please report to <a 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 $var title: Home 1 $var title: Home
3 $var mod = 'index'; 2 $var mod = 'index';
4 3
5 <div class="graybox"> 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 <p> 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 <label>Profile size: <input type="text" name="profile_size" value="10"></label><br /> 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 <!-- <label>Password:<input type="password" name="pass"></label> --> 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 </p> 57 </p>
41 <input type="submit" /> 58 <input type="submit" />
42 </form> 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 $def with (content) 1 $def with (content)
2 -$ url_base = "http://localhost:8080/" 2 +$ url_base = "http://localhost:8080"
3 3
4 <!DOCTYPE html> 4 <!DOCTYPE html>
5 <html lang="en"><head> 5 <html lang="en"><head>
@@ -133,7 +133,7 @@ function hideMesg(){ @@ -133,7 +133,7 @@ function hideMesg(){
133 <div id="nav"> 133 <div id="nav">
134 <a href="$url_base">Home</a> 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 <a href="http://github.com/tassia/AppRecommender">Devel</a> 138 <a href="http://github.com/tassia/AppRecommender">Devel</a>
139 </div> 139 </div>