Commit Graph

220 Commits

Author SHA1 Message Date
Javi Martín
07202fea10 Add and apply Style/RedundantBegin rubocop rule
We're about to add code which might fall into the `RedundantBegin`
category, so we're adding the rule in order to prevent that.
2024-10-30 15:57:44 +01:00
Javi Martín
00c97ad587 Split polls date range validation
It was a bit strange to leave the end date blank and have a message
associated with the start date, so we're using presence validations
instead.

For the range validation, we're using the comparison validator included
in Rails 7.0.
2024-07-22 18:35:35 +02:00
Javi Martín
5033691666 Avoid duplicate records in poll answers
Until now, we've stored the text of the answer somebody replied to. The
idea was to handle the scenarios where the user voters for an option but
then that option is deleted and restored, or the texts of the options
are accidentally edited and so the option "Yes" is now "Now" and vice
versa.

However, since commit 3a6e99cb8, options can no longer be edited once
the poll starts, so there's no risk of the option changing once somebody
has voted.

This means we can now store the ID of the option that has been voted.
That'll also help us deal with a bug introduced int 673ec075e, since
answers in different locales are not counted as the same answer. Note we
aren't dealing with this bug right now.

We're still keeping (and storing) the answer as well. There are two
reasons for that.

First, we might add an "open answer" type of questions in the future and
use this column for it.

Second, we've still got logic depending on the answer, and we need to be
careful when changing it because there are existing installations where
the answer is present but the option_id is not.

Note that we're using `dependent: nullify`. The reasoning is that, since
we're storing both the option_id and the answer text, we can still use
the answer text when removing the option. In practice, this won't matter
much, though, since we've got a validation rule that makes it impossible
to destroy options once the poll has started.

Also note we're still allowing duplicate records when the option is nil.
We need to do that until we've removed every duplicate record in the
database.
2024-06-26 20:20:24 +02:00
Javi Martín
9a8bfac5bd Prevent creation of duplicate poll voters
Note that, when taking votes from an erased user, since poll answers
don't belong to poll voters, we were not migrating them in the
`take_votes_from` method (and we aren't migrating them now either).
2024-06-26 15:41:44 +02:00
Javi Martín
a54d424aed Add missing validation rule to poll answers
We were checking we didn't have more votes than allowed in the case of
questions with multiple answers, but we weren't checking it in the case
of questions with a single answer. This made it possible to create more
than one answer to the same question. This could happen because the
method `find_or_initialize_user_answer` might initialize two answers in
different threads, due to a race condition.
2024-06-26 15:41:44 +02:00
Javi Martín
0c650c423d Fix exception creating an answer without an author
We were getting `undefined method `lock!' for nil:NilClass` when the
question allowed multiple answers.
2024-06-26 15:41:44 +02:00
Javi Martín
38b38d1fcc Rename Poll::Question::Answer to Poll::Question::Option
Having a class named `Poll::Question::Answer` and another class named
`Poll::Answer` was so confusing that no developer working on the project
has ever been capable of remembering which is which for more than a few
seconds.

Furthermore, we're planning to add open answers to polls, and we might
add a reference from the `poll_answers` table to the
`poll_question_answers` table to property differentiate between open
answers and closed answers. Having yet another thing named answer would
be more than what our brains can handle (we know it because we did this
once in a prototype).

So we're renaming `Poll::Question::Answer` to `Poll::Question::Option`.
Hopefully that'll make it easier to remember. The name is also (more or
less) consistent with the `Legislation::QuestionOption` class, which is
similar.

We aren't changing the table or columns names for now in order to avoid
possible issues when upgrading (old code running with the new database
tables/columns after running the migrations but before deployment has
finished, for instance). We might do it in the future.

I've tried not to change the internationalization keys either so
existing translations would still be valid. However, since we have to
change the keys in `activerecord.yml` so methods like
`human_attribute_name` keep working, I'm also changing them in places
where similar keys were used (like `poll_question_answer` or
`poll/question/answer`).

Note that it isn't clear whether we should use `option` or
`question_option` in some cases. In order to keep things simple, we're
using `option` where we were using `answer` and `question_option` where
we were using `question_answer`.

Also note we're adding tests for the admin menu component, since at
first I forgot to change the `answers` reference there and all tests
passed.
2024-06-13 19:13:01 +02:00
Javi Martín
fd04860032 Use consistent names in poll question answer tests
We were using three different variable names for the same thing, in
consecutite tests.
2024-06-12 15:16:14 +02:00
Javi Martín
a86f584791 Group people by age instead of birth year in stats
When calculating the stats on, say, January 1st 2024, and using a group
age of, say, between 20 and 24 years, we were considering that everyone
born between 2000 and 2004 had between 20 and 24 years. This wasn't
accurate, since most people born in 2004 would have 19 years at that
point, and most people born in 1999 would have 24 years.
2024-06-04 21:21:02 +02:00
Javi Martín
a4461a1a56 Expire the stats cache once per day
When we first started caching the stats, generating them was a process
that took several minutes, so we never expired the cache.

However, there have been cases where we run into issues where the stats
shown on the screen were outdated. That's why we introduced a task to
manually expire the cache.

But now, generating the stats only takes a few seconds, so we can
automatically expire them every day, remove all the logic needed to
manually expire them, and get rid of most of the issues related to the
cache being outdated.

We're expiring them every day because it's the same day we were doing in
public stats (which we removed in commit 631b48f58), only we're using
`expires_at:` to set the expiration time, in order to simplify the code.

Note that, in the test, we're using `travel_to(time)` so the test passes
even when it starts an instant before midnight. We aren't using
`:with_frozen_time` because, in similar cases (although not in this
case, but I'm not sure whether that's intentional), `travel_to` shows
this error:

> Calling `travel_to` with a block, when we have previously already made
> a call to `travel_to`, can lead to confusing time stubbing.
2024-05-17 20:11:16 +02:00
Javi Martín
1d85a63e7c Calculate age stats based on the participation date
We were calculating the age stats based on the age of the users who
participated... at the moment where we were calculating the stats. That
means that, if 20 years ago, 1000 people who were 16 years old
participated, they would be shown as having 36 years in the stats.

Instead, we want to show the stats at the time when the process took
place, so we're implementing a `participation_date` method.

Note that, for polls, we could actually use the `age` column in the
`poll_voters` table. However, doing so would be harder, would only work
for polls but not for budgets, and it wouldn't be statistically very
relevant, since the stats are shown by age groups, and only a small
percentage of people would change their age group (and only to the
nearest one) between the time they participate and the time the process
ends.

We might use the `poll_voters` table in the future, though, since we
have a similar issue with geozones and genders, and using the
information in `poll_voters` would solve it as well (only for polls,
though).

Also note that we're using the `ends_at` dates because some people but
be too young to vote when a process starts but old enough to vote when
the process ends.

Finally, note that we might need to change the way we calculate the
participation date for a budget, since some budgets might not enabled
every phase. Not sure how stats work in that scenario (even before these
changes).
2024-05-13 15:42:37 +02:00
Sebastia
be8f8e3bd8 Merge pull request #5148 from consuldemocracy/polls_order
Order expired polls by ends date
2024-01-25 15:34:29 +01:00
decabeza
bfb02093a6 Order expired polls by ends date 2023-12-11 15:38:55 +01:00
Javi Martín
0aee568977 Add and apply Rails/RedundantActiveRecordAllMethod
This rule was introduced in rubocop-rails 2.21.0.
2023-11-20 14:22:12 +01:00
Javi Martín
05757c7f02 Add and apply RSpec/Rails/InferredSpecType rule
This rule was added in rubocop-rspec 2.14.0.
2023-09-06 19:00:56 +02:00
Javi Martín
8e276e2891 Add FactoryBot/ConsistentParenthesesStyle rule
This rule was added in rubocop-rspec 2.14.0. We were already applying it
most of the time.
2023-09-06 19:00:56 +02:00
Javi Martín
f79a21f071 Add and apply RSpec/BeEq rubocop rule
This rule was added in rubocop-rspec 2.9.0. Once again, we were applying
it about 50% of the time.
2023-09-06 19:00:56 +02:00
Javi Martín
a1439d0790 Apply Layout/LineLength rubocop rule
Note we're excluding a few files:

* Configuration files that weren't generated by us
* Migration files that weren't generated by us
* The Gemfile, since it includes an important comment that must be on
  the same line as the gem declaration
* The Budget::Stats class, since the heading statistics are a mess and
  having shorter lines would require a lot of refactoring
2023-08-30 14:46:35 +02:00
Javi Martín
016595fd80 Simplify setting up poll geozones in test/dev data 2023-08-30 14:46:35 +02:00
Javi Martín
cebd6ddb30 Simplify code testing investment ids 2023-08-30 14:46:34 +02:00
Javi Martín
8b13daad95 Add and apply rules for multi-line hashes
For the HashAlignment rule, we're using the default `key` style (keys
are aligned and values aren't) instead of the `table` style (both keys
and values are aligned) because, even if we used both in the
application, we used the `key` style a lot more. Furthermore, the
`table` style looks strange in places where there are both very long and
very short keys and sometimes we weren't even consistent with the
`table` style, aligning some keys without aligning other keys.

Ideally we could align hashes to "either key or table", so developers
can decide whether keeping the symmetry of the code is worth it in a
case-per-case basis, but Rubocop doesn't allow this option.
2023-08-18 14:56:16 +02:00
Senén Rodero Rodríguez
3da4112d94 Remove Poll::Voter record when there is no more user answers
Now we can remove answers we should provide a way of removing voting.
2022-10-18 11:04:40 +02:00
decabeza
48d7ec75d0 Do not allow to create more answers than the maximum defined
In case we receive consecutive requests we are locking the poll author record
until the first request transaction ends, so the author answers count during
subsequent requests validations is up to date.
2022-10-18 10:39:00 +02:00
decabeza
4fba76f64f Do not show answers without additional information 2022-10-18 10:38:59 +02:00
Senén Rodero Rodríguez
64676be246 Remove token column from poll_voters table
As it is no longer used as originally pretended [1][2].

[1] Check consul/consul pull request 1994
[2] Check consul/consul pull request 3539
2022-09-22 10:34:07 +02:00
Julian Herrero
ecd22131f1 Disallow to modify the end date for an ended poll 2022-09-20 17:29:01 +02:00
Julian Herrero
8b5c315eb0 Allow to modify the end date as long as it is not to the past 2022-09-20 17:21:39 +02:00
Julian Herrero
471096c698 Disallow to modify the start date for an already started poll
We need to update a couple of tests because a poll is created in the
tests with a timestamp that includes nanoseconds and in the form to edit
the time of the poll the nanoseconds are not sent, meaning it was
detected as a change.
2022-09-20 17:21:36 +02:00
Julian Herrero
855fedc025 Allow only creation of polls with dates that are not in the past 2022-09-20 13:37:56 +02:00
taitus
d0359d584e Remove trait :current for poll on factories
When we create a poll with "create(:poll)" it is already a current poll.
2022-09-14 15:14:23 +02:00
Javi Martín
5a0fde4048 Allow selecting the time when a poll starts/ends
We were already saving it as a time, but we didn't offer an interface to
select the time due to lack of decent browser support for this field
back when this feature was added.

However, nowadays all major browsers support this field type and, at the
time of writing, at least 86.5% of the browsers support it [1]. This
percentage could be much higher, since support in 11.25% of the browsers
is unknown.

Note we still need to support the case where this field isn't supported,
and so we offer a fallback and on the server side we don't assume we're
always getting a time. We're doing a strange hack so we set the field
type to text before changing its value; otherwise old Firefox browsers
crashed.

Also note that, until now, we were storing end dates in the database as
a date with 00:00 as its time, but we were considering the poll to be
open until 23:59 that day. So, in order to keep backwards compatibility,
we're adding a task to update the dates of existing polls so we get the
same behavior we had until now.

This also means budget polls are now created so they end at the
beginning of the day when the balloting phase ends. This is consistent
with the dates we display in the budget phases table.

Finally, there's one test where we're using `beginning_of_minute` when
creating a poll. That's because Chrome provides an interface to enter a
time in a `%H:%M` format when the "seconds" value of the provided time
is zero. However, when the "seconds" value isn't zero, Chrome provides
an interface to enter a time in a `%H:%M:%S` format. Since Capybara
doesn't enter the seconds when using `fill_in` with a time, the test
failed when Capybara tried to enter a time in the `%H:%M` format when
Chrome expected a time in the `%H:%M:%S` format.

To solve this last point, an alternative would be to manually provide
the format when using `fill_in` so it includes the seconds.

[1] https://caniuse.com/mdn-html_elements_input_type_datetime-local
2022-09-14 15:14:23 +02:00
Javi Martín
3543767951 Don't include current polls in recounting polls
On the day when a poll ends, it was considered both current and
recounting.
2022-09-14 15:14:23 +02:00
Javi Martín
2153bd9e98 Add tests for current/expired/recounting polls
We didn't have model tests for some of these scopes, and neither scope
tests nor instance method tests covered edge cases like the current
date.
2022-09-14 15:14:23 +02:00
Javi Martín
bd85a09d32 Group poll scopes tests together
We're also using the convention of using a `.` to describe class
methods.
2022-09-14 15:14:23 +02:00
Javi Martín
c8270d58bd Add and apply Rails/DurationArithmetic rubocop rule
This rule was added in rubocop-rails 2.13.0. We were already applying it
most of the time.
2022-08-24 23:24:36 +02:00
Javi Martín
b59e2b31d3 Use unfreeze_time instead of travel_back
This is consistent with `with_frozen_time`.
2022-08-24 18:11:56 +02:00
Javi Martín
fb99d8cb33 Remove obsolete "pending" test
I'd say this feature is actually tested in the "proposal polls specific
validations"; the empty test was probably added by accident in commit
4b8cc85c4.
2022-04-07 15:34:10 +02:00
Javi Martín
b89c358d03 Remove comments related to code from Madrid
CONSUL doesn't implement blank votes via web; the comment was based on
the code used in Madrid, which was actually very complex.

And the concept of "all city" was also specific to Madrid. Poll
questions aren't associated to a geozone, so the geozone will depend on
the poll they're associated to.
2022-04-07 11:55:35 +02:00
Javi Martín
f6c4d70d78 Merge pull request #4775 from jorgebg/call_verify_ballots_after_create
Add offline ballot sheet votes to the total vote count
2022-03-22 19:17:36 +01:00
taitus
ecde8c6439 Add lambda to the validations that use model constants
In this way when we need modify the constants model value in the
model/custom folder, adding lambda it will be possible load the new
values.
2022-03-22 15:52:36 +01:00
Jorge Barata
b507acb38b Add ballot sheet votes to the total count 2022-03-21 20:33:13 +01:00
decabeza
25aa77c4c3 Show polls with the user's geozone first 2021-10-18 13:07:16 +02:00
Javi Martín
6d26ce57cb Show answers with attachments in additional info
We weren't showing the details of answers without a description, even if
they had images, videos or documents. Some users found that behavior
unexpected since the description isn't a mandatory field and so they
left it blank, but they added images to that answer and they didn't
appear on the poll page.

Note we had a condition not to show the title of an answer when it had
no description. I think that condition was redundant because answers
without a description weren't loaded in the first place. Anyway, that
condition doesn't make sense anymore because we're displaying answers
with images but no description.
2021-10-06 16:52:23 +02:00
Javi Martín
65c9786db7 Apply Layout/RedundantLineBreak rule to short lines
We're not adding the rule because it would apply the current line length
rule of 110 characters per line. We still haven't decided whether we'll
keep that rule or make lines shorter so they're easier to read,
particularly when vertically splitting the editor window.

So, for now, I'm applying the rule to lines which are about 90
characters long.
2021-09-03 11:49:53 +02:00
Javi Martín
9b61945ee4 Add and apply Lint/EmptyBlock rubocop rule
It was introduced in Rubocop 1.1.0.
2021-09-03 11:49:53 +02:00
Javi Martín
071bcb7023 Add and apply Rails/I18nLocaleAssignment rule
This rule was added in rubocop-rails 2.11.0.

Although we prevent I18n locale leaking between tests by setting it
before each test, the `with_locale` method makes the scope of the locale
change more obvious.
2021-09-03 11:49:52 +02:00
Javi Martín
433b465ec7 Don't assign IDs to assignment records in tests
The test "updates officer_assignment_id_log if amount changes" failed
when the ID we assigned when creating the records was the same as the ID
of the first officer assignment created during that test. It's recently
happened while running our test suite [1] with the following error:

```
1) Poll::Recount logging changes updates officer_assignment_id_log if
amount changes
  Failure/Error: poll_recount.officer_assignment =
                  create(:poll_officer_assignment, id: 101)

     ActiveRecord::RecordNotUnique:
       PG::UniqueViolation: ERROR:  duplicate key value violates unique
       constraint "poll_officer_assignments_pkey"
       DETAIL:  Key (id)=(101) already exists.
```

We're also removing the IDs in the "updates officer_assignment_id_log if
amount changes" test to avoid any possible issues, even if in this test
I think no other officer assignments are created and so there can't be
another record with the same ID.

[1] https://github.com/consul/consul/runs/2818889296
2021-06-17 01:46:48 +02:00
Javi Martín
852014e478 Add search method to polls
So far the method does not take questions nor answers into account.

This way we'll be able to search polls in the SDG Management section.
2020-12-21 18:04:48 +01:00
Javi Martín
4865cbeb59 Add and apply RSpec/ContextMethod rubocop rule
This rule is available since rubocop-rspec 1.36.0. We were already
applying it in most cases.
2020-10-25 14:23:53 +01:00
Javi Martín
096f546c24 Make sure users only vote once in the same poll
When skipping verification, we cannot apply the validation rule saying
the document number and document type must be unique, because they'll be
`nil` in many cases. So we were skipping the rule, but that makes it
possible for the same user to vote several times (for instance, once in
a booth and once via web).

So we're changing the scope of the uniqueness rule: instead of being
unique per document number, voters are unique per user. The reason we
made them unique per document number was that back in commit 900563e3
(when we added the rule), we hadn't added the relation between users and
poll voters yet.
2020-08-07 13:40:25 +02:00