Add map to sidebar on Heading's page

Signed-off-by: Matheus Miranda <matheusmirandalacerda@gmail.com>
This commit is contained in:
Matheus Miranda
2018-11-08 09:27:03 -02:00
parent 6cfd0c2e13
commit 06f07b1139
19 changed files with 397 additions and 41 deletions

View File

@@ -819,6 +819,7 @@ footer {
.categories a,
.geozone a,
.sidebar-links a,
.sidebar-map a,
.tags span {
background: #ececec;
border-radius: rem-calc(6);

View File

@@ -33,7 +33,7 @@ class Admin::BudgetHeadingsController < Admin::BaseController
private
def budget_heading_params
params.require(:budget_heading).permit(:name, :price, :population)
params.require(:budget_heading).permit(:name, :price, :population, :latitude, :longitude)
end
end

View File

@@ -1,5 +1,7 @@
module Budgets
class InvestmentsController < ApplicationController
OSM_DISTRICT_LEVEL_ZOOM = 12
include FeatureFlags
include CommentableActions
include FlagActions
@@ -32,13 +34,17 @@ module Budgets
respond_to :html, :js
def index
if @budget.finished?
@investments = investments.winners.page(params[:page]).per(10).for_render
all_investments = if @budget.finished?
investments.winners
else
@investments = investments.page(params[:page]).per(10).for_render
investments
end
@investments = all_investments.page(params[:page]).per(10).for_render
@investment_ids = @investments.pluck(:id)
@investments_map_coordinates = MapLocation.where(investment_id: all_investments).map { |l| l.json_data }
load_investment_votes(@investments)
@tag_cloud = tag_cloud
end
@@ -142,6 +148,7 @@ module Budgets
if params[:heading_id].present?
@heading = @budget.headings.find(params[:heading_id])
@assigned_heading = @ballot.try(:heading_for_group, @heading.try(:group))
load_map
end
end
@@ -167,6 +174,13 @@ module Budgets
end
end
def load_map
@map_location = MapLocation.new
@map_location.zoom = OSM_DISTRICT_LEVEL_ZOOM
@map_location.latitude = @heading.latitude.to_f
@map_location.longitude = @heading.longitude.to_f
end
end
end

View File

@@ -11,6 +11,10 @@ class Budget
validates :price, presence: true
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
validates :population, numericality: { greater_than: 0 }, allow_nil: true
validates :latitude, length: { maximum: 22, minimum: 1 }, presence: true, \
format: /\A(-|\+)?([1-8]?\d(?:\.\d{1,})?|90(?:\.0{1,6})?)\z/
validates :longitude, length: { maximum: 22, minimum: 1}, presence: true, \
format: /\A(-|\+)?((?:1[0-7]|[1-9])?\d(?:\.\d{1,})?|180(?:\.0{1,})?)\z/
delegate :budget, :budget_id, to: :group, allow_nil: true

View File

@@ -1,28 +1,25 @@
<%= form_for [:admin, budget, group, heading], remote: true do |f| %>
<%= render 'shared/errors', resource: heading %>
<label><%= t("admin.budgets.form.heading") %></label>
<%= f.text_field :name,
label: false,
label: t("admin.budgets.form.heading"),
maxlength: 50,
placeholder: t("admin.budgets.form.heading") %>
<div class="row">
<div class="small-12 medium-6 column">
<label><%= t("admin.budgets.form.amount") %></label>
<%= f.text_field :price,
label: false,
label: t("admin.budgets.form.amount"),
maxlength: 8,
placeholder: t("admin.budgets.form.amount") %>
</div>
</div>
<div class="row">
<div class="small-12 medium-6 column">
<label><%= t("admin.budgets.form.population") %></label>
<p class="help-text" id="budgets-population-help-text">
<%= t("admin.budgets.form.population_help_text") %>
</p>
<%= f.text_field :population,
label: false,
label: t("admin.budgets.form.population"),
maxlength: 8,
placeholder: t("admin.budgets.form.population"),
data: {toggle_focus: "population-info"},
@@ -34,6 +31,22 @@
</div>
</div>
</div>
<div class="row">
<div class="small-12 medium-6 column">
<%= f.text_field :latitude,
label: t("admin.budgets.form.latitude"),
maxlength: 22,
placeholder: "latitude" %>
</div>
</div>
<div class="row">
<div class="small-12 medium-6 column">
<%= f.text_field :longitude,
label: t("admin.budgets.form.longitude"),
maxlength: 22,
placeholder: "longitude" %>
</div>
</div>
<%= f.submit t("admin.budgets.form.save_heading"), class: "button success" %>
<% end %>

View File

@@ -88,7 +88,7 @@
</div>
<% unless current_budget.informing? %>
<div id="map">
<div class="map">
<h3><%= t("budgets.index.map") %></h3>
<%= render_map(nil, "budgets", false, nil, @budgets_coordinates) %>
</div>

View File

@@ -0,0 +1,8 @@
<div class="sidebar-divider"></div>
<br>
<ul id="sidebar-map" class="no-bullet sidebar-map">
<div class="map">
<%= render_map(@map_location, "budgets", false, nil, @investments_map_coordinates) %>
</div>
</ul>

View File

@@ -21,6 +21,7 @@
</p>
<% end %>
<%= render 'budgets/investments/map' %>
<%= render "shared/tag_cloud", taggable: 'budget/investment' %>
<%= render 'budgets/investments/categories' %>

View File

@@ -131,6 +131,8 @@ en:
population_info: "Budget Heading population field is used for Statistic purposes at the end of the Budget to show for each Heading that represents an area with population what percentage voted. The field is optional so you can leave it empty if it doesn't apply."
max_votable_headings: "Maximum number of headings in which a user can vote"
current_of_max_headings: "%{current} of %{max}"
latitude: Latitude
longitude: Longitude
winners:
calculate: Calculate Winner Investments
calculated: Winners being calculated, it may take a minute.

View File

@@ -25,3 +25,4 @@ scope '/participatory_budget' do
end
get 'investments/:id/json_data', action: :json_data, controller: 'budgets/investments'
get '/budgets/:budget_id/investments/:id/json_data', action: :json_data, controller: 'budgets/investments'

View File

@@ -40,21 +40,31 @@ section "Creating Budgets" do
city_group = budget.groups.create!(name: I18n.t('seeds.budgets.groups.all_city'))
city_group.headings.create!(name: I18n.t('seeds.budgets.groups.all_city'),
price: 1000000,
population: 1000000)
population: 1000000,
latitude: '-40.123241',
longitude: '25.123249')
districts_group = budget.groups.create!(name: I18n.t('seeds.budgets.groups.districts'))
districts_group.headings.create!(name: I18n.t('seeds.geozones.north_district'),
price: rand(5..10) * 100000,
population: 350000)
population: 350000,
latitude: '15.234521',
longitude: '-15.234234')
districts_group.headings.create!(name: I18n.t('seeds.geozones.west_district'),
price: rand(5..10) * 100000,
population: 300000)
population: 300000,
latitude: '14.125125',
longitude: '65.123124')
districts_group.headings.create!(name: I18n.t('seeds.geozones.east_district'),
price: rand(5..10) * 100000,
population: 200000)
population: 200000,
latitude: '23.234234',
longitude: '-47.134124')
districts_group.headings.create!(name: I18n.t('seeds.geozones.central_district'),
price: rand(5..10) * 100000,
population: 150000)
population: 150000,
latitude: '-26.133213',
longitude: '-10.123231')
end
end

View File

@@ -0,0 +1,6 @@
class AddLocationToHeadings < ActiveRecord::Migration
def change
add_column :budget_headings, :latitude, :text
add_column :budget_headings, :longitude, :text
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180924071722) do
ActiveRecord::Schema.define(version: 20181109111037) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -156,6 +156,8 @@ ActiveRecord::Schema.define(version: 20180924071722) do
t.integer "price", limit: 8
t.integer "population"
t.string "slug"
t.text "latitude"
t.text "longitude"
end
add_index "budget_headings", ["group_id"], name: "index_budget_headings_on_group_id", using: :btree
@@ -580,7 +582,7 @@ ActiveRecord::Schema.define(version: 20180924071722) do
t.string "locale", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "title"
t.string "title"
t.text "changelog"
t.text "body"
t.text "body_html"
@@ -1418,6 +1420,12 @@ ActiveRecord::Schema.define(version: 20180924071722) do
add_index "votes", ["votable_id", "votable_type", "vote_scope"], name: "index_votes_on_votable_id_and_votable_type_and_vote_scope", using: :btree
add_index "votes", ["voter_id", "voter_type", "vote_scope"], name: "index_votes_on_voter_id_and_voter_type_and_vote_scope", using: :btree
create_table "web_sections", force: :cascade do |t|
t.text "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "widget_card_translations", force: :cascade do |t|
t.integer "widget_card_id", null: false
t.string "locale", null: false
@@ -1450,12 +1458,6 @@ ActiveRecord::Schema.define(version: 20180924071722) do
t.datetime "updated_at", null: false
end
create_table "web_sections", force: :cascade do |t|
t.text "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_foreign_key "administrators", "users"
add_foreign_key "annotations", "legacy_legislations"
add_foreign_key "annotations", "users"

View File

@@ -8,10 +8,10 @@ class MigrateSpendingProposalsToInvestments
if sp.geozone_id.present?
group = budget.groups.find_or_create_by!(name: "Barrios")
heading = group.headings.find_or_create_by!(name: sp.geozone.name, price: 10000000)
heading = group.headings.find_or_create_by!(name: sp.geozone.name, price: 10000000, latitude: '40.416775', longitude: '-3.703790')
else
group = budget.groups.find_or_create_by!(name: "Toda la ciudad")
heading = group.headings.find_or_create_by!(name: "Toda la ciudad", price: 10000000)
heading = group.headings.find_or_create_by!(name: "Toda la ciudad", price: 10000000, latitude: '40.416775', longitude: '-3.703790')
end
feasibility = case sp.feasible

View File

@@ -78,6 +78,8 @@ FactoryBot.define do
sequence(:name) { |n| "Heading #{n}" }
price 1000000
population 1234
latitude '-25.172741'
longitude '40.127241'
trait :drafting_budget do
association :group, factory: [:budget_group, :drafting_budget]

View File

@@ -276,6 +276,8 @@ feature 'Admin budgets' do
fill_in 'budget_heading_name', with: 'District 9 reconstruction'
fill_in 'budget_heading_price', with: '6785'
fill_in 'budget_heading_population', with: '100500'
fill_in 'budget_heading_latitude', with: '40.416775'
fill_in 'budget_heading_longitude', with: '-3.703790'
click_button 'Save heading'
end

View File

@@ -1436,10 +1436,10 @@ feature 'Budget Investments' do
user = create(:user, :level_two)
global_group = create(:budget_group, budget: budget, name: 'Global Group')
global_heading = create(:budget_heading, group: global_group, name: 'Global Heading')
global_heading = create(:budget_heading, group: global_group, name: 'Global Heading', latitude: -43.145412, longitude: 12.009423)
carabanchel_heading = create(:budget_heading, group: group, name: "Carabanchel")
new_york_heading = create(:budget_heading, group: group, name: "New York")
new_york_heading = create(:budget_heading, group: group, name: "New York", latitude: -43.223412, longitude: 12.009423)
sp1 = create(:budget_investment, :selected, price: 1, heading: global_heading)
sp2 = create(:budget_investment, :selected, price: 10, heading: global_heading)
@@ -1682,4 +1682,84 @@ feature 'Budget Investments' do
expect(Flag.flagged?(user, investment)).not_to be
end
context 'sidebar map' do
scenario "Display 6 investment's markers on sidebar map", :js do
investment1 = create(:budget_investment, heading: heading)
investment2 = create(:budget_investment, heading: heading)
investment3 = create(:budget_investment, heading: heading)
investment4 = create(:budget_investment, heading: heading)
investment5 = create(:budget_investment, heading: heading)
investment6 = create(:budget_investment, heading: heading)
create(:map_location, longitude: 40.1231, latitude: -3.636, investment: investment1)
create(:map_location, longitude: 40.1232, latitude: -3.635, investment: investment2)
create(:map_location, longitude: 40.1233, latitude: -3.634, investment: investment3)
create(:map_location, longitude: 40.1234, latitude: -3.633, investment: investment4)
create(:map_location, longitude: 40.1235, latitude: -3.632, investment: investment5)
create(:map_location, longitude: 40.1236, latitude: -3.631, investment: investment6)
visit budget_investments_path(budget, heading_id: heading.id)
within ".map_location" do
expect(page).to have_css(".map-icon", count: 6, visible: false)
end
end
scenario "Display 2 investment's markers on sidebar map", :js do
investment1 = create(:budget_investment, heading: heading)
investment2 = create(:budget_investment, heading: heading)
create(:map_location, longitude: 40.1281, latitude: -3.656, investment: investment1)
create(:map_location, longitude: 40.1292, latitude: -3.665, investment: investment2)
visit budget_investments_path(budget, heading_id: heading.id)
within ".map_location" do
expect(page).to have_css(".map-icon", count: 2, visible: false)
end
end
scenario "Display only investment's related to the current heading", :js do
heading_2 = create(:budget_heading, name: "Madrid", group: group)
investment1 = create(:budget_investment, heading: heading)
investment2 = create(:budget_investment, heading: heading)
investment3 = create(:budget_investment, heading: heading)
investment4 = create(:budget_investment, heading: heading)
investment5 = create(:budget_investment, heading: heading_2)
investment6 = create(:budget_investment, heading: heading_2)
create(:map_location, longitude: 40.1231, latitude: -3.636, investment: investment1)
create(:map_location, longitude: 40.1232, latitude: -3.685, investment: investment2)
create(:map_location, longitude: 40.1233, latitude: -3.664, investment: investment3)
create(:map_location, longitude: 40.1234, latitude: -3.673, investment: investment4)
create(:map_location, longitude: 40.1235, latitude: -3.672, investment: investment5)
create(:map_location, longitude: 40.1236, latitude: -3.621, investment: investment6)
visit budget_investments_path(budget, heading_id: heading.id)
within ".map_location" do
expect(page).to have_css(".map-icon", count: 4, visible: false)
end
end
scenario "Do not display investment's, since they're all related to other heading", :js do
heading_2 = create(:budget_heading, name: "Madrid", group: group)
investment1 = create(:budget_investment, heading: heading_2)
investment2 = create(:budget_investment, heading: heading_2)
investment3 = create(:budget_investment, heading: heading_2)
create(:map_location, longitude: 40.1255, latitude: -3.644, investment: investment1)
create(:map_location, longitude: 40.1258, latitude: -3.637, investment: investment2)
create(:map_location, longitude: 40.1251, latitude: -3.649, investment: investment3)
visit budget_investments_path(budget, heading_id: heading.id)
within ".map_location" do
expect(page).to have_css(".map-icon", count: 0, visible: false)
end
end
end
end

View File

@@ -5,7 +5,7 @@ feature 'Tags' do
let(:author) { create(:user, :level_two, username: 'Isabel') }
let(:budget) { create(:budget, name: "Big Budget") }
let(:group) { create(:budget_group, name: "Health", budget: budget) }
let!(:heading) { create(:budget_heading, name: "More hospitals", group: group) }
let!(:heading) { create(:budget_heading, name: "More hospitals", group: group, latitude: '40.416775', longitude: '-3.703790') }
let!(:tag_medio_ambiente) { create(:tag, :category, name: 'Medio Ambiente') }
let!(:tag_economia) { create(:tag, :category, name: 'Economía') }
let(:admin) { create(:administrator).user }

View File

@@ -44,6 +44,216 @@ describe Budget::Heading do
end
end
describe "save latitude" do
it "Doesn't allow latitude == nil" do
expect(build(:budget_heading, group: group, name: 'Latitude is nil', population: 12412512, latitude: nil, longitude: '12.123412')).not_to be_valid
end
it "Doesn't allow latitude == ''" do
expect(build(:budget_heading, group: group, name: 'Latitude is an empty string', population: 12412512, latitude: '', longitude: '12.123412')).not_to be_valid
end
it "Doesn't allow latitude < -90" do
heading = create(:budget_heading, group: group, name: 'Latitude is < -90')
heading.latitude = '-90.127491'
expect(heading).not_to be_valid
heading.latitude = '-91.723491'
expect(heading).not_to be_valid
heading.latitude = '-108.127412'
expect(heading).not_to be_valid
heading.latitude = '-1100.888491'
expect(heading).not_to be_valid
end
it "Doesn't allow latitude > 90" do
heading = create(:budget_heading, group: group, name: 'Latitude is > 90')
heading.latitude = '90.127491'
expect(heading).not_to be_valid
heading.latitude = '97.723491'
expect(heading).not_to be_valid
heading.latitude = '119.127412'
expect(heading).not_to be_valid
heading.latitude = '1200.888491'
expect(heading).not_to be_valid
heading.latitude = '+128.888491'
expect(heading).not_to be_valid
heading.latitude = '+255.888491'
expect(heading).not_to be_valid
end
it "Doesn't allow latitude length > 22" do
heading = create(:budget_heading, group: group, name: 'Latitude length is > 22')
heading.latitude = '10.12749112312418238128213'
expect(heading).not_to be_valid
heading.latitude = '7.7234941211121231231241'
expect(heading).not_to be_valid
heading.latitude = '9.1274124111241248688995'
expect(heading).not_to be_valid
heading.latitude = '+12.8884911231238684445311'
expect(heading).not_to be_valid
end
it "Allows latitude inside [-90,90] interval" do
heading = create(:budget_heading, group: group, name: 'Latitude is inside [-90,90] interval')
heading.latitude = '90'
expect(heading).to be_valid
heading.latitude = '-90'
expect(heading).to be_valid
heading.latitude = '-90.000'
expect(heading).to be_valid
heading.latitude = '-90.00000'
expect(heading).to be_valid
heading.latitude = '90.000'
expect(heading).to be_valid
heading.latitude = '90.00000'
expect(heading).to be_valid
heading.latitude = '-80.123451'
expect(heading).to be_valid
heading.latitude = '+65.888491'
expect(heading).to be_valid
heading.latitude = '80.144812'
expect(heading).to be_valid
heading.latitude = '17.417412'
expect(heading).to be_valid
heading.latitude = '-21.000054'
expect(heading).to be_valid
heading.latitude = '+80.888491'
expect(heading).to be_valid
end
end
describe "save longitude" do
it "Doesn't allow longitude == nil" do
expect(build(:budget_heading, group: group, name: 'Longitude is nil', population: 12412512, latitude: '12.123412', longitude: nil)).not_to be_valid
end
it "Doesn't allow longitude == ''" do
expect(build(:budget_heading, group: group, name: 'Longitude is an empty string', population: 12412512, latitude: '12.127412', longitude: '')).not_to be_valid
end
it "Doesn't allow longitude < -180" do
heading = create(:budget_heading, group: group, name: 'Longitude is < -180')
heading.longitude = '-180.127491'
expect(heading).not_to be_valid
heading.longitude = '-181.723491'
expect(heading).not_to be_valid
heading.longitude = '-188.127412'
expect(heading).not_to be_valid
heading.longitude = '-1100.888491'
expect(heading).not_to be_valid
end
it "Doesn't allow longitude > 180" do
heading = create(:budget_heading, group: group, name: 'Longitude is > 180')
heading.longitude = '190.127491'
expect(heading).not_to be_valid
heading.longitude = '197.723491'
expect(heading).not_to be_valid
heading.longitude = '+207.723491'
expect(heading).not_to be_valid
heading.longitude = '300.723491'
expect(heading).not_to be_valid
heading.longitude = '189.127412'
expect(heading).not_to be_valid
heading.longitude = '1200.888491'
expect(heading).not_to be_valid
end
it "Doesn't allow longitude length > 23" do
heading = create(:budget_heading, group: group, name: 'Longitude length is > 23')
heading.longitude = '50.1274911123124112312418238128213'
expect(heading).not_to be_valid
heading.longitude = '53.73412349178811231241'
expect(heading).not_to be_valid
heading.longitude = '+20.1274124124124123121435'
expect(heading).not_to be_valid
heading.longitude = '10.88849112312312311232123311'
expect(heading).not_to be_valid
end
it "Allows longitude inside [-180,180] interval" do
heading = create(:budget_heading, group: group, name: 'Longitude is inside [-180,180] interval')
heading.longitude = '180'
expect(heading).to be_valid
heading.longitude = '-180'
expect(heading).to be_valid
heading.longitude = '-180.000'
expect(heading).to be_valid
heading.longitude = '-180.00000'
expect(heading).to be_valid
heading.longitude = '180.000'
expect(heading).to be_valid
heading.longitude = '180.00000'
expect(heading).to be_valid
heading.longitude = '+75.00000'
expect(heading).to be_valid
heading.longitude = '+15.023321'
expect(heading).to be_valid
heading.longitude = '-80.123451'
expect(heading).to be_valid
heading.longitude = '80.144812'
expect(heading).to be_valid
heading.longitude = '17.417412'
expect(heading).to be_valid
heading.longitude = '-21.000054'
expect(heading).to be_valid
end
end
describe "heading" do
it "can be deleted if no budget's investments associated" do
heading1 = create(:budget_heading, group: group, name: 'name')