Joining the translations table caused duplicate records to appear.
Ordering with SQL is simply too hard because we need to consider
fallback locales.
Thanks Senén for providing most of the tests in the poll spec.
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.
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.
This implementation is a bit more robust because we don't have to change
any of the "or_later?" methods if we add or remove a new phase.
We could also use metaprogramming to reduce code duplication in these
methods. So far, I've decided to keep the code simple since the
duplication seems reasonable.
As the Rails guides say:
> All scope methods will return an ActiveRecord::Relation object
That means `find_by_kind` will return a relation when nothing is found;
the expected behaviour is to return `nil`, like all `find_by` methods
do.
Using scopes also means strange things happen when we try to chain
scopes like `phases.published.drafting`. With scopes, the `drafting`
part would be ignored and all published phases would be returned.
The code is easier to read now, it returns the same results it used to
return, and performance-wise it's probably the same thing, but if it's
not, we'll trust Rails will do optimizations that we don't when we
manually pluck the IDs.
It is way more efficient because we're caching the result of that
method, and this way we only store each voter once in the cache. We were
storing many voters several times and then we were filtering them with
`uniq`.
It will make it far easier to call other methods on the stats object,
and we're already caching the methods.
We had to remove the view fragment caching because the stats object
isn't as easy to cache. The good thing about it is the view will
automatically be updated when we change logic regarding which stats to
show, and the methods taking long to execute are cached in the model.
We were expecting `balloters` to include `poll_ballot_voters` (that's
why we're substracting them to calculate web participants), but reality
has proven `poll_ballot_voters` aren't included in `balloters`.
Some queries were accessing original column instead of the new translatable one. This should have been causing unexpected behavior for requests maded in a different locale than the application default.
Some queries were accessing original column instead of the new
translatable one. This should have been causing unexpected behavior
for requests maded in a different locale than the application default.