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" --> | ... | ... |