Commit Graph

277 Commits

Author SHA1 Message Date
Javi Martín
16c16e3cdf Mark safe SQL with Arel.sql
Rails 5.2 is raising a warning in some places:

DEPRECATION WARNING: Dangerous query method (method whose arguments are
used as raw SQL) called with non-attribute argument(s). Non-attribute
arguments will be disallowed in Rails 6.0. This method should not be
called with user-provided values, such as request parameters or model
attributes. Known-safe values can be passed by wrapping them in
Arel.sql().

IMHO this warning is simply wrong, since we're using known PostgreSQL
functions like LOWER() or RANDOM(). AFAIK this code works without warnings
in Rails 6.0 [1][2]

However, since the warning is annoying, we need to take measures so our
logs are clean.

[1] https://github.com/rails/rails/commit/6c82b6c99d
[2] https://github.com/rails/rails/commit/64d8c54e16
2020-10-15 14:57:42 +02: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
decabeza
4367b2054a Allow voting when skip verification is enabled 2020-08-07 11:52:24 +02:00
Javi Martín
24ccf23ed8 Don't save the answer if the voter is not recorded
Up until now, we were assuming the voter was valid, but were not raising
an exception if it wasn't. And in the user interface everything seemed
to be working properly.

We were having this issue when skipping verification, when there could
be voters without a document number, which would be considered invalid.

Raising an exception when failing to save the voter and making sure the
answer and the voter are saved inside a transaction solves the problem.
2020-08-07 11:52:24 +02:00
Julian Herrero
89962ba61a Allow deleting polls with answers including videos
If a poll has a question with an answer containing a related video,
an error was raised because the poll ID was referenced in another
table.
2020-07-09 13:39:15 +02:00
Javi Martín
c6a8aa1301 Replace attribute_changed? in before callbacks
This method is deprecated in Rails 5.1 because its behavior will be
different in `before` and `after` callbacks.

Here we're replacing the deprecated `attribute_changed?` and
`attribute_was` with `will_save_change_to_attribute?` and
`attribute_in_database` during `before_save` callbacks.

https://github.com/rails/rails/pull/32835/
2020-04-24 15:43:54 +02:00
Javi Martín
216d9e61dc Remove unused Voter methods
AFAIK these methods were never used. In any case, the similar method
`set_demographic_info` was added in commit 6c34599e.
2019-11-10 00:34:45 +01:00
Julian Herrero
629c75ceeb Avoid error for polls results
When a poll is created, and any of the questions for that poll doesn't
have any answer created, the following exception was raised when
trying to see the results:

Failure/Error: question_answers.max_by {|answer| answer.total_votes }.id

  ActionView::Template::Error:
    undefined method `id' for nil:NilClass
      ./app/models/poll/question.rb:66:in `most_voted_answer_id'
2019-10-31 13:48:42 +01:00
Javi Martín
af7c37634d Remove poll votation types
Unfortunately this feature wasn't properly reviewed and tested, and it
had many bugs, some of them critical and hard to fix, like validations
being skipped in concurrent requests.

So we're removing it before releasing version 1.1. We might add it back
in the future if we manage to solve the critical issues.

This commit reverts commit 836f9ba7.
2019-10-30 18:48:55 +01:00
Javi Martín
a727dcc031 Apply Style/SymbolProc rubocop rule
This style is much more concise.
2019-10-26 20:10:32 +02:00
Javi Martín
ddb37f89ae Apply Style/Proc rubocop rule
While I tend to use `Proc.new`, using `proc` is shorter and more
consistent since we also use `lambda`.
2019-10-26 13:22:49 +02:00
Javi Martín
8b5cca746c Apply rubocop rules to freeze constants
Added by popular demand among our team members.
2019-10-26 13:21:36 +02:00
Javi Martín
eafb4018bf Apply Style/CollectionMethods rubocop rule
We were already using `map` and `reduce` almost everywhere.
2019-10-26 13:03:49 +02:00
Javi Martín
f07c422f21 Apply Layout/SpaceInLambdaLiteral rubocop rule
I had mixed feelings about this rule, since I like spaces where
possible.

However, I changed my mind when I realized writing `->(thing) { }` was
similar to defining a method, and we don't have a space before the
parenthesis when defining a method.
2019-10-26 13:03:49 +02:00
Javi Martín
97e826f2a4 Don't use update_attribute
This method is ambiguous. Sometimes we use it to set invalid data in
tests (which can usually be done with `update_column`), and other times
we use it instead of `update!`.

I'm removing it because, even if sometimes it could make sense to use
it, it's too similar to `update_attributes` (which is an alias for
`update` and runs validations), making it confusing.

However, there's one case where we're still using it: in the
ActsAsParanoidAliases module, we need to invoke the callbacks, which
`update_column` skips, but tests related to translations fail if we use
`update!`. The reason for this is the tests check what happens if we
restore a record without restoring its translations. But that will make
the record invalid, since there's a validation rule checking it has at
least one translation.

I'm not blacklisting any other method which skips validations because we
know they skip validations and use them anyway (hopefully with care).
2019-10-25 23:17:50 +02:00
Javi Martín
42d2e5b3ad Apply Rails/InverseOf rubocop rule
Not doing so has a few gotchas when working with relations, particularly
with records which are not stored in the database.

I'm excluding the related content file because it's got a very peculiar
relationship with itself: the `has_one :opposite_related_content` has no
inverse; the relation itself is its inverse. It's a false positive since
the inverse condition is true:

```
content.opposite_related_content.opposite_related_content.object_id ==
  content.object_id
```
2019-10-25 19:29:12 +02:00
Javi Martín
94d2496f8f Add missing has_many relations for users
Usually when we specify a `belongs_to` relations, we also specify its
equivalent `has_many`. That allows us to write, for example:
`topic.user.topics`.
2019-10-25 19:27:30 +02:00
Javi Martín
27ed26d6f2 Remove unnecessary class names in relations
Just like we do in the Budget module, and in some places in the Poll and
Legislation modules, we don't need to specify the class name when the
name of the relation matches the name of a class in the same module.
2019-10-25 19:03:14 +02:00
Javi Martín
fda53a0a2a Remove unnecessary foreign_key options
When we specify `belongs_to :author`, ActiveRecord automatically uses
`author_id` as the foreign key.
2019-10-25 19:03:10 +02:00
Javi Martín
d0d681a44b Add and apply EmptyLineAfterGuardClause rule
We were inconsistent on this one. I consider it particularly useful when
a method starts with a `return` statement.

In other cases, we probably shouldn't have a guard rule in the middle of
a method in any case, but that's a different refactoring.
2019-10-24 17:56:03 +02:00
Javi Martín
db97f9d08c Add and apply rubocop rules for empty lines
We were very inconsistent regarding these rules.

Personally I prefer no empty lines around blocks, clases, etc... as
recommended by the Ruby style guide [1], and they're the default values
in rubocop, so those are the settings I'm applying.

The exception is the `private` access modifier, since we were leaving
empty lines around it most of the time. That's the default rubocop rule
as well. Personally I don't have a strong preference about this one.


[1] https://rubystyle.guide/#empty-lines-around-bodies
2019-10-24 17:11:47 +02:00
Javier Martín
b6750e8f17 Merge pull request #3792 from consul/rubocop_find_by
Apply rubocop FindBy rules
2019-10-24 16:47:21 +02:00
Javi Martín
93c6347b45 Apply Rails/FindBy rubocop rule
We were already using it in most places.
2019-10-23 18:29:09 +02:00
Javi Martín
26e050b9f2 Remove redundant scopes
These scopes were already defined by the `enum :task` method.
2019-10-23 18:03:53 +02:00
Javi Martín
7ca55c44e0 Apply Rails/SaveBang rubocop rule
Having exceptions is better than having silent bugs.

There are a few methods I've kept the same way they were.

The `RelatedContentScore#score_with_opposite` method is a bit peculiar:
it creates scores for both itself and the opposite related content,
which means the opposite related content will try to create the same
scores as well.

We've already got a test to check `Budget::Ballot#add_investment` when
creating a line fails ("Edge case voting a non-elegible investment").

Finally, the method `User#send_oauth_confirmation_instructions` doesn't
update the record when the email address isn't already present, leading
to the test "Try to register with the email of an already existing user,
when an unconfirmed email was provided by oauth" fo fail if we raise an
exception for an invalid user. That's because updating a user's email
doesn't update the database automatically, but instead a confirmation
email is sent.

There are also a few false positives for classes which don't have bang
methods (like the GraphQL classes) or destroying attachments.

For these reasons, I'm adding the rule with a "Refactor" severity,
meaning it's a rule we can break if necessary.
2019-10-23 14:39:31 +02:00
Javi Martín
7bf4e4d611 Sanitize descriptions in the views
Sanitizing descriptions before saving a record has a few drawbacks:

1. It makes the application rely on data being safe in the database. If
somehow dangerous data enters the database, the application will be
vulnerable to XSS attacks
2. It makes the code complicated
3. It isn't backwards compatible; if we decide to disallow a certain
HTML tag in the future, we'd need to sanitize existing data.

On the other hand, sanitizing the data in the view means we don't need
to triple-check dangerous HTML has already been stripped when we see the
method `auto_link_already_sanitized_html`, since now every time we use
it we sanitize the text in the same line we call this method.

We could also sanitize the data twice, both when saving to the database
and when displaying values in the view. However, doing so wouldn't make
the application safer, since we sanitize text introduced through
textarea fields but we don't sanitize text introduced through input
fields.

Finally, we could also overwrite the `description` method so it
sanitizes the text. But we're already introducing Globalize which
overwrites that method, and overwriting it again is a bit too confusing
in my humble opinion. It can also lead to hard-to-debug behaviour.
2019-10-21 21:32:02 +02:00
Javi Martín
710a1d8357 Split Poll::Booth.search in two methods
We use this method in two different scenarios. In an AJAX request, we
don't want to return every booth if the search is blank. However, in a
normal HTTP GET request, we want to return every record when the search
is empty, as we do everywhere else.

It's possible the behaviour of the AJAX call is unusual, since it
searches all booths, and not just the ones assigned to a poll. If we
changed this behaviour, we could simplify the code and remove the
`quick_search` method.
2019-10-07 14:30:39 +02:00
Javi Martín
6ffe563264 Add an apply StringConversionInInterpolation rule 2019-10-05 15:03:26 +02:00
Javi Martín
70a07c095f Add and apply Style/BlockDelimiters rubocop rule
We were already using it most of the time, but there were a few places
were we used brackets for multiline blocks.
2019-10-05 14:44:14 +02:00
Javi Martín
d42b9ff4a5 Extract method to get valid answers to a question
This way we remove duplication and we avoid a multi-line block in a
validation rule, which made the code hard to read.
2019-10-05 14:34:52 +02:00
Javi Martín
9fe8c47528 Apply Rails/SafeNavigation rubocop rule 2019-09-10 21:43:39 +02:00
Javi Martín
f9ed186909 Add rubocop spacing rules
We were following these rules in most places; we just didn't define them
anywhere.
2019-09-10 21:04:56 +02:00
Javi Martín
488461b8ac Remove consecutive blank lines 2019-09-10 20:02:15 +02:00
Javi Martín
b5b07bccd3 Apply PercentLiteralDelimiters rubocop rule 2019-09-10 20:02:15 +02:00
lalo
7c9c50f4c6 Add Model changes to work with votation_types 2019-06-12 19:32:41 +02:00
Javier Martín
b14661e378 Merge pull request #3581 from consul/duplicate_translations
Avoid duplicate records using translations
2019-06-06 13:08:05 +02:00
Julian Nicolas Herrero
7802305b18 Merge pull request #3585 from consul/images_and_documents_settings
Images and documents settings
2019-06-05 19:11:22 +02:00
Julian Herrero
1f76b25e2a Show name and email for deleted poll officer's user account
Avoid to raise an exception `Module::DelegationError' when trying to
show the name and/or email of a poll officer whose user account has
been deleted.
We'll show a message "User deleted" and "Email deleted" instead.
2019-06-04 18:55:51 +02:00
Julian Herrero
8e0bbf54f6 Replace harcoded images and documents settings 2019-06-04 11:50:09 +02:00
Javi Martín
481184e7f3 Fix accidental translations join
We added the code thinking we were ordering by the name of the poll, but
here we're actually ordering by the name of the booth.
2019-06-03 18:36:03 +02:00
Javi Martín
123196e4ed Display the same results for stats and recounts
In the recounts we were incorrectly assuming the total amount included
the blank and invalid ballots.
2019-05-30 17:22:39 +02:00
Javi Martín
234a5108a4 Use strings for class_name
As mentioned in the Rails console:

DEPRECATION WARNING: Passing a class to the `class_name` is deprecated
and will raise an ArgumentError in Rails 5.2. It eagerloads more classes
than necessary and potentially creates circular dependencies. Please
pass the class name as a string.
2019-05-28 14:26:18 +02:00
Javier Martín
82e3c41aa9 Merge pull request #3512 from consul/backport-stats
Change stats layout
2019-05-21 17:18:47 +02:00
Javier Martín
f4891819db Merge pull request #3511 from consul/backport-gender_participants
Refactor gender and age stats methods
2019-05-21 17:17:53 +02:00
Javier Martín
07d55116ef Merge pull request #3510 from consul/backport-refactor_stats_module
Simplify stats caching
2019-05-21 17:16:37 +02:00
Javier Martín
d57025696f Merge pull request #3503 from consul/backport-blank_votes
Improve poll stats
2019-05-21 14:23:13 +02:00
Javi Martín
42a25ded3f Fix booth recounts poll stats discrepancies
Due to technical issues, sometimes users voted in booths and their vote
couldn't be added to the database. So we're including them in the users
with no demographic data.
2019-05-21 13:50:19 +02:00
Javi Martín
e2a3398ec0 Increase poll stats performance
Using SQL's `select` instead of converting the records to a ruby array
increases performance dramatically when there are thousands of records.
For a poll with 200000 voters, calculating stats took more than 7
minutes, and now it takes less than 2 minutes.
2019-05-21 13:50:19 +02:00
Javi Martín
aa0e813970 Use ruby cache for stats helper methods
These methods are only used while stats are being generated; once stats
are generated, they aren't used anymore. So there's no need to store
them using the Dalli cache.

Furthermore, there are polls (and even budgets) with hundreds of
thousands of participants. Calculating stats for them takes a very long
time because we can't store all those records in the Dalli cache.

However, since these records aren't used once the stats are generated,
we can store them in an instance variable while we generate the stats,
speeding up the process.
2019-05-21 13:50:19 +02:00
Javi Martín
7c0e499eee Add table to store stats versions
We need a way to manually expire the cache for a budget or poll without
expiring the cache of every budget or poll.

Using the `updated_at` column would be dangerous because most of the
times we update a budget or a poll, we don't need to regenerate their
stats.

We've considered adding a `stats_updated_at` column to each of these
tables. However, in that case we would also need to add a similar column
in the future to every process type whose stats we want to generate.
2019-05-21 13:50:19 +02:00