diff --git a/helios/migrations/0002_v3_1_new_election_and_voter_fields.py b/helios/migrations/0002_v3_1_new_election_and_voter_fields.py index dd41fc6356da2a93c52bcc206dc89f06788e7ee2..e75643d805af504ee5b6ec6275150b2c876b8062 100644 --- a/helios/migrations/0002_v3_1_new_election_and_voter_fields.py +++ b/helios/migrations/0002_v3_1_new_election_and_voter_fields.py @@ -24,7 +24,8 @@ class Migration(SchemaMigration): db.add_column('helios_voter', 'voter_email', self.gf('django.db.models.fields.CharField')(max_length=250, null=True), keep_default=False) # Adding field 'Election.datatype' - db.add_column('helios_election', 'datatype', self.gf('django.db.models.fields.CharField')(default='2011/01/election', max_length=250), keep_default=False) + # manually tweaked default value to ensure proper datatype for older elections + db.add_column('helios_election', 'datatype', self.gf('django.db.models.fields.CharField')(default='legacy/election', max_length=250), keep_default=False) # Adding field 'Election.election_type' db.add_column('helios_election', 'election_type', self.gf('django.db.models.fields.CharField')(default='election', max_length=250), keep_default=False) diff --git a/helios/models.py b/helios/models.py index ba0e3a148875e682f4a16f096d69451a0b6422b4..115056492228d1132def4fa4f7f39d4392ce368a 100644 --- a/helios/models.py +++ b/helios/models.py @@ -87,6 +87,13 @@ class Election(models.Model, electionalgs.Election): registration_starts_at = models.DateTimeField(auto_now_add=False, default=None, null=True) voting_starts_at = models.DateTimeField(auto_now_add=False, default=None, null=True) voting_ends_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + + # if this is non-null, then a complaint period, where people can cast a quarantined ballot. + # we do NOT call this a "provisional" ballot, since provisional implies that the voter has not + # been qualified. We may eventually add this, but it can't be in the same CastVote table, which + # is tied to a voter. + complaint_period_ends_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + tallying_starts_at = models.DateTimeField(auto_now_add=False, default=None, null=True) # dates when things were forced to be performed @@ -730,6 +737,10 @@ class CastVote(models.Model, electionalgs.CastVote): cast_at = models.DateTimeField(auto_now_add=True) + # some ballots can be quarantined (this is not the same thing as provisional) + quarantined_p = modelsBooleanField(default=False, null=False) + released_from_quarantine_at = models.DateTimeField(auto_now_add=False, null=True) + # when is the vote verified? verified_at = models.DateTimeField(null=True) invalidated_at = models.DateTimeField(null=True) @@ -742,6 +753,10 @@ class CastVote(models.Model, electionalgs.CastVote): def voter_hash(self): return self.voter.hash + @property + def is_quarantined(self): + return self.quarantined_p and not self.released_from_quarantine_at + def set_tinyhash(self): """ find a tiny version of the hash for a URL slug. @@ -774,6 +789,10 @@ class CastVote(models.Model, electionalgs.CastVote): return cls.objects.filter(voter = voter).order_by('-cast_at') def verify_and_store(self): + # if it's quarantined, don't let this go through + if self.is_quarantined: + raise Exception("cast vote is quarantined, verification and storage is delayed.") + result = self.vote.verify(self.voter.election) if result: diff --git a/helios/tests.py b/helios/tests.py index 17d123ffbc7764aaafedb0c2312f7d57e9d6979c..078b914ac4029a140ca28aef524ac8bbd6f3c1cc 100644 --- a/helios/tests.py +++ b/helios/tests.py @@ -200,6 +200,19 @@ class VoterModelTests(TestCase): # check that you can get at the voter user structure self.assertEquals(v.user.user_id, v.voter_email) + +class CastVoteModelTests(TestCase): + fixtures = ['users.json', 'election.json'] + + def setUp(self): + self.election = models.Election.objects.get(short_name='test') + self.user = auth_models.User.objects.get(user_id='ben@adida.net', user_type='google') + + # register the voter + self.voter = models.Voter.register_user_in_election(self.user, self.election) + + def test_cast_vote(self): + assert False ## ## Black box tests ##