From 860ecb8dc8d75bb3f4da948fb218ce0d3725882d Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 04 2019 09:06:37 +0000 Subject: [PATCH 1/4] Add an allows_updating setting to the Elections This setting allows to configure letting contributors update their rejected submission by election. --- diff --git a/nuancier/admin.py b/nuancier/admin.py index 5a7b9ff..c524037 100644 --- a/nuancier/admin.py +++ b/nuancier/admin.py @@ -88,6 +88,7 @@ def admin_edit(election_id): election_n_choice=form.election_n_choice.data, user_n_candidates=form.user_n_candidates.data, election_badge_link=form.election_badge_link.data, + allows_updating=form.allows_updating.data, user=flask.g.fas_user.username, ) SESSION.commit() @@ -136,6 +137,7 @@ def admin_new(): election_n_choice=form.election_n_choice.data, user_n_candidates=form.user_n_candidates.data, election_badge_link=form.election_badge_link.data, + allows_updating=form.allows_updating.data, user=flask.g.fas_user.username, ) diff --git a/nuancier/forms.py b/nuancier/forms.py index 465f340..90d96ce 100644 --- a/nuancier/forms.py +++ b/nuancier/forms.py @@ -122,6 +122,7 @@ class AddElectionForm(BaseForm): 'Number of candidate an user can upload', [wtf.validators.Required(), is_number]) generate_cache = wtf.BooleanField('Generate cache') + allows_updating = wtf.BooleanField('Allows updating rejected candidate') def __init__(self, *args, **kwargs): ''' Calls the default constructor and fill in additional information. @@ -140,6 +141,7 @@ class AddElectionForm(BaseForm): self.election_n_choice.data = election.election_n_choice self.submission_date_start.data = election.submission_date_start self.user_n_candidates.data = election.user_n_candidates + self.allows_updating.data = election.allows_updating class AddCandidateForm(BaseForm): diff --git a/nuancier/lib/__init__.py b/nuancier/lib/__init__.py index 29a4d8f..eb20cb0 100644 --- a/nuancier/lib/__init__.py +++ b/nuancier/lib/__init__.py @@ -152,7 +152,7 @@ def get_results(session, election_id): def add_election(session, election_name, election_folder, election_year, election_date_start, election_date_end, submission_date_start, submission_date_end, - election_n_choice, user_n_candidates, + election_n_choice, user_n_candidates, allows_updating=True, election_badge_link=None, user=None): """ Add a new election to the database. @@ -183,6 +183,7 @@ def add_election(session, election_name, election_folder, election_year, election_n_choice=election_n_choice, user_n_candidates=user_n_candidates, election_badge_link=election_badge_link, + allows_updating=allows_updating, ) session.add(election) session.flush() @@ -201,7 +202,7 @@ def add_election(session, election_name, election_folder, election_year, def edit_election(session, election, election_name, election_folder, election_year, election_date_start, election_date_end, submission_date_start, submission_date_end, - election_n_choice, user_n_candidates, + election_n_choice, user_n_candidates, allows_updating=True, election_badge_link=None, user=None): """ Edit an election of the database. @@ -263,6 +264,10 @@ def edit_election(session, election, election_name, election_folder, election.user_n_candidates = user_n_candidates edited.append('Number of candidates per user') + if election.allows_updating != allows_updating: + election.allows_updating = allows_updating + edited.append('Election allows updating') + if edited: session.add(election) session.flush() diff --git a/nuancier/lib/model.py b/nuancier/lib/model.py index 2d354db..057a1a9 100644 --- a/nuancier/lib/model.py +++ b/nuancier/lib/model.py @@ -101,6 +101,7 @@ class Elections(BASE): submission_date_start = sa.Column(sa.Date, nullable=False) submission_date_end = sa.Column(sa.Date, nullable=False) user_n_candidates = sa.Column(sa.Integer, nullable=True) + allows_updating = sa.Column(sa.Boolean, nullable=False, default=True) date_created = sa.Column(sa.DateTime, nullable=False, default=sa.func.current_timestamp()) diff --git a/nuancier/templates/admin_edit.html b/nuancier/templates/admin_edit.html index 9133ce2..087b047 100644 --- a/nuancier/templates/admin_edit.html +++ b/nuancier/templates/admin_edit.html @@ -21,6 +21,7 @@ {{ render_field_in_row(form.election_badge_link) }} {{ render_field_in_row(form.election_n_choice) }} {{ render_field_in_row(form.user_n_candidates) }} + {{ render_field_in_row(form.allows_updating) }} diff --git a/nuancier/templates/admin_new.html b/nuancier/templates/admin_new.html index fa06a10..0302ab2 100644 --- a/nuancier/templates/admin_new.html +++ b/nuancier/templates/admin_new.html @@ -21,6 +21,7 @@ {{ render_field_in_row(form.election_badge_link) }} {{ render_field_in_row(form.election_n_choice) }} {{ render_field_in_row(form.user_n_candidates) }} + {{ render_field_in_row(form.allows_updating) }} {{ render_field_in_row(form.generate_cache) }} diff --git a/nuancier/ui.py b/nuancier/ui.py index a4be5ca..b223f63 100644 --- a/nuancier/ui.py +++ b/nuancier/ui.py @@ -494,6 +494,11 @@ def update_candidate(cand_id): 'You are not the person that submitted this candidate, you may ' 'not update it', 'error') return flask.redirect(flask.url_for('elections_list')) + elif not candidate.election.allows_updating: + flask.flash( + 'This election does not allow rejected candidate to be updated', + 'error') + return flask.redirect(flask.url_for('elections_list')) form = nuancier.forms.AddCandidateForm(obj=candidate) if form.validate_on_submit(): From 0a009a098086a677ac250c9bdd33dcf989a95455 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 04 2019 09:06:37 +0000 Subject: [PATCH 2/4] Do not show the update link when updates are not allowed --- diff --git a/nuancier/templates/contributions.html b/nuancier/templates/contributions.html index 322b9e4..89526e3 100644 --- a/nuancier/templates/contributions.html +++ b/nuancier/templates/contributions.html @@ -76,10 +76,14 @@ {{ candidate.approved_motif }} - {% if candidate.election.submission_open %} + {% if candidate.election.submission_open + and candidate.election.allows_updating %} Update + {% elif candidate.election.submission_open + and not candidate.election.allows_updating %} + Your submission cannot be updated {% else %} Submission are closed {% endif %} From 18c1cd4db08952d96555287b169c2005d8c5af8f Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 04 2019 09:06:37 +0000 Subject: [PATCH 3/4] Add unit-tests when trying to update a candidate on an election not allowing it --- diff --git a/tests/test_nuancier.py b/tests/test_nuancier.py index 3e40d54..ea5b49e 100644 --- a/tests/test_nuancier.py +++ b/tests/test_nuancier.py @@ -2005,6 +2005,56 @@ class Nuanciertests(Modeltests): self.assertFalse(os.path.exists(upload_path)) + def test_update_candidate_not_allowed(self): + """ Test the update_candidate function. """ + + create_elections(self.session) + create_candidates(self.session) + approve_candidate(self.session) + deny_candidate(self.session) + + upload_path = os.path.join(PICTURE_FOLDER, 'F21') + + user = FakeFasUser() + user.cla_done = True + with user_set(nuancier.APP, user): + # Election does not allow updating submissions + election = nuancierlib.get_election(self.session, 3) + election.allows_updating = False + self.session.add(election) + self.session.commit() + + output = self.app.get('/contribution/6/update', + follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertIn( + '
  • This election does not allow rejected ' + 'candidate to be updated
  • ', output.data) + + output = self.app.get('/election/2/vote/') + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + with open(FILE_OK) as stream: + data = { + 'candidate_name': 'name', + 'candidate_author': 'pingou', + 'candidate_file': stream, + 'candidate_license': 'CC-BY-SA', + 'csrf_token': csrf_token, + } + + output = self.app.post('/contribution/6/update', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertIn( + '
  • This election does not allow rejected ' + 'candidate to be updated
  • ', output.data) + self.assertTrue('

    Elections

    ' in output.data) + + self.assertFalse(os.path.exists(upload_path)) + + def test_contribute_max_upload(self): """ Test the contribute function when the user has already submitted a number of candidates. From 9a52928d5dfa234662b2b3aaf94efe453341dac5 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jul 04 2019 09:06:37 +0000 Subject: [PATCH 4/4] Add an alembic migration script adding the allows_updating field --- diff --git a/alembic/versions/9262dc2af69d_add_allow_updating_field.py b/alembic/versions/9262dc2af69d_add_allow_updating_field.py new file mode 100644 index 0000000..2de499d --- /dev/null +++ b/alembic/versions/9262dc2af69d_add_allow_updating_field.py @@ -0,0 +1,43 @@ +"""Add allow_updating field + +Revision ID: 9262dc2af69d +Revises: 7db0e24a2a85 +Create Date: 2017-04-11 13:50:35.179230 + +""" + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = '9262dc2af69d' +down_revision = '7db0e24a2a85' + + +def upgrade(): + """ Add the allows_updating field to the Elections table. """ + op.add_column( + 'Elections', + sa.Column( + 'allows_updating', + sa.Boolean, default=True, nullable=True) + ) + + try: + ins = "UPDATE \"Elections\" SET "\ + "allows_updating=True;" + op.execute(ins) + except Exception, err: + print 'ERROR', err + + ## Enforce the nullable=False + op.alter_column( + 'Elections', + column_name='allows_updating', + nullable=False, + existing_nullable=True,) + + +def downgrade(): + ''' Drop the columns allows_updating from the Elections table. ''' + op.drop_column('Elections', 'allows_updating')