diff options
-rw-r--r-- | polls/models.py | 2 | ||||
-rw-r--r-- | polls/views.py | 312 | ||||
-rw-r--r-- | static/bg.jpg | bin | 4449 -> 3473 bytes | |||
-rw-r--r-- | static/styles.css | 70 | ||||
-rw-r--r-- | templates/createOrEdit.html | 6 | ||||
-rw-r--r-- | templates/vote.html | 8 |
6 files changed, 253 insertions, 145 deletions
diff --git a/polls/models.py b/polls/models.py index 38f1981..08395b8 100644 --- a/polls/models.py +++ b/polls/models.py @@ -46,5 +46,5 @@ class Vote(models.Model): VOTE = ((-1, 'No'), (0, 'Maybe'), (1, 'Yes'),) - vote = models.IntegerField(choices=VOTE) + value = models.IntegerField(choices=VOTE) diff --git a/polls/views.py b/polls/views.py index 040a9b9..8bb8157 100644 --- a/polls/views.py +++ b/polls/views.py @@ -14,11 +14,13 @@ from django.http import HttpResponseRedirect from papillon.polls.models import Poll, PollUser, Choice, Vote def getBaseResponse(request): + "Get the root url in order to redirect to the main page" url = "/".join([request.META['HTTP_HOST'], request.path.split('/')[1], '']) return {'root_url':url} def index(request): + "Main page" response_dct = getBaseResponse(request) error = '' if 'bad_poll' in request.GET: @@ -26,27 +28,35 @@ def index(request): return render_to_response('main.html', response_dct) def createOrEdit(request, admin_url): - + '''Creation or edition of a poll. + admin_url is given to identify a particular poll + admin_url is equal to 0 for a new poll + response_dct is given to the template with some particular keys: + - error is an error message + - new is set to true if it is a new poll + - admin_url is the url of the current page + ''' def genRandomURL(): + "Generation of a random url" chars = string.letters + string.digits url = '' - for i in range(6): + for i in xrange(6): url += random_choice(chars) url += str(int(time.time())) return url - response_dct = getBaseResponse(request) - response_dct['TYPES'] = Poll.TYPE - error = None - poll = None - if 'new' in request.POST: + def submitNewPoll(request, response_dct): + "A new poll is submited" + # verify if all the mandatory_fields are set mandatory_fields = (('author_name', "Author name"), ('poll_name', "Poll name"), ('poll_desc', "Poll description"), ('poll_type', "Poll type"), ) + error = "" for key, label in mandatory_fields: if key not in request.POST or not request.POST[key]: + # only the first error is reported if not error: error = "%s is a mandatory field" % label else: @@ -54,47 +64,47 @@ def createOrEdit(request, admin_url): if error: response_dct['new'] = True response_dct['error'] = error - response_dct['admin_url'] = \ - "/".join(request.path.split('/')[:-2]) + '/0/' - else: - author = PollUser(name=request.POST['author_name']) - author.save() - base_url = 'b' + genRandomURL() - admin_url = 'a' + genRandomURL() - poll = Poll(name=request.POST['poll_name'], + response_dct['admin_url'] += '/0/' + return response_dct, None + author = PollUser(name=request.POST['author_name']) + author.save() + base_url = 'b' + genRandomURL() + admin_url = 'a' + genRandomURL() + poll = Poll(name=request.POST['poll_name'], description=request.POST['poll_desc'], author=author, base_url=base_url, admin_url=admin_url, status = 'D', type=request.POST['poll_type']) - poll.save() - url = "/".join(request.path.split('/')[:-2]) \ - + '/%s/' % poll.admin_url - return HttpResponseRedirect(url) - elif admin_url == '0': - response_dct['new'] = True - response_dct['admin_url'] = \ - "/".join(request.path.split('/')[:-2]) + '/0/' - else: + poll.save() + url = response_dct['admin_url'] + '/%s/' % poll.admin_url + return response_dct, HttpResponseRedirect(url) + + def getExistingPoll(request, response_dct, admin_url): + "Get an existing poll" try: poll = Poll.objects.filter(admin_url=admin_url)[0] except IndexError: - url = "/".join(request.path.split('/')[:-2]) + '/0/' - return HttpResponseRedirect(url) - response_dct['choices'] = Choice.objects.filter(poll=poll).order_by('order') - response_dct['author_name'] = poll.author.name - response_dct['poll_name'] = poll.name - response_dct['poll_desc'] = poll.description - idx = [type[0] for type in poll.TYPE].index(poll.type) - response_dct['type_name'] = Poll.TYPE[idx][1] - response_dct['poll_status'] = poll.status - response_dct['admin_url'] = \ - "/".join(request.path.split('/')[:-2]) + '/%s/' % poll.admin_url + # if the poll don't exist redirect to the creation page + url = response_dct['admin_url'] + '/0/' + return response_dct, HttpResponseRedirect(url) + # base feed of the template + new_dct = {'author_name':poll.author.name, + 'poll_name':poll.name, + 'poll_desc':poll.description, + 'choices':Choice.objects.filter(poll=poll).order_by('order'), + 'poll_status':poll.status, + 'type_name':poll.getTypeLabel()} + response_dct.update(new_dct) + + # urls base_path = request.META['HTTP_HOST'] + \ "/".join(request.path.split('/')[:-3]) - response_dct['full_admin_url'] = base_path + "/edit/" \ - + admin_url + "/" response_dct['base_url'] = poll.base_url response_dct['full_base_url'] = base_path + "/poll/" \ + poll.base_url + "/" - response_dct['choiceform'] = "<input type='text' name='new_choice'/>" + response_dct['admin_url'] += '/%s/' % poll.admin_url + response_dct['full_admin_url'] = base_path + "/edit/" \ + + admin_url + "/" + + # if a new choice is submitted if 'new_choice' in request.POST and request.POST['new_choice']: try: order = Choice.objects.order_by('-order')[0].order @@ -104,110 +114,194 @@ admin_url=admin_url, status = 'D', type=request.POST['poll_type']) choice = Choice(poll=poll, name=request.POST['new_choice'], order=order) choice.save() + # check if a choice has been choosen for deletion for key in request.POST: if key.startswith('delete_') and request.POST[key]: choice = Choice.objects.get(id=int(key[len('delete_'):])) Vote.objects.filter(choice=choice).delete() choice.delete() + return response_dct, None + + response_dct = getBaseResponse(request) + response_dct['TYPES'] = Poll.TYPE + response_dct['admin_url'] = \ + "/".join(request.path.split('/')[:-2]) + redirection = None + if 'new' in request.POST: + # new poll is submited + response_dct, redirection = submitNewPoll(request, response_dct) + elif admin_url == '0': + # new empty poll + response_dct['new'] = True + response_dct['admin_url'] += '/0/' + else: + # existing poll + response_dct, redirection = getExistingPoll(request, + response_dct, admin_url) + if redirection: + return redirection return render_to_response('createOrEdit.html', response_dct) def poll(request, poll_url): + "Display a poll" + + def modifyVote(request, choices): + "Modify user's votes" + try: + author = PollUser.objects.filter( + id=int(request.POST['voter']))[0] + except (ValueError, IndexError): + return + # if no author_name is given deletion of associated votes and + # author + if not request.POST['author_name']: + for choice in choices: + v = Vote.objects.filter(voter=author, choice=choice) + v.delete() + author.delete() + return + # update the name + author.name = request.POST['author_name'] + author.save() + selected_choices = [] + # set the selected choices + for key in request.POST: + if key.startswith('vote_') and request.POST[key]: + try: + id = int(key.split('_')[1]) + vote = Vote.objects.filter(id=id)[0] + if vote.choice not in choices: + # bad vote id : the associated choice has + # probably been deleted + vote.delete() + else: + vote.vote = 1 + vote.save() + selected_choices.append(vote.choice) + except (ValueError, IndexError): + # the vote don't exist with this choice + v = Vote(voter=author, choice=choice, value=1) + v.save() + if key.startswith('choice_') and request.POST[key]: + try: + id = int(key.split('_')[1]) + choice = Choice.objects.filter(id=id)[0] + if choice not in choices: + raise ValueError + v = Vote(voter=author, choice=choice, value=1) + v.save() + selected_choices.append(choice) + except (ValueError, IndexError): + # bad choice id : the choice has probably been deleted + pass + # update non selected choices + for choice in choices: + if choice not in selected_choices: + try: + v = Vote.objects.filter(voter=author, choice=choice)[0] + v.vote = 0 + except IndexError: + # the vote don't exist with this choice : probably + # a new choice + v = Vote(voter=author, choice=choice, value=0) + v.save() + + def newVote(request, choices): + "Create new votes" + if not request.POST['author_name']: + return + author = PollUser(name=request.POST['author_name']) + author.save() + selected_choices = [] + + # set the selected choices + for key in request.POST: + if key.startswith('choice_') and request.POST[key]: + try: + id = int(key.split('_')[1]) + choice = Choice.objects.filter(id=id)[0] + if choice not in choices: + raise ValueError + v = Vote(voter=author, choice=choice, value=1) + v.save() + selected_choices.append(choice) + except (ValueError, IndexError): + # bad choice id : the choice has probably been deleted + pass + # set non selected choices + for choice in choices: + if choice not in selected_choices: + v = Vote(voter=author, choice=choice, value=0) + v.save() + response_dct = getBaseResponse(request) - error = None try: poll = Poll.objects.filter(base_url=poll_url)[0] except IndexError: poll = None choices = Choice.objects.filter(poll=poll).order_by('order') + # if the poll don't exist or if it has no choices the user is + # redirected to the main page if not choices or not poll: url = "/".join(request.path.split('/')[:-3]) - url += "?bad_poll=1" + url += "/?bad_poll=1" return HttpResponseRedirect(url) - response_dct['base_url'] = \ - "/".join(request.path.split('/')[:-2]) + '/%s/' % poll.base_url - - response_dct['choices'] = choices + + # a vote is submitted if 'author_name' in request.POST: if 'voter' in request.POST: - try: - author = PollUser.objects.filter(id=int(request.POST['voter']))[0] - except (ValueError, IndexError): - author = None - if author: - author.name = request.POST['author_name'] - author.save() - selected_choices = [] - for key in request.POST: - if key.startswith('vote_') and request.POST[key]: - try: - id = int(key.split('_')[1]) - vote = Vote.objects.filter(id=id)[0] - if vote.choice not in choices: - raise ValueError - vote.vote = 1 - vote.save() - except (ValueError, IndexError): - url = "/".join(request.path.split('/')[:-3]) - url += "?bad_poll=1" - return HttpResponseRedirect(url) - selected_choices.append(vote.choice) - for choice in choices: - if choice not in selected_choices: - try: - v = Vote.objects.filter(voter=author, choice=choice)[0] - v.vote = 0 - except IndexError: - v = Vote(voter=author, choice=choice, vote=0) - v.save() - + # modification of an old vote + modifyVote(request, choices) else: - author = PollUser(name=request.POST['author_name']) - author.save() - selected_choices = [] - for key in request.POST: - if key.startswith('choice_') and request.POST[key]: - try: - id = int(key.split('_')[1]) - choice = Choice.objects.filter(id=id)[0] - if choice not in choices: - raise ValueError - except (ValueError, IndexError): - url = "/".join(request.path.split('/')[:-3]) - url += "?bad_poll=1" - return HttpResponseRedirect(url) - v = Vote(voter=author, choice=choice, vote=1) - selected_choices.append(choice) - v.save() - for choice in choices: - if choice not in selected_choices: - v = Vote(voter=author, choice=choice, vote=0) - v.save() - votes = [] + newVote(request, choices) + + # 'voter' is in request.GET when the edit button is pushed + if 'voter' in request.GET: + try: + response_dct['current_voter_id'] = int(request.GET['voter']) + except ValueError: + pass + + response_dct.update({'choices':choices, + 'poll_type_name':poll.getTypeLabel(), + 'poll_name':poll.name, + 'poll_desc':poll.description}) + response_dct['base_url'] = "/".join(request.path.split('/')[:-2]) \ + + '/%s/' % poll.base_url + + # get voters and sum for each choice for this poll + + votes = [] # all votes for this poll votes = Vote.objects.extra(where=['choice_id IN (%s)' \ % ",".join([str(choice.id) for choice in choices])]) + voters = [] choices_sum = [0 for choice in choices] choices_ids = [choice.id for choice in choices] for vote in votes: if vote.voter not in voters: + # initialize a votes list for the current voter vote.voter.votes = [None for choice in choices] voters.append(vote.voter) voter = vote.voter else: + # get the appropriate voter voter = voters[voters.index(vote.voter)] idx = choices_ids.index(vote.choice.id) + # associate vote in the votes list of the voter voter.votes[idx] = vote - choices_sum[idx] += vote.vote - response_dct['voters'] = voters - if 'voter' in request.GET: - try: - response_dct['current_voter_id'] = int(request.GET['voter']) - except ValueError: - pass - response_dct['voter'] = voters - response_dct['choices_sum'] = [str(sum) for sum in choices_sum] - response_dct['poll_type_name'] = poll.getTypeLabel() - response_dct['poll_name'] = poll.name - response_dct['poll_desc'] = poll.description + choices_sum[idx] += vote.value + # for undefined vote get the choice id + # on the template the distinction between the choice and the voter + # is made by the type of the "vote" + for voter in voters: + for vote in voter.votes: + if not vote: + idx = voter.votes.index(vote) + voter.votes[idx] = choices[idx].id + response_dct.update({'voters':voters, + 'voter':voters, + 'choices_sum':[str(sum) for sum in choices_sum] + }) return render_to_response('vote.html', response_dct) diff --git a/static/bg.jpg b/static/bg.jpg Binary files differindex e603ff8..7653c63 100644 --- a/static/bg.jpg +++ b/static/bg.jpg diff --git a/static/styles.css b/static/styles.css index 2b88e3c..a5bd11e 100644 --- a/static/styles.css +++ b/static/styles.css @@ -10,22 +10,13 @@ font-size:12px; background-color:#ced3e1; } -#main{ -background-color:white; -border:1px solid; -margin:20px; -background-image: url(bg.jpg); -background-repeat:no-repeat; -background-position:top right; -} - h1, h1 a{ border:1px solid black; border-right:None; border-left:None; background-color:#6f819d; color:white; -padding-left:15px; +padding-left:10px; font-size:32px; text-decoration:None; } @@ -41,7 +32,7 @@ font-size:24px; p{ padding:6px; margin:6px; -width:600px; +max-width:600px; } td{ @@ -58,10 +49,33 @@ label{ font-weight:bold; } +#main{ +background-color:white; +border:1px solid; +margin:20px; +background-image: url(bg.jpg); +background-repeat:no-repeat; +background-position:top right; +} + +.error{ +color:red; +} + +#new_poll input{ +width:130px; +} + +#new_poll textarea{ +width:130px; +height:100px; +} + .form_description{ background-color:#6f819d; color:white; font-size:11px; +width:200px; } #content{ @@ -72,52 +86,52 @@ margin:5px; overflow:auto; } -.error{ -color:red; -} - -table.poll{ +#poll{ text-align:center; } -.poll a{ +#poll a{ color:black; } -.poll td{ +#poll td{ border:1px solid black; padding:0; } -.poll th{ +#poll td.simple{ +border:None; +} + +#poll th{ background-color:#ced3e1; border:1px solid black; padding:5px; } -.poll .OK{ +#poll input{ +width:100px; +} + +#poll .OK{ background-color:#9ec5d5; } -.poll .OKO{ +#poll .OKO{ background-color:#b689d5; } -.poll .KO{ +#poll .KO{ background-color:#b9b3bd; } -td.simple{ -border:None; -} - -.sum th{ +#sum th{ background-color:white; border:None; text-align:center; } -.sum td{ +#sum td{ border:None; font-weight:bold; } diff --git a/templates/createOrEdit.html b/templates/createOrEdit.html index cee7616..073dcfd 100644 --- a/templates/createOrEdit.html +++ b/templates/createOrEdit.html @@ -2,12 +2,12 @@ {% block content %} {% if not new and not choices %} - <p class='error'>As long as no option was added to the poll, it will not be made available.</p> + <p class='error'>As long as no options were added to the poll, it will not be made available.</p> {% endif %} <h2>{% if new %}New{% else %}Edit{% endif %} poll</h2> {% if error %}<p class='error'>{{ error }}</p>{% endif %} <form action="{{admin_url}}" method="post"> - <table class='new_poll'> + <table id='new_poll'> {% if not new %}<tr> <td><label>Poll url</label></td> <td><a href='http://{{full_base_url}}'>http://{{full_base_url}}</a></td> @@ -54,7 +54,7 @@ {% for choice in choices %}<tr> <td> </td><td>{{choice.name}}</td><td><input type='checkbox' name='delete_{{choice.id}}'/></td> </tr> - {% endfor %}{% endif %}<tr><td><label>New choice</label></td><td>{{choiceform}}</td></tr> + {% endfor %}{% endif %}<tr><td><label>New choice</label></td><td><input type='text' name='new_choice'/></td></tr> {% endif %}</table> {% if new %}<input type='hidden' name='new' value='1'/> <input type='submit' value='Create' /> diff --git a/templates/vote.html b/templates/vote.html index 19150d2..1b862c4 100644 --- a/templates/vote.html +++ b/templates/vote.html @@ -6,7 +6,7 @@ <p>{{poll_desc}}</p> <form method='post' action='{{base_url}}'> <div id='poll_table'> - <table class='poll'> + <table id='poll'> <tr> <td class='simple'></td> <td class='simple'></td> @@ -16,10 +16,10 @@ <input type='hidden' name='voter' value='{{voter.id}}'/> <td class='simple'></td> <td><input type='text' name='author_name' value='{{voter.name}}'/></td> - {% for vote in voter.votes %}<td><input type='checkbox' name='vote_{{vote.id}}'{%ifequal vote.vote 1%} checked='checked'{%endifequal%}/></td>{%endfor%} + {% for vote in voter.votes %}<td><input type='checkbox' name='{%if vote.id%}vote_{{vote.id}}{%else%}choice_{{vote}}{%endif%}'{%ifequal vote.value 1%} checked='checked'{%endifequal%}/></td>{%endfor%} {%else%}<td class='simple'><a href='?voter={{voter.id}}'>Edit</a></td> <td>{{voter.name}}</td> - {% for vote in voter.votes %}<td class='{%ifequal vote.vote 1%}OK{%else%}KO{%endifequal%}'>{% ifequal vote.vote 1%}Yes{%else%}No{%endifequal%}</td> + {% for vote in voter.votes %}<td class='{%ifequal vote.value 1%}OK{%else%}KO{%endifequal%}'>{% ifequal vote.value 1%}Yes{%else%}No{%endifequal%}</td> {%endfor%} {%endifequal%} </tr>{%endfor%} @@ -30,7 +30,7 @@ {%for choice in choices%}<td><input type='checkbox' name='choice_{{choice.id}}'/></td>{%endfor%} </tr> {%endif%} - <tr class='sum'> + <tr id='sum'> <td class='simple'></td><th>Sum</th> {% for sum in choices_sum %}<td>{{sum}}</td> {% endfor %} |