Similar to what we did in PR "Avoid duplicate records in poll answers" 5539,
specifically in commit 503369166, we want to stop relying on the plain text
"answer" and start using "option_id" to avoid issues with counts across
translations and to add consistency to the poll_partial_results table.
Note that we also moved the `possible_answers` method from Poll::Question to
Poll::Question::Option, since the list of valid answers really comes from the
options of a question and not from the question itself. Tests were updated
to validate answers against the translations of the assigned option.
Additionally, we renamed lambda parameters in validations to improve clarity.
Until now, when writing `create(:poll_answer, option: option)`, the
`answer` field would take the title of a random option instead of taking
the title from the `option` variable.
So now, if we're given the option, the `answer` field will be taken from
the option itself.
Note that writing something like:
```
option { question.question_options.find_by(title: answer) }
answer { option.title }
```
Would create an infinite loop when creating a poll answer if we don't
pass the `option` and/or the `answer` attributes.
So, instead, we're making the `option` depend on the `answer` attribute
exclusively when we pass the `answer` attribute to the factory.
According to the GeoJSON specification [1]:
> * A linear ring is a closed LineString with four or more positions.
> * The first and last positions are equivalent, and they MUST contain
> identical values; their representation SHOULD also be identical.
> (...)
> * For type "Polygon", the "coordinates" member MUST be an array of
> linear ring coordinate arrays.
Note that, for simplicity, right now we aren't checking whether the
coordinates are defined counterclockwise for exterior rings and
clockwise for interior rings, which is what the specification expects.
[1] https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6
We're reworking the format validation to correctly interpret feature
collection, feature, and geometry, according to RFC 7946 [1].
Since Leaflet interprets GeoJSON format, we're rendering the GeoJSON as
a layer instead of as a set of points. For that, we're normalizing the
GeoJSON to make sure it contains either a Feature or a
FeatureCollection. We're also adding the Leaflet images to the assets
path so the markers used for point geometries are rendered correctly.
Note we no longer allow a GeoJSON containing a geometry but not a
defined type. Since there might be invalid GeoJSON in existing Consul
Democracy databases, we're normalizing these existing geometry objects
to be part of a feature object.
We're also wrapping the outline points in a FeatureCollection object
because most of the large GIS systems eg ArcGIS, QGIS export geojson as
a complete FeatureCollection.
[1] https://datatracker.ietf.org/doc/html/rfc7946
Co-authored-by: Javi Martín <javim@elretirao.net>
We had a trait called `:admin_request` for actions that are requests to
administrators, but the default factories were also requests to
administrators.
The tests checking that the "Request" button is not present, which
shouldn't pass with the wrong default factories, were passing by
coincidence. The issue was that we weren't checking whether that the
request had finished before checking that the "Request" button wasn't
present. That meant that we were checking that the "Request" button
wasn't there right at the moment we pressed the link, before the request
was finished.
So we're now checking that the request is finished before checking that
the button isn't there.
On the other hand, the tests checking for the "Request resource" link
being present were checking a behavior that's no longer there since
commit 9d85b3935, when we changed the conditions affecting that link.
We were checking the proposal video URL, but its value was `nil` since
commit bedcb5bca2. This resulted in a warning:
```
Checking for expected text of nil is confusing and/or pointless since it
will always match. Please specify a string or regexp instead.
spec/system/admin/hidden_proposals_spec.rb:14
```
Since August 29, probably due to a change in the browser used by GitHub
Actions (since branches whose code we didn't change were suddenly
affected that day), many tests related to proposals are failing on
GitHub Actions. Although every time the test suite is run different
tests fail, on each run at least half a dozen tests fail.
Most tests have one thing in common: they click on an element on the
`proposals#show` page, and the click isn't done correctly.
One possible explanation is that the video included on the page causes
the page to scroll at the exact same time that Capybara is clicking on a
link, which results in a misclick.
I haven't been able to reproduce the issue on my machine, so I'm not
sure whether giving the video element a fixed height using CSS (so the
page doesn't scroll when the iframe is loaded) could solve the issue.
But, after using proposals without videos in the tests (except the tests
testing the videos themselves), all these tests are passing in the test
suite.
So, for now, we're simply removing the videos in the proposal factories.
Note this issue wasn't caused by the "no cookie" changes done in commit
ee64efe659, since running the tests in Cosul Democracy 2.1.1 (which
didn't contain those changes) also fails on GitHub Actions.
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.
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.
We were tracking some events with Ahoy, but in an inconsistent way. For
example, we were tracking when a debate was created, but (probably
accidentally) we were only tracking proposals when they were created
from the management section. For budget investments and their supports,
we weren't using Ahoy events but checking their database tables instead.
And we were only using ahoy events for the charts; for the other stats,
we were using the real data.
While we could actually fix these issues and start tracking events
correctly, existing production data would remain broken because we
didn't track a certain event when it happened. And, besides, why should
we bother, for instance, to track when a debate is created, when we can
instead access that information in the debates table?
There are probably some features related to tracking an event and their
visits, but we weren't using them, and we were storing more user data
than we needed to.
So we're removing the track events, allowing us to simplify the code and
make it more consistent. We aren't removing the `ahoy_events` table in
case existing Consul Democracy installations use it, but we'll remove it
after releasing version 2.2.0 and adding a warning in the release notes.
This change fixes the proposal created chart, since we were only
tracking proposals created in the management section, and opens the
possibility to add more charts in the future using data we didn't track
with Ahoy.
Also note the "Level 2 user Graph" test wasn't testing the graph, so
we're changing it in order to test it. We're also moving it next to the
other graphs test and, since we were tracking the event when we were
confirming the phone, we're renaming to "Level 3 users".
Finally, note that, since we were tracking events when something was
created, we're including the `with_hidden` scope. This is also
consistent with the other stats shown in the admin section as well as
the public stats.
The only way to use campaigns is to manually insert them in the
database, which IMHO isn't very practical.
We're going to change every piece of code that generates an Ahoy event
and, in this case, the easiest way to change the Campaing model so it
doesn't use Ahoy events is to simply remove it.
Note we're keeping the database tables until we release a new version,
just in case some Consul Democracy installations are using them. We'll
inform in the release notes that we'll remove the campaigns table after
the release, so existing installations using the `campaigns` table can
move the data somewhere else before we remove the table.
We were testing what happens when clicking on a geozone without HTML
coordinates, which won't happen in a real browser.
So we're now defining the HTML coordinates and clicking on the area in
the test, which is what real people will do.
We also avoid having two consecutive `visit` calls, which will interfere
with the way we plan to test the presence of the <main> tag after every
`visit`.
Note that, the test didn't work with the HTML coordinates defined in the
`with_html_coordinates` trait, with Capybara showing the following
error:
```
Selenium::WebDriver::Error::ElementClickInterceptedError:
element click intercepted: Element
<area shape="poly"
coords="30,139,45,153,77,148,107,165"
href="/proposals?search=California"
title="California" alt="California">
is not clickable at point (413, 456).
Other element would receive the click:
<img usemap="#map" src="/assets/map.jpg">
```
The cause of this error was the strange shape of the polygon, which was
greatly concave and and so the middle of its area wasn't part of it.
We're changing the polygon so it's now convex and when Capybara clicks
on its middle point everything will work as expected.
When we create a budget heading through factories it's placed at Puerta del Sol,
Madrid. It seems reasonable that the `map_location` factory places the points near
there.
Before these changes sometimes the map center was placed in Madrid while map
locations were placed in Greenwich, therefore markers were not visible in the
map current pane.
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
* Add Tables option to Redcarpet in Legislation draft
* Allow table tags in Admin Legislation Sanitizer
* Add Test to render markdown tables in Legislation drafts
* Add Test for Admin Legislation Sanitizer
We include test for image, table and h1 to h6 tags and additional tests to strengthen the allowed and disallowed parameters
* Add Table from markdown test in System and Factories
* Add test to render tables for admin user
* Remove comment line about Redcarpet options
* Edit custom css for legislation draft table to make it responsive
Note that in the budgets wizard test we now create district with no
associated geozone, so the text "all city" will appear in the districts
table too, meaning we can't use `within "section", text: "All city" do`
anymore since it would result in an ambiguous match.
Co-Authored-By: Julian Herrero <microweb10@gmail.com>
Co-Authored-By: Javi Martín <javim@elretirao.net>
It was possible to remove a map location from a different proposal (even
one created by a different author) by modifying the hidden `id`
parameter in the form.
So we're making sure the map location we destroy is the one associated
to the proposal we're updating.
Since we're now using the `@proposal` instance variable in the
`destroy_map_location_association` method, we're calling that method
after loading the resource with cancancan.
We were rendering the whole sidebar again, which wasn't necessary since
most of it remains unchanged. This resulted in complicated code to pass
the necessary information to render the same map that was already
rendered. Furthermore, when users had been moving the map around or
zooming out, we were resetting the map to its default settings after
voting, which was potentially annoying.
This also fixes the wrong investments being displayed in the map after
voting; only the investments on the current page were displayed in this
case while the index displayed all of them.
Some institutions using CONSUL have expressed interest in this feature
since some of their tenants might already have their own domains.
We've considered many options for the user interface to select whether
we're using a subdomain or a domain, like having two separate fields,
using a check box, ... In the end we've chosen radio buttons because
they make it easier to follow a logical sequence: first you decide
whether you're introducing a domain or subdomain, and then you enter it.
We've also considered hiding this option and assuming "if it's got a
dot, it's a domain". However, this wouldn't work with nested subdomains
and it wouldn't work with domains which are simply machine names.
Note that a group of radio buttons (or check boxes) is difficult to
style when the text of the label might expand over more than one line
(as is the case here on small screens); in this case, most solutions
result in the second line of the label appearing immediately under the
radio button, instead of being aligned with the first line of the label.
That's why I've added a container for the input+label combination.
Note that the `create` action doesn't create an image but updates an
answer instead. We're removing the references to `:create` in the
abilities since it isn't used.
In the future we might change the form to add an image to an answer
because it's been broken for ages since it shows all the attached
images.
We were using this hack in order to allow `File.new` attachments in
tests files. However, we can use the `fixture_file_upload` helper
instead.
Just like it happened with `file_fixture`, this helper method doesn't
work in fixtures, so in this case we're using `Rack::Test::UploadedFile`
instead.
We're going to make it dynamic using the geozones. Besides, class
methods can be overwritten using custom models, while constants can't be
overwritten without getting a warning [1].
Makes the definition of segments with geozones a little cleaner. I
think it’s worth it, compared to the slight memory gain of using a
constant [2].
[1] warning: already initialized constant UserSegments::SEGMENTS
[2] https://stackoverflow.com/questions/15903835/class-method-vs-constant-in-ruby-rails#answer-15903970