Commit 100c50829e5d0c6defa61cf0941bd37728f1714d
1 parent
13f6743d
Exists in
master
and in
1 other branch
New script dedicated for survey.
Showing
5 changed files
with
280 additions
and
47 deletions
Show diff stats
src/web/server.py
... | ... | @@ -16,16 +16,16 @@ from user import * |
16 | 16 | |
17 | 17 | import urllib |
18 | 18 | |
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 = []) | |
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 = []) | |
29 | 29 | |
30 | 30 | class Index: |
31 | 31 | def GET(self): |
... | ... | @@ -38,9 +38,9 @@ class About: |
38 | 38 | def GET(self): |
39 | 39 | return render.about() |
40 | 40 | |
41 | -class Support: | |
42 | - def GET(self): | |
43 | - return render.support() | |
41 | +#class Support: | |
42 | +# def GET(self): | |
43 | +# return render.support() | |
44 | 44 | |
45 | 45 | class Thanks: |
46 | 46 | def POST(self): |
... | ... | @@ -220,14 +220,15 @@ class Request: |
220 | 220 | print details |
221 | 221 | return details |
222 | 222 | |
223 | -class RandomRequest(Request): | |
224 | - def __init__(self): | |
225 | - pass | |
226 | - #self.storage = web.Storage() | |
223 | +#class RandomRequest(Request): | |
224 | +# def __init__(self): | |
225 | +# pass | |
226 | +# #self.storage = web.Storage() | |
227 | 227 | |
228 | 228 | class AppRecommender: |
229 | 229 | def __init__(self): |
230 | - self.rec = Recommender(Config()) | |
230 | + self.cfg = Config() | |
231 | + self.rec = Recommender(self.cfg) | |
231 | 232 | |
232 | 233 | def POST(self): |
233 | 234 | request = Request(web.input(pkgs_file={})) |
... | ... | @@ -248,8 +249,7 @@ class AppRecommender: |
248 | 249 | pkg_summaries[pkg] = "" |
249 | 250 | if Config().survey_mode: |
250 | 251 | return render.survey(recommendation, pkg_details, |
251 | - FeedbackForm(request.selected_strategies), | |
252 | - request) | |
252 | + FeedbackForm(request.selected_strategies)) | |
253 | 253 | else: |
254 | 254 | return render.apprec(recommendation, pkg_summaries, |
255 | 255 | FeedbackForm(request.selected_strategies), | ... | ... |
... | ... | @@ -0,0 +1,222 @@ |
1 | +#!/usr/bin/env python | |
2 | + | |
3 | +import os | |
4 | +import web | |
5 | +from web import form | |
6 | +import tempfile | |
7 | +import sys | |
8 | +import simplejson as json | |
9 | +import apt | |
10 | +import re | |
11 | + | |
12 | +sys.path.insert(0,"../") | |
13 | + | |
14 | +from config import * | |
15 | +from recommender import * | |
16 | +from user import * | |
17 | + | |
18 | +import urllib | |
19 | + | |
20 | +class Index: | |
21 | + def GET(self): | |
22 | + return render.survey_index() | |
23 | + | |
24 | +class About: | |
25 | + def GET(self): | |
26 | + return render.about() | |
27 | + | |
28 | +class Thanks: | |
29 | + def POST(self): | |
30 | + return render.thanks() | |
31 | + | |
32 | +class Package: | |
33 | + def GET(self, pkg): | |
34 | + result = self.get_details_from_dde(pkg) | |
35 | + return render_plain.package(result) | |
36 | + | |
37 | + def get_details_from_dde(self, pkg): | |
38 | + json_source = Config().dde_url % pkg | |
39 | + json_data = json.load(urllib.urlopen(json_source)) | |
40 | + # parse tags | |
41 | + tags = self._debtags_list_to_dict(json_data['r']['tag']) | |
42 | + json_data['r']['tag'] = tags | |
43 | + # format long description | |
44 | + json_data['r']['long_description'] = json_data['r']['long_description'].replace(' .\n','').replace('\n','<br />') | |
45 | + return json_data['r'] | |
46 | + | |
47 | + def _debtags_list_to_dict(self, debtags_list): | |
48 | + """ in: | |
49 | + ['use::editing', | |
50 | + 'works-with-format::gif', | |
51 | + 'works-with-format::jpg', | |
52 | + 'works-with-format::pdf'] | |
53 | + out: | |
54 | + {'use': [editing], | |
55 | + 'works-with-format': ['gif', 'jpg', 'pdf']' | |
56 | + } | |
57 | + """ | |
58 | + debtags = {} | |
59 | + subtags = [] | |
60 | + for tag in debtags_list: | |
61 | + match = re.search(r'^(.*)::(.*)$', tag) | |
62 | + if not match: | |
63 | + log.error("Could not parse debtags format from tag: %s", tag) | |
64 | + facet, subtag = match.groups() | |
65 | + subtags.append(subtag) | |
66 | + if facet not in debtags: | |
67 | + debtags[facet] = subtags | |
68 | + else: | |
69 | + debtags[facet].append(subtag) | |
70 | + subtags = [] | |
71 | + return debtags | |
72 | + | |
73 | +class Request: | |
74 | + def __init__(self,web_input,submissions_dir,user_id=0,pkgs_list=0): | |
75 | + self.strategy = "" | |
76 | + if user_id: | |
77 | + self.user_id = user_id | |
78 | + self.outputdir = os.path.join(submissions_dir,user_id) | |
79 | + else: | |
80 | + self.outputdir = tempfile.mkdtemp(prefix='',dir=submissions_dir) | |
81 | + print "created dir %s",self.outputdir | |
82 | + self.user_id = self.outputdir.lstrip(submissions_dir) | |
83 | + | |
84 | + if pkgs_list: | |
85 | + self.pkgs_list = pkgs_list | |
86 | + else: | |
87 | + self.pkgs_list = [] | |
88 | + if web_input['pkgs_file'].value: | |
89 | + f = open(self.outputdir + "/packages_list", "wb") | |
90 | + lines = web_input['pkgs_file'].file.readlines() | |
91 | + # popcon submission format | |
92 | + if lines[0].startswith('POPULARITY-CONTEST'): | |
93 | + del lines[0] | |
94 | + del lines[-1] | |
95 | + package_name_field = 2 | |
96 | + else: | |
97 | + package_name_field = 0 | |
98 | + for line in lines: | |
99 | + self.pkgs_list.append(line.split()[package_name_field]) | |
100 | + f.write(line) | |
101 | + f.close() | |
102 | + | |
103 | + def __str__(self): | |
104 | + return "Request %s:\n %s" % (self.user_id,str(self.pkgs_list)) | |
105 | + | |
106 | + def validates(self): | |
107 | + self.errors = [] | |
108 | + if not self.pkgs_list: | |
109 | + self.errors.append("No packages list provided.") | |
110 | + if self.errors: | |
111 | + return False | |
112 | + return True | |
113 | + | |
114 | +class Save: | |
115 | + def POST(self): | |
116 | + web_input = web.input() | |
117 | + print web_input | |
118 | + user_id = web_input['user_id'].encode('utf8') | |
119 | + with open("./submissions/%s/packages_list" % user_id) as packages_list: | |
120 | + pkgs_list = [line.strip() for line in packages_list.readlines()] | |
121 | + strategy = web_input['strategy'] | |
122 | + print user_id,strategy,pkgs_list | |
123 | + output_dir = "./submissions/%s/%s/" % (user_id,strategy) | |
124 | + if not os.path.exists(output_dir): | |
125 | + os.makedirs(output_dir) | |
126 | + evaluations = {} | |
127 | + evaluations["poor"] = [] | |
128 | + evaluations["good"] = [] | |
129 | + evaluations["surprising"] = [] | |
130 | + for key, value in web_input.items(): | |
131 | + if key.startswith("evaluation-"): | |
132 | + evaluations[value.encode('utf8')].append(key.lstrip("evaluation-")) | |
133 | + for key,value in evaluations.items(): | |
134 | + with open(output_dir+key,'w') as output: | |
135 | + for item in value: | |
136 | + output.write(item+"\n") | |
137 | + with open(output_dir+"report",'w') as report: | |
138 | + report.write("# User: %s\n# Strategy: %s\n# TP FP\n%d %d\n" % | |
139 | + (user_id,strategy, | |
140 | + len(evaluations["good"])+len(evaluations["surprising"]), | |
141 | + len(evaluations["poor"]))) | |
142 | + if web_input.has_key('strategy_button'): | |
143 | + return Survey().POST() | |
144 | + elif web_input.has_key('finish_button'): | |
145 | + return render.thanks() | |
146 | + else: | |
147 | + return render.survey_index() | |
148 | + | |
149 | +class Survey: | |
150 | + def __init__(self): | |
151 | + self.strategies = ["cb","cbd","cbt"] | |
152 | + self.rec = Recommender(Config()) | |
153 | + self.submissions_dir = "./submissions/" | |
154 | + if not os.path.exists(self.submissions_dir): | |
155 | + os.makedirs(self.submissions_dir) | |
156 | + | |
157 | + def POST(self): | |
158 | + web_input = web.input(pkgs_file={}) | |
159 | + print "WEB_INPUT",web_input | |
160 | + # If it is not the first strategy round, save the previous evaluation | |
161 | + if not web_input.has_key('user_id'): | |
162 | + request = Request(web_input,self.submissions_dir) | |
163 | + else: | |
164 | + user_id = web_input['user_id'].encode('utf8') | |
165 | + with open("./submissions/%s/packages_list" % user_id) as packages_list: | |
166 | + pkgs_list = [line.strip() for line in packages_list.readlines()] | |
167 | + request = Request(web_input,self.submissions_dir,user_id,pkgs_list) | |
168 | + if not request.validates(): | |
169 | + return render.error(request.errors) | |
170 | + else: | |
171 | + user = User(dict.fromkeys(request.pkgs_list,1),request.user_id) | |
172 | + user.maximal_pkg_profile() | |
173 | + results = dict() | |
174 | + old_strategies = [dirs for root, dirs, files in | |
175 | + os.walk(os.path.join(self.submissions_dir, | |
176 | + request.user_id))] | |
177 | + print "OLD Strategies", old_strategies[0] | |
178 | + strategies = [s for s in self.strategies if s not in old_strategies[0]] | |
179 | + print "LEFT",strategies | |
180 | + request.strategy = random.choice(strategies) | |
181 | + print "selected",request.strategy | |
182 | + self.rec.set_strategy(request.strategy) | |
183 | + prediction = self.rec.get_recommendation(user,10).get_prediction() | |
184 | + print prediction | |
185 | + recommendation = [result[0] for result in prediction] | |
186 | + pkg_summaries = {} | |
187 | + pkg_details = [] | |
188 | + cache = apt.Cache() | |
189 | + for pkg in recommendation: | |
190 | + try: | |
191 | + pkg_details.append(Package().get_details_from_dde(pkg)) | |
192 | + pkg_summaries[pkg] = cache[pkg].candidate.summary | |
193 | + except: | |
194 | + pkg_summaries[pkg] = "" | |
195 | + return render.survey(pkg_details, request) | |
196 | + | |
197 | +def add_global_hook(): | |
198 | + g = web.storage({"counter": "1"}) | |
199 | + def _wrapper(handler): | |
200 | + web.ctx.globals = g | |
201 | + return handler() | |
202 | + return _wrapper | |
203 | + | |
204 | +render = web.template.render('templates/', base='layout') | |
205 | +render_plain = web.template.render('templates/') | |
206 | + | |
207 | +urls = ('/', 'Index', | |
208 | + '/survey', 'Survey', | |
209 | + '/apprec', 'Survey', | |
210 | + '/thanks', 'Thanks', | |
211 | + '/save', 'Save', | |
212 | + '/about', 'About', | |
213 | + '/package/(.*)', 'Package' | |
214 | + ) | |
215 | + | |
216 | +web.webapi.internalerror = web.debugerror | |
217 | + | |
218 | +if __name__ == "__main__": | |
219 | + apprec = web.application(urls, globals()) | |
220 | + apprec.add_processor(add_global_hook()) | |
221 | + apprec.run() | |
222 | + | ... | ... |
src/web/templates/layout.html
... | ... | @@ -12,14 +12,14 @@ $ url_base = "http://localhost:8080" |
12 | 12 | |
13 | 13 | <link href="static/css/style.css" rel="stylesheet" type="text/css" media="screen" charset="utf-8"> |
14 | 14 | |
15 | - $if content.cssfiles: | |
15 | + $if content.has_key('cssfiles'): | |
16 | 16 | $for css in content.cssfiles.split(): |
17 | 17 | <link href="$css" rel="stylesheet" type="text/css" media="screen" charset="utf-8"/> |
18 | 18 | |
19 | 19 | <script src="/static/js/jquery.js" type="text/javascript"></script> |
20 | 20 | <script src="/static/js/FormManager.js" type="text/javascript"></script> |
21 | 21 | |
22 | - $if content.jsfiles: | |
22 | + $if content.has_key('jsfiles'): | |
23 | 23 | $for js in content.jsfiles.split(): |
24 | 24 | <script src="$js" type="text/javascript"></script> |
25 | 25 | |
... | ... | @@ -85,10 +85,11 @@ function validateForm() |
85 | 85 | </p> |
86 | 86 | <div id="tip-upload" class="tip important"> |
87 | 87 | <p> |
88 | - You can use file <strong>/var/log/popularity-contest</strong> or create a with <strong>dpkg</strong>, use: | |
88 | + Upload a popularity-contest submission file ('/var/log/popularity-contest') | |
89 | + or run the following command and upload generated 'packages.list' file. | |
89 | 90 | </p> |
90 | 91 | <p> |
91 | - <code> # dpkg -l > blih.list </code> | |
92 | + <code> # dpkg-query --show > packages.list </code> | |
92 | 93 | </p> |
93 | 94 | </div><!-- id="tip-upload" --> |
94 | 95 | </fieldset> | ... | ... |
src/web/templates/survey.html
1 | -$def with (recommends, pkg_details, form, request) | |
1 | +$def with (pkg_details, request) | |
2 | 2 | $var title: Survey |
3 | 3 | $var mod = 'survey'; |
4 | 4 | $var cssfiles: static/coda-slider-2.0/stylesheets/coda-slider-2.0.css static/css/facebox.css static/css/survey.css |
... | ... | @@ -24,18 +24,23 @@ $var jsfiles: static/coda-slider-2.0/javascripts/jquery-1.3.2.min.js static/coda |
24 | 24 | |
25 | 25 | <h1>AppRecommender Survey</h1> |
26 | 26 | |
27 | -<form action="/thanks" method="post" enctype="multipart/form-data" name="surveyform"> | |
27 | +<form action="/save" method="post" enctype="multipart/form-data" name="surveyform"> | |
28 | 28 | |
29 | +<input type="hidden" name="user_id" value=$request.user_id> | |
30 | +<input type="hidden" name="strategy" value=$request.strategy> | |
29 | 31 | <div id="controls-form" style="display: none;"><!-- display show in the end form --> |
30 | -<a id="finish-button" class="glass">Finish | |
31 | - <div class="tip important" id="tip-bar"><p>Tem certeza que não gostaria de avaliar uma nova estratégia? </p></div> | |
32 | -</a> | |
33 | 32 | |
34 | -<a id="bar-button" class="glass">New Recommends | |
35 | - <div class="tip note" id="tip-finish"><p>Fazer uma nova recomendação com uma nova lista de pacotes.</p></div> | |
33 | +<!--<a id="finish-button" class="glass">Finish | |
34 | + <div class="tip important" id="tip-bar"><p>Conclude your participation in this survey</p></div> | |
36 | 35 | </a> |
36 | +<a id="bar-button" class="glass" href="http://localhost:8080">Restart | |
37 | + <div class="tip note" id="tip-finish"><p>Restart the survey with a new packages list.</p></div> | |
38 | +</a>--> | |
39 | + | |
40 | +<input id="restart-button" name="restart_button" type="submit" value="Restart" class="glass" /> | |
41 | +<input id="finish-button" name="finish_button" type="submit" value="Finish" class="glass" /> | |
37 | 42 | |
38 | -<label for="strategy_button" id="tip-strategy"><div class="tip tip"><p> Gerar um novo resultado com uma estratégia diferente para esta mesma lista de pacotes. </p></div><input id="strategy-button" name="strategy_button" type="submit" value="New Strategy" class="glass" /></label> | |
43 | +<label for="strategy_button" id="tip-strategy"><div class="tip tip"><p> New round of evaluations for the same list of packages and suggestions produced by a different recommendation strategy. </p></div><input id="strategy-button" name="strategy_button" type="submit" value="New Round" class="glass" /></label> | |
39 | 44 | </div><!-- id="controls-form" --> |
40 | 45 | |
41 | 46 | <div class="coda-slider-wrapper"> |
... | ... | @@ -44,12 +49,13 @@ $for pkg in pkg_details: |
44 | 49 | <div class="panel"> |
45 | 50 | <div class="panel-wrapper"> |
46 | 51 | <div id="panel-controls"> |
47 | - <label for="bad-$pkg['package']">Bad</label> | |
48 | - <input class="radio" type="radio" name="radio" value="poor-$pkg['package']" /><br /> | |
49 | - <label for="good-$pkg['package']">Good</label> | |
50 | - <input class="radio" type="radio" name="radio" value="good-$pkg['package']" /><br /> | |
51 | - <label for="surprise-$pkg['package']">Surprise</label> | |
52 | - <input class="radio" type="radio" name="radio" value="surprise-$pkg['package']" /> | |
52 | + <label for="poor">Poor</label> | |
53 | + <input class="radio" type="radio" name="evaluation-$pkg_details.index(pkg)" value="poor" /><br /> | |
54 | + <label for="good">Good</label> | |
55 | + <input class="radio" type="radio" name="evaluation-$pkg_details.index(pkg)" value="good" /><br /> | |
56 | + <label for="surprising">Surprising</label> | |
57 | + <input class="radio" type="radio" | |
58 | + name="evaluation-$pkg_details.index(pkg)" value="surprising" /> | |
53 | 59 | </div><!-- #panel-controls --> |
54 | 60 | <h1 id="title_pkg">$pkg['package'] <br /> |
55 | 61 | <span>$pkg['description']</span></h1> |
... | ... | @@ -72,6 +78,17 @@ $for pkg in pkg_details: |
72 | 78 | <li><b>Origin</b>: $pkg['origin']</li> |
73 | 79 | <li><b>Maintainer</b>: $pkg['maintainer']</li> |
74 | 80 | </ul> |
81 | + <div id="debresources_box" class="graybox vert-grad"> | |
82 | + <h2>Debian resources:</h2> | |
83 | + <ul> | |
84 | + <li><a href="http://packages.debian.org/$pkg['package']">Package page on debian.org</a></li> | |
85 | + <li><a href="http://bugs.debian.org/$pkg['package']">Debian bug tracking system (BTS)</a></li> | |
86 | + <li><a href="http://packages.qa.debian.org/$pkg['package']">Debian package tracking system (PTS)</a></li> | |
87 | + <li><a href="http://qa.debian.org/popcon.php?package=$pkg['package']">Popularity contest statistics (Popcon)</a></li> | |
88 | + <li><a href="http://patch-tracker.debian.org/package/$pkg['package']">Debian patch tracking system</a></li> | |
89 | + <li><a href="http://screenshots.debian.net/package/$pkg['package']">Screenshots</a></li> | |
90 | + </ul> | |
91 | + </div><!-- id="debresources_box --> | |
75 | 92 | </div><!-- id="content-pkg" --> |
76 | 93 | </div><!-- .panel-wrapper --> |
77 | 94 | </div><!-- .panel --> |
... | ... | @@ -80,6 +97,5 @@ $for pkg in pkg_details: |
80 | 97 | |
81 | 98 | </form> |
82 | 99 | |
83 | - | |
84 | 100 | </div><!-- class="innertube" --> |
85 | 101 | </div><!-- id="maincontent" --> | ... | ... |
src/web/templates/survey_index.html
... | ... | @@ -3,7 +3,6 @@ $var mod = 'index'; |
3 | 3 | $var cssfiles: static/css/survey.css |
4 | 4 | $var jsfiles: |
5 | 5 | |
6 | - | |
7 | 6 | <!-- Dynamic form --> |
8 | 7 | <script type="application/x-javascript"> |
9 | 8 | window.onload = function() { |
... | ... | @@ -15,7 +14,8 @@ window.onload = function() { |
15 | 14 | <div class="innertube"> |
16 | 15 | |
17 | 16 | <div class="textbox"> |
18 | -<h1>Help us learn your needs</h1> | |
17 | +<h1>AppRecommender Survey</h1> | |
18 | +<h2>Help us learn your needs</h2> | |
19 | 19 | |
20 | 20 | <p>Participate in this survey and contribute for the development of |
21 | 21 | AppRecommender, a recommender system for GNU/Linux applications.</p> |
... | ... | @@ -26,12 +26,6 @@ You need to analyse at least 10 sugestions to be considered in the survey, |
26 | 26 | though we appreciate if you do as many as you can.</p> |
27 | 27 | <br /> |
28 | 28 | <p>Your help is very much appreciated!</p> |
29 | -<!-- | |
30 | -(Caixinha com intruções) | |
31 | -<p> Instructions: if you have popularity-contest package installed, upload your | |
32 | -popcon submission file, usually located at '/var/log/popularity-contest'. | |
33 | -Otherwise run the following command and upload the 'packages_list' file: ``dpkg -l > packages_list''.</p> | |
34 | ---> | |
35 | 29 | </div> |
36 | 30 | |
37 | 31 | </div><!-- class="innertube" --> | ... | ... |