From 868c37ef35f2cf14c727ee571a0df868d0044ba8 Mon Sep 17 00:00:00 2001 From: taitus Date: Tue, 5 Feb 2019 18:16:40 +0100 Subject: [PATCH 01/19] Detect new actions for proposals Create new Dashboard::Action class method to detect new actions between yesterday and today for a proposal. - Published proposal: Only detect new actions with "published_proposal" field eq true - Draft proposal: Only detect new actions with "published_proposal" field eq false --- app/models/dashboard/action.rb | 40 ++++++++++++ spec/models/dashboard/action_spec.rb | 91 ++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/app/models/dashboard/action.rb b/app/models/dashboard/action.rb index 357bb1412..2e9a68f88 100644 --- a/app/models/dashboard/action.rb +++ b/app/models/dashboard/action.rb @@ -46,6 +46,9 @@ class Dashboard::Action < ActiveRecord::Base scope :by_proposal, lambda { |proposal| return where(published_proposal: false) if proposal.draft? } + scope :by_published_proposal, lambda { |published| + return where(published_proposal: published) + } def self.active_for(proposal) published_at = proposal.published_at&.to_date || Date.today @@ -85,4 +88,41 @@ class Dashboard::Action < ActiveRecord::Base def self.next_goal_for(proposal) course_for(proposal).first end + + def self.detect_new_actions(proposal) + actions_for_today = get_actions_for_today(proposal) + actions_for_yesterday = get_actions_for_yesterday(proposal) + + new_actions = actions_for_today - actions_for_yesterday + end + + private + def self.get_actions_for_today(proposal) + proposal_votes = proposal.cached_votes_up + day_offset = calculate_day_offset(proposal, Date.today) + + calculate_actions(proposal_votes, day_offset, proposal) + end + + def self.get_actions_for_yesterday(proposal) + proposal_votes = calculate_votes(proposal) + day_offset = calculate_day_offset(proposal, Date.yesterday) + + calculate_actions(proposal_votes, day_offset, proposal) + end + + def self.calculate_day_offset(proposal, date) + start_date = proposal.published? ? proposal.published_at : proposal.created_at + (date - start_date.to_date).to_i + end + + def self.calculate_actions(proposal_votes, day_offset, proposal) + Dashboard::Action.active.where("required_supports <= ?", proposal_votes) + .where("day_offset <= ?", day_offset) + .by_published_proposal(proposal.published?) + end + + def self.calculate_votes(proposal) + Vote.where(votable: proposal).where("created_at <= ?", Date.yesterday).count + end end diff --git a/spec/models/dashboard/action_spec.rb b/spec/models/dashboard/action_spec.rb index 738282992..ca813cd06 100644 --- a/spec/models/dashboard/action_spec.rb +++ b/spec/models/dashboard/action_spec.rb @@ -224,4 +224,95 @@ describe Dashboard::Action do expect(described_class.course_for(proposal)).to include(resource) end end + + context "#detect_new_actions" do + + describe "No detect new actions" do + + let!(:action) { create(:dashboard_action, :proposed_action, :active, day_offset: 1) } + let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 1) } + + it "when there are not news actions actived for published proposals" do + proposal = create(:proposal) + action.update(published_proposal: true) + resource.update(published_proposal: true) + + expect(described_class.detect_new_actions(proposal)).to eq [] + end + + it "when there are news actions actived for draft_proposal but proposal is published" do + proposal = create(:proposal) + action.update(published_proposal: false, day_offset: 0) + resource.update(published_proposal: false, day_offset: 0) + + expect(described_class.detect_new_actions(proposal)).to eq [] + end + + it "when there are not news actions actived for draft proposals" do + proposal = create(:proposal, :draft) + action.update(published_proposal: false) + resource.update(published_proposal: false) + + expect(described_class.detect_new_actions(proposal)).to eq [] + end + + it "when there are news actions actived for published_proposal but proposal is draft" do + proposal = create(:proposal, :draft) + action.update(published_proposal: true, day_offset: 0) + resource.update(published_proposal: true, day_offset: 0) + + expect(described_class.detect_new_actions(proposal)).to eq [] + end + + end + + describe "Detect new actions when there are news actions actived" do + + context "for published proposals" do + + let!(:proposal) { create(:proposal) } + let!(:action) { create(:dashboard_action, :proposed_action, :active, day_offset: 0, published_proposal: true) } + let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: true) } + + it "when proposal has been created today and day_offset is valid only for today" do + expect(described_class.detect_new_actions(proposal)).to include(resource) + expect(described_class.detect_new_actions(proposal)).to include(action) + end + + it "when proposal has received a new vote today" do + proposal.update(created_at: Date.yesterday, published_at: Date.yesterday) + action.update(required_supports: 1) + resource.update(required_supports: 0) + create(:vote, voter: proposal.author, votable: proposal) + + expect(described_class.detect_new_actions(proposal)).to include(action) + expect(described_class.detect_new_actions(proposal)).not_to include(resource) + end + + end + + context "for draft proposals" do + + let!(:proposal) { create(:proposal, :draft) } + let!(:action) { create(:dashboard_action, :proposed_action, :active, day_offset: 0, published_proposal: false) } + let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: false) } + + it "when day_offset field is valid for today and invalid for yesterday" do + expect(described_class.detect_new_actions(proposal)).to include(resource) + expect(described_class.detect_new_actions(proposal)).to include(action) + end + + it "when proposal has received a new vote today" do + proposal.update(created_at: Date.yesterday) + action.update(required_supports: 1) + resource.update(required_supports: 2) + create(:vote, voter: proposal.author, votable: proposal) + + expect(described_class.detect_new_actions(proposal)).to include(action) + expect(described_class.detect_new_actions(proposal)).not_to include(resource) + end + + end + end + end end From c959e078b879d78a9246d14efec49afb7939b90b Mon Sep 17 00:00:00 2001 From: taitus Date: Tue, 5 Feb 2019 18:23:52 +0100 Subject: [PATCH 02/19] Send new action notification email after create Proposal After create a proposal send email to proposal's author with information text and new actions available. --- app/mailers/dashboard/mailer.rb | 5 ++ app/models/proposal.rb | 10 ++++ ...ew_actions_notification_on_create.html.erb | 47 +++++++++++++++++++ config/locales/en/mailers.yml | 15 ++++++ config/locales/es/mailers.yml | 15 ++++++ spec/mailers/dashboard/mailer_spec.rb | 44 +++++++++++++++++ .../previews/dashboard_mailer_preview.rb | 6 +++ spec/models/proposal_spec.rb | 26 ++++++++++ 8 files changed, 168 insertions(+) create mode 100644 app/views/dashboard/mailer/new_actions_notification_on_create.html.erb create mode 100644 spec/mailers/dashboard/mailer_spec.rb diff --git a/app/mailers/dashboard/mailer.rb b/app/mailers/dashboard/mailer.rb index e246fb914..91b0dbd2d 100644 --- a/app/mailers/dashboard/mailer.rb +++ b/app/mailers/dashboard/mailer.rb @@ -5,4 +5,9 @@ class Dashboard::Mailer < ApplicationMailer @proposal = proposal mail to: proposal.author.email, subject: proposal.title end + + def new_actions_notification_on_create(proposal) + @proposal = proposal + mail to: proposal.author.email, subject: I18n.t("mailers.new_actions_notification_on_create.subject") + end end diff --git a/app/models/proposal.rb b/app/models/proposal.rb index e96e49592..00cf8bec9 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -55,6 +55,8 @@ class Proposal < ActiveRecord::Base before_save :calculate_hot_score, :calculate_confidence_score + after_create :send_new_actions_notification_on_create + scope :for_render, -> { includes(:tags) } scope :sort_by_hot_score, -> { reorder(hot_score: :desc) } scope :sort_by_confidence_score, -> { reorder(confidence_score: :desc) } @@ -236,6 +238,14 @@ class Proposal < ActiveRecord::Base Setting["feature.user.skip_verification"].present? end + def send_new_actions_notification_on_create + new_actions = Dashboard::Action.detect_new_actions(self) + + if new_actions.present? + Dashboard::Mailer.new_actions_notification_on_create(self).deliver_later + end + end + protected def set_responsible_name diff --git a/app/views/dashboard/mailer/new_actions_notification_on_create.html.erb b/app/views/dashboard/mailer/new_actions_notification_on_create.html.erb new file mode 100644 index 000000000..b26d8f7f9 --- /dev/null +++ b/app/views/dashboard/mailer/new_actions_notification_on_create.html.erb @@ -0,0 +1,47 @@ + + +

<%= t("mailers.new_actions_notification_on_create.hi", name: @proposal.author.name) %>

+

<%= t("mailers.new_actions_notification_on_create.introduction", title: @proposal.title) %>

+

+ <%= t("mailers.new_actions_notification_on_create.text_1") %> +
+ <%= t("mailers.new_actions_notification_on_create.text_2", link: proposal_dashboard_url(@proposal)).html_safe %> +

+

<%= t("mailers.new_actions_notification_on_create.text_3") %>

+

<%= t("mailers.new_actions_notification_on_create.text_4") %>

+ +
+

+ <%= t("mailers.new_actions_notification_on_create.text_5") %> +
+ <%= t("mailers.new_actions_notification_on_create.text_6") %> +

+ + + + + + + +
+ <%= link_to progress_proposal_dashboard_url(@proposal), + style: "font-family: 'Open Sans',arial,sans-serif; background: #3700fd; + border-radius: 6px; color: #fff !important; font-weight: bold; + padding: 17px 20px; text-align: center; text-decoration: none; + font-size: 20px; min-width: 200px; display: inline-block;", + target: "_blank" do %> + <%= t("mailers.new_actions_notification_on_create.dashboard_button") %> + <% end %> +
+ + diff --git a/config/locales/en/mailers.yml b/config/locales/en/mailers.yml index daf1ef259..253d25214 100644 --- a/config/locales/en/mailers.yml +++ b/config/locales/en/mailers.yml @@ -77,3 +77,18 @@ en: hi: "Dear user," thanks: "Thank you again for participating." sincerely: "Sincererly" + new_actions_notification_on_create: + subject: "Your draft citizen proposal in Decide Madrid is created" + hi: "Hi %{name}!" + introduction: "Your %{title} proposal has been successfully created." + text_1: "Take advantage that your proposal is not public yet and get ready to contact a lot of people." + text_2: "When you are ready publish your citizen proposal from this link." + text_3: "We know that creating a proposal with a hook and getting the necessary support can seem complicated. But dont worry because we are going to help you!" + text_4: "You have a tool that will be your new best ally: The Citizen Proposals panel." + list_1: "Enter every day in the panel of your proposal to use the tips and resources that we will share with you." + list_2: "These tips, actions and resources will give you ideas and also practical solutions to get more support and a wider community. Dont forget them!" + list_3: "As you gain more support, you will unlock new and better resources. At the moment, you have an e-mail template to send massively to all your contacts, a poster to print, among other features and rewards that you will discover. Dont stop adding support and we will not stop rewarding and helping you!" + list_4: "You have %{max_month} months since you publish the proposal to get %{max_votes} support and your proposal can become a reality. But the first days are the most important. It is a challenge. Get ready!" + text_5: "And for you to start with all the motivation," + text_6: "here you have several resources and a whole list of tips that will come to you every day to prepare the broadcast!" + dashboard_button: "Go ahead, discover them!" diff --git a/config/locales/es/mailers.yml b/config/locales/es/mailers.yml index 8f1bd9d2f..ef7dfb7b6 100644 --- a/config/locales/es/mailers.yml +++ b/config/locales/es/mailers.yml @@ -77,3 +77,18 @@ es: hi: "Estimado/a usuario/a" thanks: "Gracias de nuevo por tu participación." sincerely: "Atentamente" + new_actions_notification_on_create: + subject: "Tu borrador de propuesta ciudadana en Decide Madrid está creado" + hi: "Hola %{name}!" + introduction: "Tu propuesta %{title} ha sido creada con éxito." + text_1: "Aprovecha que tu propuesta aún no es pública y prepárate para contactar a mucha gente." + text_2: "Cuando estés preparado/a publica tu propuesta ciudadana desde este enlace." + text_3: "Sabemos que crear una propuesta con gancho y conseguir los apoyos necesarios, puede parecerte complicado. Pero no te preocupes porque ¡vamos a ayudarte!" + text_4: "Tienes una herramienta que será tu nuevo mejor aliado: El panel de Propuestas Ciudadanas." + list_1: "Entra cada día en el panel de tu propuesta para utilizar los consejos y recursos que te iremos compartiendo." + list_2: "Estos consejos, acciones y recursos te darán ideas y también soluciones prácticas para lograr más apoyos y contar con una comunidad más amplia. ¡No te olvides de ellos!" + list_3: "A medida que logres más apoyos, desbloquearás nuevos y mejores recursos. De momento, cuentas con una plantilla de e-mail para enviar masivamente a todos tus contactos, un cartel para imprimir, entre otras funcionalidades y recompensas que irás descubriendo ¡No dejes de sumar apoyos y nosotros no dejaremos de premiarte y ayudarte!" + list_4: "Tienes %{max_month} meses desde que publiques la propuesta para conseguir %{max_votes} apoyos y que tu propuesta pueda hacerse realidad. Pero los primeros días son los más importantes. Es un reto ¡prepárate!" + text_5: "Y para que empieces con toda la motivación," + text_6: "¡aquí tienes varios recursos y toda una lista de consejos que te irán llegando cada día para preparar la difusión!" + dashboard_button: "Adelante, ¡descúbrelos!" diff --git a/spec/mailers/dashboard/mailer_spec.rb b/spec/mailers/dashboard/mailer_spec.rb new file mode 100644 index 000000000..d4d3a660e --- /dev/null +++ b/spec/mailers/dashboard/mailer_spec.rb @@ -0,0 +1,44 @@ +require "rails_helper" + +describe Dashboard::Mailer do + + describe "#new_actions_notification_on_create" do + + before do + ActionMailer::Base.deliveries.clear + end + + let!(:proposal) { build(:proposal, :draft) } + let!(:action) { create(:dashboard_action, :proposed_action, :active, + day_offset: 0, + published_proposal: true) } + let!(:resource) { create(:dashboard_action, :resource, :active, + day_offset: 0, + published_proposal: true) } + + it "sends emails when detect new actions when create a proposal" do + action.update(published_proposal: false) + resource.update(published_proposal: false) + proposal.save + + email = open_last_email + + expect(email).to deliver_from("CONSUL ") + expect(email).to deliver_to(proposal.author) + expect(email).to have_subject("Your draft citizen proposal in Decide Madrid is created") + expect(email).to have_body_text("Hi #{proposal.author.name}!") + expect(email).to have_body_text("Your #{proposal.title} proposal has been successfully created.") + expect(email).to have_body_text("Take advantage that your proposal is not public yet and get ready to contact a lot of people.") + expect(email).to have_body_text(I18n.t("mailers.new_actions_notification_on_create.text_2", link: proposal_dashboard_url(proposal)).html_safe) + expect(email).to have_body_text("We know that creating a proposal with a hook and getting the necessary support can seem complicated. But dont worry because we are going to help you!") + expect(email).to have_body_text("You have a tool that will be your new best ally: The Citizen Proposals panel.") + expect(email).to have_body_text("Enter every day in the panel of your proposal to use the tips and resources that we will share with you.") + expect(email).to have_body_text("These tips, actions and resources will give you ideas and also practical solutions to get more support and a wider community. Dont forget them!") + expect(email).to have_body_text("As you gain more support, you will unlock new and better resources. At the moment, you have an e-mail template to send massively to all your contacts, a poster to print, among other features and rewards that you will discover. Dont stop adding support and we will not stop rewarding and helping you!") + expect(email).to have_body_text("You have #{Setting['months_to_archive_proposals']} months since you publish the proposal to get #{Setting['votes_for_proposal_success']} support and your proposal can become a reality. But the first days are the most important. It is a challenge. Get ready!") + expect(email).to have_body_text("And for you to start with all the motivation,") + expect(email).to have_body_text("here you have several resources and a whole list of tips that will come to you every day to prepare the broadcast!") + expect(email).to have_body_text("Go ahead, discover them!") + end + end +end diff --git a/spec/mailers/previews/dashboard_mailer_preview.rb b/spec/mailers/previews/dashboard_mailer_preview.rb index eded26aef..5e6ce05f4 100644 --- a/spec/mailers/previews/dashboard_mailer_preview.rb +++ b/spec/mailers/previews/dashboard_mailer_preview.rb @@ -3,4 +3,10 @@ class DashboardMailerPreview < ActionMailer::Preview proposal = Proposal.first Dashboard::Mailer.forward(proposal) end + + # http://localhost:3000/rails/mailers/dashboard_mailer/new_actions_notification_on_create + def new_actions_notification_on_create + proposal = Proposal.first + Dashboard::Mailer.new_actions_notification_on_create(proposal) + end end diff --git a/spec/models/proposal_spec.rb b/spec/models/proposal_spec.rb index fa4226764..ca78a9d7f 100644 --- a/spec/models/proposal_spec.rb +++ b/spec/models/proposal_spec.rb @@ -992,4 +992,30 @@ describe Proposal do end + describe "#send_new_actions_notification_on_create" do + + before do + ActionMailer::Base.deliveries.clear + end + + it "send notification after create when there are new actived actions" do + create(:dashboard_action, :proposed_action, :active, day_offset: 0, published_proposal: false) + create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: false) + + create(:proposal, :draft) + + expect(ActionMailer::Base.deliveries.count).to eq(1) + end + + it "Not send notification after create when there are not new actived actions" do + create(:dashboard_action, :proposed_action, :active, day_offset: 1, published_proposal: false) + create(:dashboard_action, :resource, :active, day_offset: 1, published_proposal: false) + + create(:proposal, :draft) + + expect(ActionMailer::Base.deliveries.count).to eq(0) + end + + end + end From 5177adb32ac8a28c6b8bde641797d6cccf068661 Mon Sep 17 00:00:00 2001 From: taitus Date: Tue, 5 Feb 2019 18:31:33 +0100 Subject: [PATCH 03/19] New rake task for proposals to send new action email Execute rake task every day to detect new actions available for not archived proposals. If there are new actions available for today, send email to proposal's author with information text, new actions available and link to proposal dashboard url. --- app/mailers/dashboard/mailer.rb | 17 ++++ app/models/dashboard/action.rb | 5 +- ...actions_notification_rake_created.html.erb | 73 ++++++++++++++++ ...tions_notification_rake_published.html.erb | 74 +++++++++++++++++ config/locales/en/mailers.yml | 24 ++++++ config/locales/es/mailers.yml | 24 ++++++ config/schedule.rb | 4 + lib/tasks/dashboards.rake | 18 ++++ spec/lib/tasks/dashboards_spec.rb | 83 +++++++++++++++++++ spec/mailers/dashboard/mailer_spec.rb | 83 +++++++++++++++++-- .../previews/dashboard_mailer_preview.rb | 14 ++++ spec/models/dashboard/action_spec.rb | 16 ++-- 12 files changed, 420 insertions(+), 15 deletions(-) create mode 100644 app/views/dashboard/mailer/new_actions_notification_rake_created.html.erb create mode 100644 app/views/dashboard/mailer/new_actions_notification_rake_published.html.erb create mode 100644 lib/tasks/dashboards.rake create mode 100644 spec/lib/tasks/dashboards_spec.rb diff --git a/app/mailers/dashboard/mailer.rb b/app/mailers/dashboard/mailer.rb index 91b0dbd2d..e7cbc9a36 100644 --- a/app/mailers/dashboard/mailer.rb +++ b/app/mailers/dashboard/mailer.rb @@ -6,8 +6,25 @@ class Dashboard::Mailer < ApplicationMailer mail to: proposal.author.email, subject: proposal.title end + def new_actions_notification_rake_published(proposal, new_actions_ids) + @proposal = proposal + @new_actions = get_new_actions(new_actions_ids) + mail to: proposal.author.email, subject: I18n.t("mailers.new_actions_notification_rake_published.subject") + end + + def new_actions_notification_rake_created(proposal, new_actions_ids) + @proposal = proposal + @new_actions = get_new_actions(new_actions_ids) + mail to: proposal.author.email, subject: I18n.t("mailers.new_actions_notification_rake_created.subject") + end + def new_actions_notification_on_create(proposal) @proposal = proposal mail to: proposal.author.email, subject: I18n.t("mailers.new_actions_notification_on_create.subject") end + + private + def get_new_actions(new_actions_ids) + Dashboard::Action.where(id: new_actions_ids) + end end diff --git a/app/models/dashboard/action.rb b/app/models/dashboard/action.rb index 2e9a68f88..e88c5a714 100644 --- a/app/models/dashboard/action.rb +++ b/app/models/dashboard/action.rb @@ -93,7 +93,10 @@ class Dashboard::Action < ActiveRecord::Base actions_for_today = get_actions_for_today(proposal) actions_for_yesterday = get_actions_for_yesterday(proposal) - new_actions = actions_for_today - actions_for_yesterday + actions_for_today_ids = actions_for_today.pluck(:id) + actions_for_yesterday_ids = actions_for_yesterday.pluck(:id) + + actions_for_today_ids - actions_for_yesterday_ids end private diff --git a/app/views/dashboard/mailer/new_actions_notification_rake_created.html.erb b/app/views/dashboard/mailer/new_actions_notification_rake_created.html.erb new file mode 100644 index 000000000..ff6f3d817 --- /dev/null +++ b/app/views/dashboard/mailer/new_actions_notification_rake_created.html.erb @@ -0,0 +1,73 @@ + + +

<%= t("mailers.new_actions_notification_rake_created.hi", name: @proposal.author.name) %>

+ +

+ <%= t("mailers.new_actions_notification_rake_created.introduction", + created_at_day: @proposal.created_at.day, + created_at_month: @proposal.created_at.strftime("%B"), + title: @proposal.title) %> +

+

+ <%= t("mailers.new_actions_notification_rake_created.text_1", + link_to_published: link_to(proposal_dashboard_url(@proposal), + proposal_dashboard_url(@proposal))).html_safe %> +

+

<%= t("mailers.new_actions_notification_rake_created.text_2") %>

+
+

<%= t("mailers.new_actions_notification_rake_created.text_3") %>

+ +
+ + <% first_resource = @new_actions.resources.first %> + <% if first_resource.present? %> + <%= t("mailers.new_actions_notification_rake_created.new_resource") %> +
    +
  • <%= first_resource.title %>
  • +
+
+ <% end %> + + <% first_proposed_action = @new_actions.proposed_actions.first %> + <% if first_proposed_action.present? %> +

<%= t("mailers.new_actions_notification_rake_created.text_4") %>

+
    +
  • <%= first_proposed_action.title %>
  • + <% if first_proposed_action.description.present? %> +

    <%= first_proposed_action.description.html_safe %>

    + <% end %> +
+
+ <% end %> + + <% if @new_actions.proposed_actions.count > 1 %> + <% second_proposed_action = @new_actions.proposed_actions.second %> +

<%= t("mailers.new_actions_notification_rake_created.text_5") %>

+
    +
  • <%= second_proposed_action.title %>
  • +
+
+ <% end %> + +

<%= t("mailers.new_actions_notification_rake_created.footer_text") %>

+ +
+ + + + + + +
+ <%= link_to proposal_dashboard_url(@proposal), + style: "font-family: 'Open Sans',arial,sans-serif; background: #3700fd; + border-radius: 6px; color: #fff !important; font-weight: bold; + padding: 17px 20px; text-align: center; text-decoration: none; + font-size: 20px; min-width: 200px; display: inline-block;", + target: "_blank" do %> + <%= t("mailers.new_actions_notification_rake_created.dashboard_button") %> + <% end %> +
+ + diff --git a/app/views/dashboard/mailer/new_actions_notification_rake_published.html.erb b/app/views/dashboard/mailer/new_actions_notification_rake_published.html.erb new file mode 100644 index 000000000..daea3d0a6 --- /dev/null +++ b/app/views/dashboard/mailer/new_actions_notification_rake_published.html.erb @@ -0,0 +1,74 @@ + + +

<%= t("mailers.new_actions_notification_rake_published.hi", name: @proposal.author.name) %>

+

+ <%= t("mailers.new_actions_notification_rake_published.introduction", + published_at_day: @proposal.published_at.day, + published_at_month: @proposal.published_at.strftime("%B"), + title: @proposal.title) %> +

+
+ <% first_resource = @new_actions.resources.first %> + <% if first_resource.present? %> +

<%= t("mailers.new_actions_notification_rake_published.text_1", votes_count: @proposal.cached_votes_up) %>

+
+

<%= t("mailers.new_actions_notification_rake_published.text_2") %>

+
+

<%= t("mailers.new_actions_notification_rake_published.new_resource") %>

+
    +
  • <%= first_resource.title %>
  • +
+
+ <% end %> + + <% limit_to_archive_proposal = @proposal.created_at.to_date + + Setting["months_to_archive_proposals"].to_i.months %> +

+ <%= t("mailers.new_actions_notification_rake_published.text_3", + days_count: (limit_to_archive_proposal - Date.today).to_i, + max_votes_count: Setting["votes_for_proposal_success"]) %> +

+ + <% first_proposed_action = @new_actions.proposed_actions.first %> + <% if first_proposed_action.present? %> +

<%= t("mailers.new_actions_notification_rake_published.text_4") %>

+
    +
  • <%= first_proposed_action.title %>
  • + <% if first_proposed_action.description.present? %> +

    <%= first_proposed_action.description.html_safe %>

    + <% end %> +
+
+ <% end %> + + <% if @new_actions.proposed_actions.count > 1 %> + <% second_proposed_action = @new_actions.proposed_actions.second %> +

<%= t("mailers.new_actions_notification_rake_published.text_5") %>

+
    +
  • <%= second_proposed_action.title %>
  • +
+
+ <% end %> + +

<%= t("mailers.new_actions_notification_rake_published.footer_text") %>

+ +
+ + + + + + +
+ <%= link_to proposal_dashboard_url(@proposal), + style: "font-family: 'Open Sans',arial,sans-serif; background: #3700fd; + border-radius: 6px; color: #fff !important; font-weight: bold; + padding: 17px 20px; text-align: center; text-decoration: none; + font-size: 20px; min-width: 200px; display: inline-block;", + target: "_blank" do %> + <%= t("mailers.new_actions_notification_rake_published.dashboard_button") %> + <% end %> +
+ + diff --git a/config/locales/en/mailers.yml b/config/locales/en/mailers.yml index 253d25214..d2ac7287b 100644 --- a/config/locales/en/mailers.yml +++ b/config/locales/en/mailers.yml @@ -77,6 +77,30 @@ en: hi: "Dear user," thanks: "Thank you again for participating." sincerely: "Sincererly" + new_actions_notification_rake_created: + subject: "More news about your citizen proposal in Decide Madrid" + hi: "Hello %{name}," + new_resource: "NEW UNLOCKED RESOURCE" + introduction: "As you know, on the %{created_at_day} day of the %{created_at_month} you created the proposal in draft mode %{title} on the Decide Madrid platform." + text_1: "Whenever you want you can publish on this link: %{link_to_published}. If you're still not sure..." + text_2: "Seize this moment! Learn, add other people with the same interests and prepare the diffusion that you will need when you publish your proposal definitively." + text_3: "And to accompany you in this challenge, here are the news..." + text_4: "Take a look at this NEW recommended ACTION:" + text_5: "The next action we recommend is:" + footer_text: "As always, enter the Proposals Panel and we will tell you in detail how to use these resources and how to get the most out of it." + dashboard_button: "Go ahead, discover them!" + new_actions_notification_rake_published: + subject: "More news about your citizen proposal in Decide Madrid" + hi: "Hello %{name}," + new_resource: "NEW UNLOCKED RESOURCE" + introduction: "As you know, on the %{published_at_day} day of the %{published_at_month} you published the proposal %{title} on the Decide Madrid platform." + text_1: "Well, you've got %{votes_count} support so far Congratulations!" + text_2: "And so, you have a new resource available to help you keep moving forward." + text_3: "You are missing %{days_count} days before your proposal gets the %{max_votes_count} supports and goes to referendum. Cheer up and keep spreading. Are you short of ideas?" + text_4: "NEW RECOMMENDED DIFFUSION ACTION" + text_5: "There's more yet, see the following action:" + footer_text: "As always, enter the Proposals Panel and we will tell you in detail how to use these resources and how to get the most out of it." + dashboard_button: "Go ahead, discover them!" new_actions_notification_on_create: subject: "Your draft citizen proposal in Decide Madrid is created" hi: "Hi %{name}!" diff --git a/config/locales/es/mailers.yml b/config/locales/es/mailers.yml index ef7dfb7b6..5d26c0ac7 100644 --- a/config/locales/es/mailers.yml +++ b/config/locales/es/mailers.yml @@ -77,6 +77,30 @@ es: hi: "Estimado/a usuario/a" thanks: "Gracias de nuevo por tu participación." sincerely: "Atentamente" + new_actions_notification_rake_created: + subject: "Más novedades de tu propuesta ciudadana en Decide Madrid" + hi: "Hola %{name}," + new_resource: "NUEVO RECURSO DESBLOQUEADO" + introduction: "Como sabes, el día %{created_at_day} del %{created_at_month} creaste la propuesta en modo borrador %{title} en la plataforma Decide Madrid." + text_1: "Cuando quieras puedes publicar en este enlace: %{link_to_published}. Si aún no lo tienes claro…" + text_2: "¡aprovecha este momento! Aprende, suma a otras personas con los mismos intereses y prepara la difusión que necesitarás al publicar tu propuesta definitivamente." + text_3: "Y para acompañarte en este reto, aquí van las novedades..." + text_4: "Échale un vistazo a esta NUEVA ACCIÓN recomendada:" + text_5: "La siguiente acción que te recomendamos es:" + footer_text: "Como siempre, entra en el Panel de propuestas y te contamos detalladamente cómo utilizar estos recursos y cómo sacarle el máximo partido." + dashboard_button: "Adelante, ¡descúbrelos!" + new_actions_notification_rake_published: + subject: "Más novedades de tu propuesta ciudadana en Decide Madrid" + hi: "Hola %{name}," + new_resource: "NUEVO RECURSO DESBLOQUEADO" + introduction: "Como sabes, el día %{published_at_day} del %{published_at_month} publicaste la propuesta %{title} en la plataforma Decide Madrid." + text_1: "Pues bien, has conseguido %{votes_count} apoyos hasta este momento ¡Felicidades!" + text_2: "Y por eso, tienes disponible un nuevo recurso para ayudarte a seguir avanzando." + text_3: "Te faltan %{days_count} días para que tu propuesta consiga los %{max_votes_count} apoyos y que vaya a referéndum. Ánimo y sigue difundiendo. ¿Te faltan ideas?" + text_4: "NUEVA ACCIÓN DE DIFUSIÓN RECOMENDADA" + text_5: "Aún hay más, consulta la siguiente acción:" + footer_text: "Como siempre, entra en el Panel de propuestas y te contamos detalladamente cómo utilizar estos recursos y cómo sacarle el máximo partido." + dashboard_button: "Adelante, ¡descúbrelos!" new_actions_notification_on_create: subject: "Tu borrador de propuesta ciudadana en Decide Madrid está creado" hi: "Hola %{name}!" diff --git a/config/schedule.rb b/config/schedule.rb index f4c03fd61..16420a765 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -26,3 +26,7 @@ end every 1.day, at: '5:00 am' do rake "-s sitemap:refresh" end + +every 1.day, at: '7:00 am' do + rake "dashboards:send_notifications" +end diff --git a/lib/tasks/dashboards.rake b/lib/tasks/dashboards.rake new file mode 100644 index 000000000..44a3954f8 --- /dev/null +++ b/lib/tasks/dashboards.rake @@ -0,0 +1,18 @@ +namespace :dashboards do + + desc "Send to user notifications from new actions availability on dashboard" + task send_notifications: :environment do + Proposal.not_archived.each do |proposal| + new_actions_ids = Dashboard::Action.detect_new_actions(proposal) + + if new_actions_ids.present? + if proposal.published? + Dashboard::Mailer.new_actions_notification_rake_published(proposal, new_actions_ids).deliver_later + else + Dashboard::Mailer.new_actions_notification_rake_created(proposal, new_actions_ids).deliver_later + end + end + end + end + +end diff --git a/spec/lib/tasks/dashboards_spec.rb b/spec/lib/tasks/dashboards_spec.rb new file mode 100644 index 000000000..434f2b249 --- /dev/null +++ b/spec/lib/tasks/dashboards_spec.rb @@ -0,0 +1,83 @@ +require "rails_helper" +require "rake" + +describe "Dashboards Rake" do + + describe "#send_notifications" do + + before do + Rake.application.rake_require "tasks/dashboards" + Rake::Task.define_task(:environment) + ActionMailer::Base.deliveries.clear + end + + let :run_rake_task do + Rake::Task["dashboards:send_notifications"].reenable + Rake.application.invoke_task "dashboards:send_notifications" + end + + describe "Not send notifications to proposal author" do + + let!(:action) { create(:dashboard_action, :proposed_action, :active, day_offset: 1) } + let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 1) } + + it "when there are not news actions actived for published proposals" do + create(:proposal) + action.update(published_proposal: true) + resource.update(published_proposal: true) + + expect {run_rake_task}.to change { ActionMailer::Base.deliveries.count }.by(0) + end + + it "when there are not news actions actived for draft proposals" do + create(:proposal, :draft) + action.update(published_proposal: false) + resource.update(published_proposal: false) + + expect {run_rake_task}.to change { ActionMailer::Base.deliveries.count }.by(0) + end + + it "when there are news actions actived for archived proposals" do + create(:proposal, :archived) + action.update(day_offset: 0, published_proposal: true) + resource.update(day_offset: 0, published_proposal: true) + + expect {run_rake_task}.to change { ActionMailer::Base.deliveries.count }.by(0) + end + + end + + describe "Send notifications to proposal author" do + let!(:action) { create(:dashboard_action, :proposed_action, :active, day_offset: 0) } + let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 0) } + + it " when there are news actions actived for published proposals" do + proposal = create(:proposal) + action.update(published_proposal: true) + resource.update(published_proposal: true) + + run_rake_task + email = open_last_email + + expect(email).to deliver_from("CONSUL ") + expect(email).to deliver_to(proposal.author) + expect(email).to have_subject("More news about your citizen proposal in Decide Madrid") + end + + it "when there are news actions actived for draft proposals" do + proposal = create(:proposal, :draft) + action.update(published_proposal: false) + resource.update(published_proposal: false) + + run_rake_task + email = open_last_email + + expect(email).to deliver_from("CONSUL ") + expect(email).to deliver_to(proposal.author) + expect(email).to have_subject("More news about your citizen proposal in Decide Madrid") + end + end + + end + +end diff --git a/spec/mailers/dashboard/mailer_spec.rb b/spec/mailers/dashboard/mailer_spec.rb index d4d3a660e..d407138a0 100644 --- a/spec/mailers/dashboard/mailer_spec.rb +++ b/spec/mailers/dashboard/mailer_spec.rb @@ -2,6 +2,83 @@ require "rails_helper" describe Dashboard::Mailer do + let!(:action) { create(:dashboard_action, :proposed_action, :active, + day_offset: 0, + published_proposal: true) } + let!(:resource) { create(:dashboard_action, :resource, :active, + day_offset: 0, + published_proposal: true) } + + describe "#new_actions_notification rake task" do + + before do + Rake.application.rake_require "tasks/dashboards" + Rake::Task.define_task(:environment) + ActionMailer::Base.deliveries.clear + end + + let :run_rake_task do + Rake::Task["dashboards:send_notifications"].reenable + Rake.application.invoke_task "dashboards:send_notifications" + end + + describe "#new_actions_notification_rake_created" do + let!(:proposal) { create(:proposal, :draft) } + + it "sends emails when detect new actions for draft proposal" do + action.update(published_proposal: false) + resource.update(published_proposal: false) + run_rake_task + + email = open_last_email + + expect(email).to deliver_from("CONSUL ") + expect(email).to deliver_to(proposal.author) + expect(email).to have_subject("More news about your citizen proposal in Decide Madrid") + expect(email).to have_body_text("Hello #{proposal.author.name},") + expect(email).to have_body_text("As you know, on the #{proposal.created_at.day} day of the #{proposal.created_at.strftime("%B")} you created the proposal in draft mode #{proposal.title} on the Decide Madrid platform.") + expect(email).to have_body_text("Whenever you want you can publish on this link:") + expect(email).to have_body_text("Seize this moment! Learn, add other people with the same interests and prepare the diffusion that you will need when you publish your proposal definitively.") + expect(email).to have_body_text("And to accompany you in this challenge, here are the news...") + expect(email).to have_body_text("NEW UNLOCKED RESOURCE") + expect(email).to have_body_text("#{resource.title}") + expect(email).to have_body_text("Take a look at this NEW recommended ACTION:") + expect(email).to have_body_text("#{action.title}") + expect(email).to have_body_text("#{action.description}") + expect(email).to have_body_text("As always, enter the Proposals Panel and we will tell you in detail how to use these resources and how to get the most out of it.") + expect(email).to have_body_text("Go ahead, discover them!") + end + end + + describe "#new_actions_notification_rake_published" do + let!(:proposal) { create(:proposal) } + + it "sends emails when detect new actions for proposal" do + run_rake_task + + email = open_last_email + + expect(email).to deliver_from("CONSUL ") + expect(email).to deliver_to(proposal.author) + expect(email).to have_subject("More news about your citizen proposal in Decide Madrid") + expect(email).to have_body_text("Hello #{proposal.author.name},") + expect(email).to have_body_text("As you know, on the #{proposal.published_at.day} day of the #{proposal.published_at.strftime("%B")} you published the proposal #{proposal.title} on the Decide Madrid platform.") + expect(email).to have_body_text("And so, you have a new resource available to help you keep moving forward.") + expect(email).to have_body_text("NEW UNLOCKED RESOURCE") + expect(email).to have_body_text("#{resource.title}") + + limit_to_archive_proposal = proposal.created_at.to_date + Setting["months_to_archive_proposals"].to_i.months + days_count = (limit_to_archive_proposal - Date.today).to_i + expect(email).to have_body_text("You are missing #{days_count} days before your proposal gets the #{Setting["votes_for_proposal_success"]} supports and goes to referendum. Cheer up and keep spreading. Are you short of ideas?") + expect(email).to have_body_text("NEW RECOMMENDED DIFFUSION ACTION") + expect(email).to have_body_text("#{action.title}") + expect(email).to have_body_text("#{action.description}") + expect(email).to have_body_text("As always, enter the Proposals Panel and we will tell you in detail how to use these resources and how to get the most out of it.") + expect(email).to have_body_text("Go ahead, discover them!") + end + end + end + describe "#new_actions_notification_on_create" do before do @@ -9,12 +86,6 @@ describe Dashboard::Mailer do end let!(:proposal) { build(:proposal, :draft) } - let!(:action) { create(:dashboard_action, :proposed_action, :active, - day_offset: 0, - published_proposal: true) } - let!(:resource) { create(:dashboard_action, :resource, :active, - day_offset: 0, - published_proposal: true) } it "sends emails when detect new actions when create a proposal" do action.update(published_proposal: false) diff --git a/spec/mailers/previews/dashboard_mailer_preview.rb b/spec/mailers/previews/dashboard_mailer_preview.rb index 5e6ce05f4..8cf2f99c3 100644 --- a/spec/mailers/previews/dashboard_mailer_preview.rb +++ b/spec/mailers/previews/dashboard_mailer_preview.rb @@ -4,6 +4,20 @@ class DashboardMailerPreview < ActionMailer::Preview Dashboard::Mailer.forward(proposal) end + # http://localhost:3000/rails/mailers/dashboard_mailer/new_actions_notification_rake_published + def new_actions_notification_rake_published + proposal = Proposal.where(published: true).first + new_actions_ids = Dashboard::Action.limit(1).id + Dashboard::Mailer.new_actions_notification(proposal, [new_actions_ids]) + end + + # http://localhost:3000/rails/mailers/dashboard_mailer/new_actions_notification_rake_created + def new_actions_notification_rake_created + proposal = Proposal.where(published: false).first + new_actions_ids = Dashboard::Action.limit(1).id + Dashboard::Mailer.new_actions_notification(proposal, [new_actions_ids]) + end + # http://localhost:3000/rails/mailers/dashboard_mailer/new_actions_notification_on_create def new_actions_notification_on_create proposal = Proposal.first diff --git a/spec/models/dashboard/action_spec.rb b/spec/models/dashboard/action_spec.rb index ca813cd06..bf9a85110 100644 --- a/spec/models/dashboard/action_spec.rb +++ b/spec/models/dashboard/action_spec.rb @@ -275,8 +275,8 @@ describe Dashboard::Action do let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: true) } it "when proposal has been created today and day_offset is valid only for today" do - expect(described_class.detect_new_actions(proposal)).to include(resource) - expect(described_class.detect_new_actions(proposal)).to include(action) + expect(described_class.detect_new_actions(proposal)).to include(resource.id) + expect(described_class.detect_new_actions(proposal)).to include(action.id) end it "when proposal has received a new vote today" do @@ -285,8 +285,8 @@ describe Dashboard::Action do resource.update(required_supports: 0) create(:vote, voter: proposal.author, votable: proposal) - expect(described_class.detect_new_actions(proposal)).to include(action) - expect(described_class.detect_new_actions(proposal)).not_to include(resource) + expect(described_class.detect_new_actions(proposal)).to include(action.id) + expect(described_class.detect_new_actions(proposal)).not_to include(resource.id) end end @@ -298,8 +298,8 @@ describe Dashboard::Action do let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: false) } it "when day_offset field is valid for today and invalid for yesterday" do - expect(described_class.detect_new_actions(proposal)).to include(resource) - expect(described_class.detect_new_actions(proposal)).to include(action) + expect(described_class.detect_new_actions(proposal)).to include(resource.id) + expect(described_class.detect_new_actions(proposal)).to include(action.id) end it "when proposal has received a new vote today" do @@ -308,8 +308,8 @@ describe Dashboard::Action do resource.update(required_supports: 2) create(:vote, voter: proposal.author, votable: proposal) - expect(described_class.detect_new_actions(proposal)).to include(action) - expect(described_class.detect_new_actions(proposal)).not_to include(resource) + expect(described_class.detect_new_actions(proposal)).to include(action.id) + expect(described_class.detect_new_actions(proposal)).not_to include(resource.id) end end From 08383b9cac174bb82407434443a8c8e806051894 Mon Sep 17 00:00:00 2001 From: taitus Date: Mon, 11 Feb 2019 14:04:22 +0100 Subject: [PATCH 04/19] Send new notification email after publish Proposal After publish a proposal send email to proposal's author with information text and new actions available. --- app/mailers/dashboard/mailer.rb | 8 ++- app/models/proposal.rb | 9 +++ ...actions_notification_on_published.html.erb | 64 +++++++++++++++++++ config/locales/en/mailers.yml | 16 +++++ config/locales/es/mailers.yml | 16 +++++ spec/mailers/dashboard/mailer_spec.rb | 36 +++++++++++ .../previews/dashboard_mailer_preview.rb | 7 ++ spec/models/proposal_spec.rb | 28 ++++++++ 8 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 app/views/dashboard/mailer/new_actions_notification_on_published.html.erb diff --git a/app/mailers/dashboard/mailer.rb b/app/mailers/dashboard/mailer.rb index e7cbc9a36..f60119a50 100644 --- a/app/mailers/dashboard/mailer.rb +++ b/app/mailers/dashboard/mailer.rb @@ -23,8 +23,14 @@ class Dashboard::Mailer < ApplicationMailer mail to: proposal.author.email, subject: I18n.t("mailers.new_actions_notification_on_create.subject") end + def new_actions_notification_on_published(proposal, new_actions_ids) + @proposal = proposal + @new_actions = get_new_actions(new_actions_ids) + mail to: proposal.author.email, subject: I18n.t("mailers.new_actions_notification_on_published.subject") + end + private def get_new_actions(new_actions_ids) Dashboard::Action.where(id: new_actions_ids) - end + end end diff --git a/app/models/proposal.rb b/app/models/proposal.rb index 00cf8bec9..07723ea97 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -86,6 +86,7 @@ class Proposal < ActiveRecord::Base def publish update(published_at: Time.now) + send_new_actions_notification_on_published end def published? @@ -246,6 +247,14 @@ class Proposal < ActiveRecord::Base end end + def send_new_actions_notification_on_published + new_actions_ids = Dashboard::Action.detect_new_actions(self) + + if new_actions_ids.present? + Dashboard::Mailer.new_actions_notification_on_published(self, new_actions_ids).deliver_later + end + end + protected def set_responsible_name diff --git a/app/views/dashboard/mailer/new_actions_notification_on_published.html.erb b/app/views/dashboard/mailer/new_actions_notification_on_published.html.erb new file mode 100644 index 000000000..bae02cd64 --- /dev/null +++ b/app/views/dashboard/mailer/new_actions_notification_on_published.html.erb @@ -0,0 +1,64 @@ + + +

+ <%= t("mailers.new_actions_notification_on_published.hi", + name: @proposal.author.name, + title: @proposal.title) %> +

+

+ <%= t("mailers.new_actions_notification_on_published.text_1") %> +
+ <%= t("mailers.new_actions_notification_on_published.text_2") %> +

+

<%= t("mailers.new_actions_notification_on_published.text_3") %>

+

<%= t("mailers.new_actions_notification_on_published.text_4") %>

+

<%= t("mailers.new_actions_notification_on_published.text_5") %>

+

<%= t("mailers.new_actions_notification_on_published.text_6") %>

+
    +
  • <%= t("mailers.new_actions_notification_on_published.list_1") %>
  • +
  • <%= t("mailers.new_actions_notification_on_published.list_2") %>
  • +
+
+ +

<%= t("mailers.new_actions_notification_on_published.text_7") %>

+ <% first_resource = @new_actions.resources.first %> + <% if first_resource.present? %> +

<%= t("mailers.new_actions_notification_on_published.text_8") %>

+
    +
  • <%= first_resource.title %>
  • +
+ <% end %> + + <% first_proposed_action = @new_actions.proposed_actions.first %> + <% if first_proposed_action.present? %> +

<%= t("mailers.new_actions_notification_on_published.text_9") %>

+
    +
  • <%= first_proposed_action.title %>
  • + <% if first_proposed_action.short_description.present? %> +

    <%= first_proposed_action.short_description.html_safe %>

    + <% end %> +
+ <% end %> +
+ +

<%= t("mailers.new_actions_notification_on_published.text_10") %>

+ + + + + + + +
+ <%= link_to progress_proposal_dashboard_url(@proposal), + style: "font-family: 'Open Sans',arial,sans-serif; background: #3700fd; + border-radius: 6px; color: #fff !important; font-weight: bold; + padding: 17px 20px; text-align: center; text-decoration: none; + font-size: 20px; min-width: 200px; display: inline-block;", + target: "_blank" do %> + <%= t("mailers.new_actions_notification_on_published.dashboard_button") %> + <% end %> +
+ + diff --git a/config/locales/en/mailers.yml b/config/locales/en/mailers.yml index d2ac7287b..e2239c651 100644 --- a/config/locales/en/mailers.yml +++ b/config/locales/en/mailers.yml @@ -116,3 +116,19 @@ en: text_5: "And for you to start with all the motivation," text_6: "here you have several resources and a whole list of tips that will come to you every day to prepare the broadcast!" dashboard_button: "Go ahead, discover them!" + new_actions_notification_on_published: + subject: "Your citizen proposal is already published Dont stop spreading!" + hi: "Congratulations %{name}! Your proposal %{title} has been created successfully." + text_1: "And now, go for your first 100 supports!" + text_2: "Why 100?" + text_3: "Our experience tells us that the first day is fundamental. Because in addition to having the energy to launch something new, being a newly published proposal, you will have the important visibility of being among the new proposals highlighted in Decide Madrid." + text_4: "Get 100 supports on the first day, and you will have a first community to back you up." + text_5: "That is why we challenge you to get it, but not without a lot of help!" + text_6: "Remember that in your Proposal Panel you have new resources available and recommendations for dissemination actions." + list_1: "Come in every day to see your progress and use the tips and resources we will share with you. They are ideas and also practical solutions to get the support you need." + list_2: "As you get more support, you will unlock new and better resources. Do not stop adding support and we will not stop rewarding and helping you!" + text_7: "And for you to start at full speed..." + text_8: "Here is a great resource at your disposal!" + text_9: "You will also find this new recommended dissemination action..." + text_10: "You sure have more resources to use!" + dashboard_button: "Go ahead, discover them!" diff --git a/config/locales/es/mailers.yml b/config/locales/es/mailers.yml index 5d26c0ac7..c5de488ab 100644 --- a/config/locales/es/mailers.yml +++ b/config/locales/es/mailers.yml @@ -116,3 +116,19 @@ es: text_5: "Y para que empieces con toda la motivación," text_6: "¡aquí tienes varios recursos y toda una lista de consejos que te irán llegando cada día para preparar la difusión!" dashboard_button: "Adelante, ¡descúbrelos!" + new_actions_notification_on_published: + subject: "Tu propuesta ciudadana ya está publicada ¡No pares de difundir!" + hi: "¡Enhorabuena %{name}! Tu propuesta %{title} ha sido creado con éxito." + text_1: "Y ahora, ¡a por tus primeros 100 apoyos! " + text_2: "¿Por qué 100?" + text_3: "Nuestra experiencia nos dice que el primer día es fundamental. Porque además de tener la energía de lanzar algo nuevo, al ser una propuesta recién publicada, contarás con la importante visibilidad de estar entre las nuevas propuestas destacadas en Decide Madrid." + text_4: "Logra 100 apoyos en el primer día, y tendrás una primera comunidad en la que respaldarte." + text_5: "Por eso, te retamos a que lo consigas, ¡pero no sin una gran ayuda!" + text_6: "Recuerda que en tu Panel de propuestas tienes nuevos recursos disponibles y recomendaciones de acciones de difusión." + list_1: "Entra cada día para ver tu progreso y utilizar los consejos y recursos que te iremos compartiendo. Son ideas y también soluciones prácticas para lograr los apoyos que necesitas." + list_2: "A medida que logres más apoyos, desbloquearás nuevos y mejores recursos. ¡No dejes de sumar apoyos y nosotros no dejaremos de premiarte y ayudarte!" + text_7: "Y para que empieces a toda máquina..." + text_8: "¡aquí tienes un gran recurso a tu total disposición!" + text_9: "También encontrarás esta nueva acción de difusión recomendada…" + text_10: "¡Seguro que tienes más recursos pendientes de usar!" + dashboard_button: "Adelante, ¡descúbrelos!" diff --git a/spec/mailers/dashboard/mailer_spec.rb b/spec/mailers/dashboard/mailer_spec.rb index d407138a0..3f43be4b2 100644 --- a/spec/mailers/dashboard/mailer_spec.rb +++ b/spec/mailers/dashboard/mailer_spec.rb @@ -112,4 +112,40 @@ describe Dashboard::Mailer do expect(email).to have_body_text("Go ahead, discover them!") end end + + describe "#new_actions_notification_on_published" do + + before do + ActionMailer::Base.deliveries.clear + end + + let!(:proposal) { build(:proposal, :draft) } + let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: true) } + let!(:proposed_action) { create(:dashboard_action, :proposed_action, :active, day_offset: 0, published_proposal: true) } + + it "sends emails when detect new actions when publish a proposal" do + proposal.save + proposal.publish + + email = open_last_email + + expect(email).to deliver_from("CONSUL ") + expect(email).to deliver_to(proposal.author) + expect(email).to have_subject("Your citizen proposal is already published Dont stop spreading!") + expect(email).to have_body_text("Congratulations #{proposal.author.name}! Your proposal #{proposal.title} has been created successfully.") + expect(email).to have_body_text("And now, go for your first 100 supports!") + expect(email).to have_body_text("Why 100?") + expect(email).to have_body_text("Our experience tells us that the first day is fundamental. Because in addition to having the energy to launch something new, being a newly published proposal, you will have the important visibility of being among the new proposals highlighted in Decide Madrid.") + expect(email).to have_body_text("Get 100 supports on the first day, and you will have a first community to back you up.") + expect(email).to have_body_text("That is why we challenge you to get it, but not without a lot of help!") + expect(email).to have_body_text("Remember that in your Proposal Panel you have new resources available and recommendations for dissemination actions.") + expect(email).to have_body_text("Come in every day to see your progress and use the tips and resources we will share with you. They are ideas and also practical solutions to get the support you need.") + expect(email).to have_body_text("As you get more support, you will unlock new and better resources. Do not stop adding support and we will not stop rewarding and helping you!") + expect(email).to have_body_text("And for you to start at full speed...") + expect(email).to have_body_text("Here is a great resource at your disposal!") + expect(email).to have_body_text("You will also find this new recommended dissemination action...") + expect(email).to have_body_text("You sure have more resources to use!") + expect(email).to have_body_text("Go ahead, discover them!") + end + end end diff --git a/spec/mailers/previews/dashboard_mailer_preview.rb b/spec/mailers/previews/dashboard_mailer_preview.rb index 8cf2f99c3..38d5f4942 100644 --- a/spec/mailers/previews/dashboard_mailer_preview.rb +++ b/spec/mailers/previews/dashboard_mailer_preview.rb @@ -23,4 +23,11 @@ class DashboardMailerPreview < ActionMailer::Preview proposal = Proposal.first Dashboard::Mailer.new_actions_notification_on_create(proposal) end + + # http://localhost:3000/rails/mailers/dashboard_mailer/new_actions_notification_on_published + def new_actions_notification_on_published + proposal = Proposal.first + new_actions = Dashboard::Action.all + Dashboard::Mailer.new_actions_notification_on_published(proposal, new_actions) + end end diff --git a/spec/models/proposal_spec.rb b/spec/models/proposal_spec.rb index ca78a9d7f..4cac95081 100644 --- a/spec/models/proposal_spec.rb +++ b/spec/models/proposal_spec.rb @@ -1018,4 +1018,32 @@ describe Proposal do end + describe "#send_new_actions_notification_on_published" do + + before do + ActionMailer::Base.deliveries.clear + end + + it "send notification after published when there are new actived actions" do + create(:dashboard_action, :proposed_action, :active, day_offset: 0, published_proposal: true) + create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: true) + + proposal = create(:proposal, :draft) + proposal.publish + + expect(ActionMailer::Base.deliveries.count).to eq(1) + end + + it "Not send notification after published when there are not new actived actions" do + create(:dashboard_action, :proposed_action, :active, day_offset: 1, published_proposal: true) + create(:dashboard_action, :resource, :active, day_offset: 1, published_proposal: true) + + proposal = create(:proposal, :draft) + proposal.publish + + expect(ActionMailer::Base.deliveries.count).to eq(0) + end + + end + end From b115c3af493b1aa09c52794ebe020ace51d11607 Mon Sep 17 00:00:00 2001 From: taitus Date: Wed, 13 Mar 2019 20:43:55 +0100 Subject: [PATCH 05/19] Rake task for create basic templates with actions and resources by default. --- lib/tasks/dashboards.rake | 344 ++++++++++++++++++++++++++++++ spec/lib/tasks/dashboards_spec.rb | 23 ++ 2 files changed, 367 insertions(+) diff --git a/lib/tasks/dashboards.rake b/lib/tasks/dashboards.rake index 44a3954f8..8ec07f1f9 100644 --- a/lib/tasks/dashboards.rake +++ b/lib/tasks/dashboards.rake @@ -15,4 +15,348 @@ namespace :dashboards do end end + desc "Basic templates with Dashboard::Actions recommended" + task create_basic_dashboard_actions_template: :environment do + Dashboard::Action.create(title: "Kit de difusión", + description: "

Aquí tienes un manual para ayudarte a comunicar tu propuesta y que tengas el mayor éxito posible. Es fundamental que estés detrás de ella impulsándola. Este documento te ayudará a tener una estrategia en tu comunicación. Puedes descargártelo en pdf, como también leerlo online.

\r\n", + request_to_administrators: false, + day_offset: 1, + required_supports: 0, + order: 1, + active: true, + action_type: 1, + short_description: "Manual para diseñar tu estrategia de comunicación", + published_proposal: false) + Dashboard::Action.create(title: "Habla primero con familiares y amigos", + description: "

Cuéntales tu propuesta, pídeles consejo y, una vez la hayas publicado, pídeles que la compartan en sus redes sociales. Ellos serán los primeros en apoyar tu campaña.

\r\n", + request_to_administrators: false, + day_offset: 0, + required_supports: 0, + order: 2, + active: true, + action_type: 0, + short_description: "Ellos serán tu primer y más importante apoyo", + published_proposal: false) + Dashboard::Action.create(title: "Haz que tu campaña tenga la mejor imagen", + description: "

¡Añadir una fotografía o vídeo a tu propuesta consigue hasta 6 veces más apoyos que las propuestas que no la tienen! Es fundamental que escojas la mejor fotografía posible y, si las personas implicadas en la propuesta aparecen en ella, ¡mucho mejor! Además, aquí te dejamos algunos consejos más técnicos para que apliques a la elección de la imagen de tu propuesta. Síguelos y verás el resultado:
\r\n- Fotos de animales y personas, siempre funcionan mejor.
\r\n- Las fotos grandes siempre quedan mejor, pero ¡ojo, la imagen no puede exceder 1mb de peso máximo!
\r\n- Haz que tu foto sea apta para todos los públicos y no contenga contenido explícito.

\r\n", + request_to_administrators: false, + day_offset: 0, + required_supports: 0, + order: 0, + active: true, + action_type: 0, + short_description: "", + published_proposal: false) + Dashboard::Action.create(title: "Elige un título corto, potente y llamativo", + description: "

Es importante ir al grano. Haz partícipes a todos de tu propuesta. Céntrate en la solución, en el beneficio o en aquello que se necesita resolver. Ubica tu propuesta ciudadana. Aquí te dejamos algunos ejemplos para que adaptes al título de tu propuesta:
\r\n- 'Queremos una Plaza de Olavide limpia y habitable'
\r\n- 'No al cierre del mercado de abastos de Leganés'
\r\n- 'No más basura en nuestro barrio de La Latina'
\r\n- 'Colocar más contenedores de vidrio en Argüelles'
\r\n- 'Arreglen la estación para bicicletas de Legazpi'

\r\n", + request_to_administrators: false, + day_offset: 0, + required_supports: 0, + order: 1, + active: true, + action_type: 0, + short_description: "Sé conciso y directo para que tu propuesta se entienda al instante", + published_proposal: false) + Dashboard::Action.create(title: "Expresa siempre tu agradecimiento", + description: "

Tanto si te brindan su apoyo como si no, da las gracias siempre. Además de mostrar tu agradecimiento, servirá para crear una comunicación nueva que podría atraer a otras personas hasta tu propuesta y de este modo, conseguir nuevos apoyos.

\r\n", + request_to_administrators: false, + day_offset: 3, + required_supports: 0, + order: 5, + active: true, + action_type: 0, + short_description: "Es una buena manera de lograr apoyos en el futuro", + published_proposal: false) + Dashboard::Action.create(title: "Cuéntaselo a tus amigos en persona", + description: "

Antes de publicar, haz un evento o plan con tu gente, amigos, familiares, compañeros de trabajo... Reúne a todo el mundo alrededor de unas cervecitas y una buena conversación, cuéntales tu propuesta e invítales a que la mejoren y participen de ella. Esta idea es fácilmente combinable con la creación de tus encuestas previas a la publicación de la propuesta. Puedes desarrollar las encuestas para estos encuentros y debatir las conclusiones; o bien que las respondan in situ, lo que prefieras.

\r\n", + request_to_administrators: false, + day_offset: 4, + required_supports: 0, + order: 6, + active: true, + action_type: 0, + short_description: "Crea un encuentro para compartir tu propuesta.", + published_proposal: false) + Dashboard::Action.create(title: "Crea una encuesta personalizada", + description: "

Las encuestas sirven para resolver dudas, pedir opinión, mejorar tu propuesta y también para crear una comunidad en torno a la cual hacer crecer tu propuesta en apoyos, una vez la hayas afinado y mejorado y decidas hacerla pública para todos ellos.
\r\n
\r\nEsta idea es fácilmente combinable con la organización de un encuentro o evento en el que les cuentes a todos tus contactos, amigos y familiares cuál es tu propuesta y el por qué de la importancia de su apoyo para lograr tus objetivos.

\r\n", + request_to_administrators: false, + day_offset: 5, + required_supports: 0, + order: 7, + active: true, + action_type: 0, + short_description: "Pregunta lo que quieras sobre tu propuesta", + published_proposal: false) + Dashboard::Action.create(title: "Suma en tu propósito a los negocios de tu barrio", + description: "

Quizá tu propuesta influya directamente en los negocios de tu barrio e incluso pueda ayudar a mejorar su situación y tú, seguro que conoces a esas personas que los dirigen o regentan, ¿verdad? Contacta con ellos y cuéntales todo para que te ayuden a lograr votos. ¡Ellos, como tú, también ganarán!

\r\n", + request_to_administrators: false, + day_offset: 7, + required_supports: 0, + order: 10, + active: true, + action_type: 0, + short_description: "¿Has pensado en hablar con los bares o comercios que más visitas?", + published_proposal: false) + Dashboard::Action.create(title: "Incluye hashtags en tus publicaciones", + description: "

El hashtag es una herramienta indispensable para aumentar la participación y tus apoyos. Se utiliza principalmente en Twitter, pero Facebook, Instagram, Pinterest o Google+ también incorporan esta opción. Es muy recomendable que al publicar, siempre utilices los mismos hashtags en tus publicaciones para, sobre ellos, crear tu propio contenido. Haz una búsqueda o pide ayuda sobre cuáles utilizar y que tengan que ver con tu propuesta en concreto. Nosotros te dejamos aquí algunos ejemplos o ideas de hashtags que tienen que ver con diversos ámbitos sociales sobre los que podría ir tu propuesta. Pero lo ideal es que utilices otros más acordes a la temática de la tuya propia.
\r\n- #madrid_detodos #madridtoday #lovemadrid #madridlimpio #madridsinfiltros #madridverde #lavapiesenpositivo
\r\n

\r\n", + request_to_administrators: false, + day_offset: 6, + required_supports: 0, + order: 9, + active: true, + action_type: 0, + short_description: "Utilizar hashtags te permitirá llegar a más gente", + published_proposal: false) + Dashboard::Action.create(title: "Suma en tu propósito a ONGs o centros sociales de tu barrio", + description: "

Quizá tu propuesta influya directamente en la mejora de la calidad de vida de las personas que vivie en tu barrio o localidad, incluso aquellas con menos recursos y los colectivos más desfavorecidos o en riesgo de exclusión social. Contacta con las ONG's y Centros Sociales cercanos a ti y cuéntales tu propuesta para que te ayuden a lograr votos. ¡Estarán encantados de colaborar!

\r\n", + request_to_administrators: false, + day_offset: 7, + required_supports: 0, + order: 11, + active: true, + action_type: 0, + short_description: "Cuéntales tu propuesta a todos los que pueda interesarles", + published_proposal: false) + Dashboard::Action.create(title: "¿Conoces a algún influencer?", + description: "

Un 'influencer' es una persona que cuenta con numerosos seguidores en redes sociales. Por ello, si en tu entorno cercano conoces a alguien que lo sea o que utilice activamente sus perfiles sociales y obtenga mucha o cierta repercusión en ellos, es una gran oportunidad para contarle acerca de tu propuesta y pedirle que te ayude a difundirla compartiéndola en sus redes sociales.

\r\n", + request_to_administrators: false, + day_offset: 8, + required_supports: 0, + order: 12, + active: true, + action_type: 0, + short_description: "Cuéntale tu propuesta y consigue que él y sus seguidores te apoyen", + published_proposal: false) + Dashboard::Action.create(title: "Pide apoyos en persona", + description: "

Aunque tenemos medios a nuestro alcance capaces de llegar en un instante a cientos y miles de personas, nada funciona mejor que pedir el apoyo en persona. ¡Es 34 veces más eficaz que por email!

\r\n", + request_to_administrators: false, + day_offset: 1, + required_supports: 0, + order: 3, + active: true, + action_type: 0, + short_description: "¡Es 34 veces más eficaz que por email!", + published_proposal: false) + Dashboard::Action.create(title: "Prepara tus mensajes en redes sociales", + description: "

Crea un pequeño calendario de publicaciones semanales en Facebook y/o Twitter (tres a la semana es suficiente durante los primeros meses). Utiliza imágenes en las que aparezcas tú mismo solicitando el apoyo o imágenes llamativas sobre el proyecto. Y sobre todo: utiliza un lenguaje sencillo, directo a la hora de pedir el apoyo y siempre con mensajes personalizados si vas a dirigirte a tus contactos de manera privada. Es importante que se sientan partícipes y no uno más en tu necesidad de conseguir apoyos.

\r\n", + request_to_administrators: false, + day_offset: 2, + required_supports: 0, + order: 4, + active: true, + action_type: 0, + short_description: "Es muy importante saber qué vas a decir y cómo", + published_proposal: false) + Dashboard::Action.create(title: "Informa de tus planes antes de la publicación", + description: "

No es necesario que debas tener creada totalmente o publicada tu propuesta para comenzar a hablar de ella. De hecho, te recomendamos que hagas publicaciones en tus redes sociales y hables con tus amigos y familiares de ella antes de que vea la luz. Es lo que se llama una campaña 'teaser' y sirve para crear expectación sobre algo que va a llegar muy pronto.
\r\n
\r\nPara ello te recomendamos que incluyas mensajes de este tipo en tus publicaciones en redes sociales. De hecho, puedes copiar estos textos y utilizarlos si lo deseas:
\r\n- 'Muy pronto crearé mi propia propuesta ciudadana para mejorar nuestra ciudad de Madrid, y necesitaré vuestro apoyo más que nunca. Os seguiré informando desde aquí. Muchas gracias a todos'
\r\n- Este es un mensaje para todos aquellos a los que les importa su ciudad, su barrio y nuestro futuro. Quiero mejorar Madrid con una propuesta que muy pronto os haré llegar a todos para que juntos, logremos que se lleve a cabo. ¡Gracias a todos!

\r\n", + request_to_administrators: false, + day_offset: 6, + required_supports: 0, + order: 8, + active: true, + action_type: 0, + short_description: "Todos en redes sociales deben saber que pronto crearás tu propuesta. ", + published_proposal: false) + Dashboard::Action.create(title: "Comparte tu propuesta en comunidades de facebook", + description: "

Es fundamental que busques el apoyo que necesitas en todas partes. Las comunidades y grupos (privados o abiertos) de Facebook en Madrid, también son un buen lugar del que recabar nuevos apoyos y aliados en tu camino hacia la meta.
\r\n
\r\nPara ello, busca y selecciona bien aquellas comunidades y grupos ya creados en Facebook que son más afines a la naturaleza de tu propuesta. Por ejemplo si tu propuesta versa sobre 'mejorar un parque público', seguramente encuentres el apoyo que necesitas en ciclistas, grupos de running, colectivos y personas cercanas al parque, etc. Solicita unirte a ellos, y una vez estés dentro, publica allí tu propuesta informándoles de ella y pidiéndoles respetuosamente, su apoyo en la mejora de vuestra ciudad. Ten en cuenta siempre cómo enfocar tu mensaje para que sea lo más acorde al grupo en el que publicas y a también a sus intereses. Piensa que al final se trata de obtener apoyos de personas que no conoces, y ella, también debe entender cuál es el beneficio que obtiene por ayudarte. Y desde luego, en ningún caso, debe percibir tu petición como 'spam' o intrusiva, así que ten tacto al pedir tus apoyos.

\r\n", + request_to_administrators: false, + day_offset: 9, + required_supports: 0, + order: 13, + active: true, + action_type: 0, + short_description: "¿Sabes a cuántas personas podrías llegar con estos grupos?", + published_proposal: false) + Dashboard::Action.create(title: "Analiza con detenimiento otras propuestas", + description: "

Parece una obviedad pero asegúrate de hacerlo. Antes de que tu propuesta vea la luz, fíjate en cómo lo han hecho otros ciudadanos y ciudadanas a la hora de crear la suya: fotografía, mensajes, título... Habrá algunas que te gusten más y menos, mejor o peor expuestas; tú debes analizarlas y quedarte con lo mejor de cada una para implantarlo a la tuya propia.
\r\n
\r\nAquí te dejamos estos ejemplos de las más destacadas hasta el momento para que veas cómo lo han hecho. ¡Si ellos han conseguido los apoyos necesarios o están a punto, tú también puedes!
\r\n
\r\nHaz click aquí: https://decide.madrid.es/proposals/20657-moratoria-turistica-en-el-centro-de-madrid
\r\nhttps://decide.madrid.es/proposals/9-billete-unico-para-el-transporte-publico para ver las propuestas.
\r\n
\r\n

\r\n", + request_to_administrators: false, + day_offset: 10, + required_supports: 0, + order: 14, + active: true, + action_type: 0, + short_description: "Antes de publicar tu propuesta, mira cómo lo han hecho otros", + published_proposal: false) + Dashboard::Action.create(title: "Utiliza whatsapp para difundir ", + description: "

¡Tus amigos, familiares, tu entorno cercano te apoyarán para que tu propuesta se lleve a cabo. ¡No olvides copiar el enlace de tu propuesta e incluirlo en tu mensaje para que puedan ir directamente a apoyarte! Compártelo en todos tus grupos, pégalo a todos tus contactos de manera personal y ¡obtendrás resultados más rápidamente!

\r\n", + request_to_administrators: false, + day_offset: 0, + required_supports: 0, + order: 17, + active: true, + action_type: 0, + short_description: "Whatsapp o su equivalente Telegram son grandes herramientas para conseguir apoyos instantáneos.", + published_proposal: true) + Dashboard::Action.create(title: "Utiliza tus redes sociales", + description: "

En la fase de precampaña ya te invitamos a utilizar tus redes sociales, ¡son el mejor medio para llegar al máximo de personas! Además, también te hemos dado un KIT de imágenes para que puedas comunicar tu propuesta. Ahora además, te aconsejamos que, directamente, copies y pegues el enlace de tu propuesta en tus perfiles sociales para que la gente pueda acceder y apoyarte.

\r\n", + request_to_administrators: false, + day_offset: 0, + required_supports: 0, + order: 16, + active: true, + action_type: 0, + short_description: "Copia y pega el enlace de tu propuesta en tus perfiles sociales.", + published_proposal: true) + Dashboard::Action.create(title: "Continúa pidiendo apoyo a nuevos embajadores de tu propuesta", + description: "

Tu propuesta para mejorar la ciudad puede mejorar también la vida de muchas personas incluidas aquellas que regentan bares, fruterías, peluquerías, o que ayudan a otros como ONG's, Centros Sociales, Asociaciones de Vecinos... Piensa a quién podría interesarle apoyar tu propuesta, cuéntale todo con detalle y ¡verás cómo logras muchos más apoyos de los que esperabas!

\r\n", + request_to_administrators: false, + day_offset: 1, + required_supports: 0, + order: 18, + active: true, + action_type: 0, + short_description: "Comercios, negocios locales, Centros Sociales, Colectivos...", + published_proposal: true) + Dashboard::Action.create(title: "Pide que te dejen colocar carteles", + description: "

Seguro que te llevas genial con esos dueños de negocios, comercios, tiendas, centros sociales y cívicos, asociaciones... de tu barrio. ¡Pídeles que te permitan colocar en sus instalaciones y locales algún cartel informando de tu propuesta para que todo el que pase por allí pueda apoyarte y contárselo a otras personas!
\r\n
\r\nPara ello, puedes decírselo personalmente o recurrir a los nuevos recursos de Decide Madrid. Cuando logres los apoyos necesarios (si no los tienes ya), verás que a tu disposición hemos puesto un recurso 'Póster' para que puedas descargarlo e imprimirlo o imprimirlo directamente desde esta herramienta. Una vez lo tengas, pide permiso, y pégalo en distintos lugares para que todo el que pase por allí puedan saber de tu propuesta y entrar en la plataforma para apoyarte.
\r\n

\r\n", + request_to_administrators: false, + day_offset: 1, + required_supports: 0, + order: 19, + active: true, + action_type: 0, + short_description: "¿Imaginas tu propuesta visible por todas partes?", + published_proposal: true) + Dashboard::Action.create(title: "Aplica las acciones que tienes pendientes", + description: "

Es hora de llevar un paso más allá las acciones que te aconsejamos mientras tu propuesta estaba en borrador. Si no las conoces es el momento de que las repases con atención. El primer día de campaña es muy importante. Tu propuesta, por estar entre las nuevas publicadas, tiene más atención en la web de Decide hoy ¡Aprovecha y difunde!

\r\n", + request_to_administrators: false, + day_offset: 0, + required_supports: 0, + order: 15, + active: true, + action_type: 0, + short_description: "Encontrarás todas las acciones en la página de \"Acciones recomendadas\" en tu Panel de Propuesta. Recuerda utilizarlas habitualmente para lograr nuevos apoyos.", + published_proposal: true) + Dashboard::Action.create(title: "Pide a tu gente que comparta tu propuesta", + description: "

¡Cuanta más gente conozca tu propuesta mejor! No te cortes en pedir a tus amigos, familiares y contactos que compartan ellos también tu iniciativa en sus redes sociales. ¡La unión hace la fuerza!
\r\n
\r\nEn la fase de precampaña ya te invitamos a utilizar tus redes sociales, ¡son el mejor medio para llegar al máximo de personas! Además, también te hemos dado un KIT de imágenes para que puedas comunicar tu propuesta. Y si quieres, también puedes pasarles a ellos este KIT para que publiquen las imágenes y capten apoyos para ti a través de sus perfiles personales.
\r\n

\r\n", + request_to_administrators: false, + day_offset: 3, + required_supports: 0, + order: 21, + active: true, + action_type: 0, + short_description: "Pídeles que la muevan en sus redes sociales", + published_proposal: true) + Dashboard::Action.create(title: "Tus vecinos son una gran comunidad para pedir apoyo", + description: "

El lugar en el que vives es el sitio idóneo para pedir apoyo ya que conoces a tus vecinos y de manera personal puedes pedirles que te ayuden, es sencillo y efectivo.
\r\n
\r\nPara ello, puedes decírselo personalmente, pegar un cartel en tu portal o convocar una reunión en tu casa para los interesados. Verás que dispones del recurso 'Cartel ' para que puedas descargarlo e imprimirlo. Si es necesario ayúdales para que se hagan un usuario en la plataforma Decide. Si has convocado una reunión, y vienen a tu casa o llevas contigo un ordenador, tableta o móvil con Internet, les puede echar una mano para que se registren como nuevo usuario y te apoyen.

\r\n\r\n

\r\n", + request_to_administrators: false, + day_offset: 4, + required_supports: 0, + order: 22, + active: true, + action_type: 0, + short_description: "Deja un cartel en tu portal o convoca una reunión", + published_proposal: true) + Dashboard::Action.create(title: "Imprime tu propuesta y repártela", + description: "

Estás pidiéndoles su apoyo y es normal que ellos te pidan más información. Lo mejor es que no les reclames demasiado en primera instancia. Haz unas copias del resumen de tu propuesta, y entrégaselas a quien consideres oportuno. Las personas solemos interesarnos y apoyar aquello cuyo carácter parece serio y meditado. Por ello, te aconsejamos que redactes tu propuesta brevemente e imprimas varias copias para que todos aquellos que deseen saber más, puedan leer en sus casas y entender con tranquilidad, de qué trata realmente.

\r\n", + request_to_administrators: false, + day_offset: 2, + required_supports: 0, + order: 20, + active: true, + action_type: 0, + short_description: "La gente confía más cuando tiene información suficiente", + published_proposal: true) + Dashboard::Action.create(title: "Crea tarjetas de visita adaptándolas para difundir tu propuesta", + description: "

¿Sabes cuánto cuesta hacer 500 tarjetas para presentar tu propuesta y captar nuevos apoyos de manera inmediata? Esta cantidad de tarjetas puede costar a partir de 7,25€. Busca la página web que más confianza te dé o imprimelas en una copistería, y pide a todo el mundo que te ayude a distribuirlas: amigos y familiares, vecinos, compañeros de trabajo, gente del barrio... ¡a quien tú quieras! No importa que no conozcas a la gente a la cual solicitas el apoyo para tu propuesta, ¡quien menos lo esperes te puede ayudar! Pero debes ponérselo fácil, por eso: añade el título y el identificador de tu propuesta en la tarjeta, el enlace a la web decide.madrid.es y tu nombre ¡y no te olvides de pedirles a todos/as que te apoyen, no sólo que visiten el link!

\r\n", + request_to_administrators: false, + day_offset: 5, + required_supports: 0, + order: 23, + active: true, + action_type: 0, + short_description: "Es una manera directa y diferente de llegar a más gente", + published_proposal: true) + Dashboard::Action.create(title: "Pide que te apoyen en tu trabajo", + description: "

No dudes en hacerles saber de la creación de tu propuesta y de la necesidad de su apoyo por su parte para lograr hacerla realidad. En ellos y ellas, encontrarás una fuente de apoyo enorme que puede extenderse más allá si logras que hablen de la propuesta en sus entornos personales, por eso vale la pena hacer el esfuerzo durante un sólo día y no resultar demasiado insistente.
\r\n
\r\nPara ello, puedes contarles personalmente la motivación que te llevó a publicar la propuesta o explicarlo en grupo en los momentos de descanso. También pedir permiso a tu jefe/a para enviar un email a todos solicitando el apoyo. Puedes utilizar las tarjetas de visita o el cártel que ya conoces. Una vez lo tengas, pide permiso, y pégalo en distintos lugares como la entrada del baño, la zona de descanso...

\r\n", + request_to_administrators: false, + day_offset: 6, + required_supports: 0, + order: 24, + active: true, + action_type: 0, + short_description: "Apóyate en tus compañeros y compañeras de trabajo, ¡son cruciales!", + published_proposal: true) + Dashboard::Action.create(title: "Manual Diez claves", + description: "

En estos momentos tu propuesta aún esta en modo borrador, es un momento perfecto para leerte este resumen que trata cómo publicar una propuesta ciudadana y empezar ya a reunir apoyos en Decide Madrid. Aunque ya habrás superado alguno de los pasos que repasa este artículo, como por ejemplo hacerte usuario/a, hay muchos otros que te pueden muy útiles, ya que incorporan recomendaciones. Es el caso de los consejos para conseguir una mejor redacción o con qué archivos podrías complementar la información que estás introduciendo en el formulario. ¡Échale un vistazo!

\r\n\r\n

\r\n", + request_to_administrators: true, + day_offset: 0, + required_supports: 0, + order: 0, + active: true, + action_type: 1, + short_description: "Recomendaciones antes de publicar", + published_proposal: false) + Dashboard::Action.create(title: "Mailing masivo", + description: "", + request_to_administrators: true, + day_offset: 0, + required_supports: 8000, + order: 7, + active: true, + action_type: 1, + short_description: "", + published_proposal: true) + Dashboard::Action.create(title: "Píldora de vídeo", + description: "", + request_to_administrators: false, + day_offset: 0, + required_supports: 15000, + order: 8, + active: true, + action_type: 1, + short_description: "", + published_proposal: true) + Dashboard::Action.create(title: "Imágenes para tus perfiles", + description: "

Algunos ejemplos para inspirarte o incluir en los perfiles que tengas activos en redes sociales. Se trata de cambiar temporalmente la imágen típica de tu usuario o el banner general en tu espacio de perfil, por otra que anuncie que tienes una propuesta en decide y que buscas apoyo. Puedes utilizar alguno de ellos, crear tu mismo/a el slogan que mejor te vaya e ir cambiandolos regularmente. Es un espacio muy agradecido ya que cualquier persona que te conozca lo verá sin tener que hacer tanto esfuerzo por tu parte.

\r\n", + request_to_administrators: false, + day_offset: 1, + required_supports: 0, + order: 2, + active: true, + action_type: 1, + short_description: "Ejemplos para incluir en tus Redes Sociales", + published_proposal: true) + Dashboard::Action.create(title: "Mención en RRSS del Ayuntamiento", + description: "", + request_to_administrators: true, + day_offset: 0, + required_supports: 1000, + order: 3, + active: true, + action_type: 1, + short_description: "Una difusión específica en las redes del Ayuntamiento", + published_proposal: true) + Dashboard::Action.create(title: "Decide Corner", + description: "", + request_to_administrators: true, + day_offset: 0, + required_supports: 1500, + order: 4, + active: true, + action_type: 1, + short_description: "", + published_proposal: true) + Dashboard::Action.create(title: "1 día en portada", + description: "", + request_to_administrators: true, + day_offset: 0, + required_supports: 2500, + order: 5, + active: true, + action_type: 1, + short_description: "", + published_proposal: true) + Dashboard::Action.create(title: "Anuncio en Facebook", + description: "", + request_to_administrators: true, + day_offset: 0, + required_supports: 5000, + order: 6, + active: true, + action_type: 1, + short_description: "", + published_proposal: true) + end + end diff --git a/spec/lib/tasks/dashboards_spec.rb b/spec/lib/tasks/dashboards_spec.rb index 434f2b249..745f267f4 100644 --- a/spec/lib/tasks/dashboards_spec.rb +++ b/spec/lib/tasks/dashboards_spec.rb @@ -80,4 +80,27 @@ describe "Dashboards Rake" do end + describe "#create_basic_dashboard_actions_template" do + + before do + Rake.application.rake_require "tasks/dashboards" + Rake::Task.define_task(:environment) + ActionMailer::Base.deliveries.clear + end + + let :run_rake_task do + Rake::Task["dashboards:create_basic_dashboard_actions_template"].reenable + Rake.application.invoke_task "dashboards:create_basic_dashboard_actions_template" + end + + it "Create 34 basic dashboad actions template" do + expect(Dashboard::Action.count).to eq 0 + + run_rake_task + + expect(Dashboard::Action.count).to eq 34 + end + + end + end From 8b1c458fdf21ed81c646ca066f7d6431ee52c72f Mon Sep 17 00:00:00 2001 From: taitus Date: Wed, 13 Mar 2019 23:27:18 +0100 Subject: [PATCH 06/19] Allow detect new actions since a custom date - Rename method "detect_new_actions" to "detect_new_actions_since". Add new param date. --- app/models/dashboard/action.rb | 18 +++++++++--------- app/models/proposal.rb | 4 ++-- lib/tasks/dashboards.rake | 2 +- spec/models/dashboard/action_spec.rb | 26 +++++++++++++------------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/app/models/dashboard/action.rb b/app/models/dashboard/action.rb index e88c5a714..b4a450b8f 100644 --- a/app/models/dashboard/action.rb +++ b/app/models/dashboard/action.rb @@ -89,14 +89,14 @@ class Dashboard::Action < ActiveRecord::Base course_for(proposal).first end - def self.detect_new_actions(proposal) + def self.detect_new_actions_since(date, proposal) actions_for_today = get_actions_for_today(proposal) - actions_for_yesterday = get_actions_for_yesterday(proposal) + actions_for_date = get_actions_for_date(proposal, date) actions_for_today_ids = actions_for_today.pluck(:id) - actions_for_yesterday_ids = actions_for_yesterday.pluck(:id) + actions_for_date_ids = actions_for_date.pluck(:id) - actions_for_today_ids - actions_for_yesterday_ids + actions_for_today_ids - actions_for_date_ids end private @@ -107,9 +107,9 @@ class Dashboard::Action < ActiveRecord::Base calculate_actions(proposal_votes, day_offset, proposal) end - def self.get_actions_for_yesterday(proposal) - proposal_votes = calculate_votes(proposal) - day_offset = calculate_day_offset(proposal, Date.yesterday) + def self.get_actions_for_date(proposal, date) + proposal_votes = calculate_votes(proposal, date) + day_offset = calculate_day_offset(proposal, date) calculate_actions(proposal_votes, day_offset, proposal) end @@ -125,7 +125,7 @@ class Dashboard::Action < ActiveRecord::Base .by_published_proposal(proposal.published?) end - def self.calculate_votes(proposal) - Vote.where(votable: proposal).where("created_at <= ?", Date.yesterday).count + def self.calculate_votes(proposal, date) + Vote.where(votable: proposal).where("created_at <= ?", date).count end end diff --git a/app/models/proposal.rb b/app/models/proposal.rb index 07723ea97..95473ed15 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -240,7 +240,7 @@ class Proposal < ActiveRecord::Base end def send_new_actions_notification_on_create - new_actions = Dashboard::Action.detect_new_actions(self) + new_actions = Dashboard::Action.detect_new_actions_since(Date.yesterday, self) if new_actions.present? Dashboard::Mailer.new_actions_notification_on_create(self).deliver_later @@ -248,7 +248,7 @@ class Proposal < ActiveRecord::Base end def send_new_actions_notification_on_published - new_actions_ids = Dashboard::Action.detect_new_actions(self) + new_actions_ids = Dashboard::Action.detect_new_actions_since(Date.yesterday, self) if new_actions_ids.present? Dashboard::Mailer.new_actions_notification_on_published(self, new_actions_ids).deliver_later diff --git a/lib/tasks/dashboards.rake b/lib/tasks/dashboards.rake index 8ec07f1f9..5e5264f01 100644 --- a/lib/tasks/dashboards.rake +++ b/lib/tasks/dashboards.rake @@ -3,7 +3,7 @@ namespace :dashboards do desc "Send to user notifications from new actions availability on dashboard" task send_notifications: :environment do Proposal.not_archived.each do |proposal| - new_actions_ids = Dashboard::Action.detect_new_actions(proposal) + new_actions_ids = Dashboard::Action.detect_new_actions_since(Date.yesterday, proposal) if new_actions_ids.present? if proposal.published? diff --git a/spec/models/dashboard/action_spec.rb b/spec/models/dashboard/action_spec.rb index bf9a85110..3d92cae79 100644 --- a/spec/models/dashboard/action_spec.rb +++ b/spec/models/dashboard/action_spec.rb @@ -225,7 +225,7 @@ describe Dashboard::Action do end end - context "#detect_new_actions" do + context "#detect_new_actions_since" do describe "No detect new actions" do @@ -237,7 +237,7 @@ describe Dashboard::Action do action.update(published_proposal: true) resource.update(published_proposal: true) - expect(described_class.detect_new_actions(proposal)).to eq [] + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to eq [] end it "when there are news actions actived for draft_proposal but proposal is published" do @@ -245,7 +245,7 @@ describe Dashboard::Action do action.update(published_proposal: false, day_offset: 0) resource.update(published_proposal: false, day_offset: 0) - expect(described_class.detect_new_actions(proposal)).to eq [] + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to eq [] end it "when there are not news actions actived for draft proposals" do @@ -253,7 +253,7 @@ describe Dashboard::Action do action.update(published_proposal: false) resource.update(published_proposal: false) - expect(described_class.detect_new_actions(proposal)).to eq [] + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to eq [] end it "when there are news actions actived for published_proposal but proposal is draft" do @@ -261,7 +261,7 @@ describe Dashboard::Action do action.update(published_proposal: true, day_offset: 0) resource.update(published_proposal: true, day_offset: 0) - expect(described_class.detect_new_actions(proposal)).to eq [] + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to eq [] end end @@ -275,8 +275,8 @@ describe Dashboard::Action do let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: true) } it "when proposal has been created today and day_offset is valid only for today" do - expect(described_class.detect_new_actions(proposal)).to include(resource.id) - expect(described_class.detect_new_actions(proposal)).to include(action.id) + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to include(resource.id) + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to include(action.id) end it "when proposal has received a new vote today" do @@ -285,8 +285,8 @@ describe Dashboard::Action do resource.update(required_supports: 0) create(:vote, voter: proposal.author, votable: proposal) - expect(described_class.detect_new_actions(proposal)).to include(action.id) - expect(described_class.detect_new_actions(proposal)).not_to include(resource.id) + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to include(action.id) + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).not_to include(resource.id) end end @@ -298,8 +298,8 @@ describe Dashboard::Action do let!(:resource) { create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: false) } it "when day_offset field is valid for today and invalid for yesterday" do - expect(described_class.detect_new_actions(proposal)).to include(resource.id) - expect(described_class.detect_new_actions(proposal)).to include(action.id) + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to include(resource.id) + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to include(action.id) end it "when proposal has received a new vote today" do @@ -308,8 +308,8 @@ describe Dashboard::Action do resource.update(required_supports: 2) create(:vote, voter: proposal.author, votable: proposal) - expect(described_class.detect_new_actions(proposal)).to include(action.id) - expect(described_class.detect_new_actions(proposal)).not_to include(resource.id) + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).to include(action.id) + expect(described_class.detect_new_actions_since(Date.yesterday, proposal)).not_to include(resource.id) end end From a1de5b233419cd30ceaca2373c94f48c5c9ea280 Mon Sep 17 00:00:00 2001 From: taitus Date: Wed, 13 Mar 2019 23:30:38 +0100 Subject: [PATCH 07/19] Display tag "new" on actions An author will see a "new" tag on each action or resource that has appeared on his dashboard since his last login. Too add "new" tag on dashbord menu when there are new resources. --- app/controllers/dashboard/base_controller.rb | 7 +- app/helpers/proposals_dashboard_helper.rb | 8 +++ app/views/dashboard/_menu.html.erb | 6 ++ app/views/dashboard/_proposed_action.html.erb | 4 ++ app/views/dashboard/_resource.html.erb | 3 + config/locales/en/general.yml | 1 + config/locales/es/general.yml | 1 + spec/features/dashboard/dashboard_spec.rb | 70 +++++++++++++++++++ 8 files changed, 99 insertions(+), 1 deletion(-) diff --git a/app/controllers/dashboard/base_controller.rb b/app/controllers/dashboard/base_controller.rb index 2755e74c9..14a4f7ff6 100644 --- a/app/controllers/dashboard/base_controller.rb +++ b/app/controllers/dashboard/base_controller.rb @@ -1,5 +1,5 @@ class Dashboard::BaseController < ApplicationController - before_action :authenticate_user! + before_action :authenticate_user!, :detect_new_actions_after_last_login include Dashboard::HasProposal @@ -35,4 +35,9 @@ class Dashboard::BaseController < ApplicationController def next_goal @next_goal ||= Dashboard::Action.next_goal_for(proposal) end + + def detect_new_actions_after_last_login + author_last_login = proposal.author.last_sign_in_at.to_date + @new_actions_since_last_login = Dashboard::Action.detect_new_actions_since(author_last_login, proposal) + end end diff --git a/app/helpers/proposals_dashboard_helper.rb b/app/helpers/proposals_dashboard_helper.rb index 58d468e1b..e45cd50ed 100644 --- a/app/helpers/proposals_dashboard_helper.rb +++ b/app/helpers/proposals_dashboard_helper.rb @@ -83,4 +83,12 @@ module ProposalsDashboardHelper return t("dashboard.resource.resource_requested") if resource.requested_for?(proposal) t("dashboard.resource.request_resource") end + + def is_new_action_since_last_login?(proposed_action, new_actions_since_last_login) + new_actions_since_last_login.include?(proposed_action.id) if new_actions_since_last_login.present? + end + + def new_resources_since_last_login?(resources, new_actions_since_last_login) + resources.pluck(:id).any? {|id| new_actions_since_last_login.include?(id) } if resources.present? + end end diff --git a/app/views/dashboard/_menu.html.erb b/app/views/dashboard/_menu.html.erb index 463ad470c..c6d633e79 100644 --- a/app/views/dashboard/_menu.html.erb +++ b/app/views/dashboard/_menu.html.erb @@ -18,12 +18,18 @@ <%= link_to recommended_actions_proposal_dashboard_path(proposal.to_param) do %> <%= t("dashboard.menu.recommended_actions") %> <% end %> + <% if new_resources_since_last_login?(proposed_actions, @new_actions_since_last_login) %> + <%= t("dashboard.progress.new_action" ) %> + <% end %> <% if resources_menu_visible?(proposal, resources) %>
  • <%= t("dashboard.menu.resources") %> + <% if new_resources_since_last_login?(resources, @new_actions_since_last_login) %> + <%= t("dashboard.progress.new_action" ) %> + <% end %>
      <% if can?(:manage_polls, proposal) %>
    • diff --git a/app/views/dashboard/_proposed_action.html.erb b/app/views/dashboard/_proposed_action.html.erb index 5c653efef..03a888e28 100644 --- a/app/views/dashboard/_proposed_action.html.erb +++ b/app/views/dashboard/_proposed_action.html.erb @@ -12,6 +12,10 @@ <% end %>

      <%= proposed_action.title %>

      + <% if is_new_action_since_last_login?(proposed_action, @new_actions_since_last_login) %> + <%= t("dashboard.progress.new_action") %> + <% end %> + <% if proposed_action.proposals.where(id: proposal.id).any? %>

      <%= l(proposed_action.executed_actions.find_by(proposal: proposal).executed_at.to_date) %> diff --git a/app/views/dashboard/_resource.html.erb b/app/views/dashboard/_resource.html.erb index d9a66ac67..a0447d518 100644 --- a/app/views/dashboard/_resource.html.erb +++ b/app/views/dashboard/_resource.html.erb @@ -3,6 +3,9 @@ data-equalizer-watch="resources" title="<%= resource_tooltip(resource, proposal) %>">

      <%= resource.title %>

      + <% if is_new_action_since_last_login?(resource, @new_actions_since_last_login) %> + <%= t("dashboard.progress.new_action") %> + <% end %>

      <%= resource.short_description %>

      diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index c3a0bc3c9..ce6469246 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -497,6 +497,7 @@ en: progress: Acumulated progress supports: Supports success: Ideal progress + new_action: New index: title: Edition edit_proposal_link: Edit proposal diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index b5aaeb310..5d9dc6bb4 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -497,6 +497,7 @@ es: progress: Progreso acumulado supports: Apoyos success: Progreso ideal + new_action: Nuevo index: title: Edición edit_proposal_link: Editar propuesta diff --git a/spec/features/dashboard/dashboard_spec.rb b/spec/features/dashboard/dashboard_spec.rb index 85bd04aaf..0d6e4eedf 100644 --- a/spec/features/dashboard/dashboard_spec.rb +++ b/spec/features/dashboard/dashboard_spec.rb @@ -384,4 +384,74 @@ feature "Proposal's dashboard" do expect(page).to have_content("No recommended actions done") end + describe "detect_new_actions_after_last_login" do + + before do + proposal.author.update(last_sign_in_at: Date.yesterday) + end + + scenario "Display tag 'new' on resouce when it is new for author since last login" do + resource = create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: false) + + visit progress_proposal_dashboard_path(proposal) + + within "#dashboard_action_#{resource.id}" do + expect(page).to have_content('New') + end + end + + scenario "Not display tag 'new' on resouce when there is not new resources since last login" do + resource = create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: false) + proposal.author.update(last_sign_in_at: Date.today) + + visit progress_proposal_dashboard_path(proposal) + + within "#dashboard_action_#{resource.id}" do + expect(page).not_to have_content('New') + end + end + + scenario "Display tag 'new' on proposed_action when it is new for author since last login" do + proposed_action = create(:dashboard_action, :proposed_action, :active, day_offset: 0, published_proposal: false) + + visit progress_proposal_dashboard_path(proposal) + + within "#dashboard_action_#{proposed_action.id}" do + expect(page).to have_content('New') + end + end + + scenario "Not display tag 'new' on proposed_action when there is not new proposed_action since last login" do + proposed_action = create(:dashboard_action, :proposed_action, :active, day_offset: 0, published_proposal: false) + proposal.author.update(last_sign_in_at: Date.today) + + visit progress_proposal_dashboard_path(proposal) + + within "#dashboard_action_#{proposed_action.id}" do + expect(page).not_to have_content('New') + end + end + + scenario "Display tag 'new' on sidebar menu when there is a new resouce since last login" do + resource = create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: false) + + visit progress_proposal_dashboard_path(proposal) + + within "#side_menu" do + expect(page).to have_content('New') + end + end + + scenario "Not display tag 'new' on sidebar menu when there is not a new resouce since last login" do + resource = create(:dashboard_action, :resource, :active, day_offset: 0, published_proposal: false) + proposal.author.update(last_sign_in_at: Date.today) + + visit progress_proposal_dashboard_path(proposal) + + within "#side_menu" do + expect(page).not_to have_content('New') + end + end + + end end From 6342d598f75bfc94c12c996af3e6317483986e6e Mon Sep 17 00:00:00 2001 From: taitus Date: Thu, 14 Mar 2019 11:36:01 +0100 Subject: [PATCH 08/19] Update active resources count for proposal Fix: Sum default resources (mail, poster, poll) to actives resources for a proposal. --- app/helpers/proposals_dashboard_helper.rb | 10 ++++++++++ app/views/layouts/dashboard/_proposal_totals.html.erb | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/helpers/proposals_dashboard_helper.rb b/app/helpers/proposals_dashboard_helper.rb index e45cd50ed..a96a09d84 100644 --- a/app/helpers/proposals_dashboard_helper.rb +++ b/app/helpers/proposals_dashboard_helper.rb @@ -91,4 +91,14 @@ module ProposalsDashboardHelper def new_resources_since_last_login?(resources, new_actions_since_last_login) resources.pluck(:id).any? {|id| new_actions_since_last_login.include?(id) } if resources.present? end + + def active_resources_for(proposal) + default_resources_count = 3 #resources: mail, poster, poll + Dashboard::Action.active.resources.active_for(proposal).count + default_resources_count + end + + def active_resources_count(proposal) + default_resources_count = 3 #resources: mail, poster, poll + Dashboard::Action.active.resources.by_proposal(proposal).count + default_resources_count + end end diff --git a/app/views/layouts/dashboard/_proposal_totals.html.erb b/app/views/layouts/dashboard/_proposal_totals.html.erb index 7b72cdd30..6b6d7a071 100644 --- a/app/views/layouts/dashboard/_proposal_totals.html.erb +++ b/app/views/layouts/dashboard/_proposal_totals.html.erb @@ -3,9 +3,9 @@
      - <%= Dashboard::Action.active.resources.active_for(proposal).count %> + <%= active_resources_for(proposal) %> / - <%= Dashboard::Action.active.resources.count %> + <%= active_resources_count(proposal) %>
      <%= t("layouts.dashboard.proposal_totals.active_resources") %>
      From 9d85b39356d9f29c7cf7a66cacb2e8ac34c366c8 Mon Sep 17 00:00:00 2001 From: taitus Date: Thu, 14 Mar 2019 11:39:20 +0100 Subject: [PATCH 09/19] Display correcty link for resources with request_to_administrator: false Fix: When a resource not need request_for_administrator always show text "request_resource" when the correct text is "view_resource". --- app/views/dashboard/_resource.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/dashboard/_resource.html.erb b/app/views/dashboard/_resource.html.erb index a0447d518..7b0f77d35 100644 --- a/app/views/dashboard/_resource.html.erb +++ b/app/views/dashboard/_resource.html.erb @@ -11,7 +11,7 @@

      - <% if resource.executed_for?(proposal) %> + <% if resource.executed_for?(proposal) || (!resource.request_to_administrators && resource.active_for?(proposal)) %> <%= link_to t("dashboard.resource.view_resource"), new_request_proposal_dashboard_action_path(proposal, resource), class: "button" %> From e876c07579885a1ed345da09af8c0478de2d1a56 Mon Sep 17 00:00:00 2001 From: Alessandro Cuoghi Date: Tue, 19 Mar 2019 15:01:23 +0100 Subject: [PATCH 10/19] Left-aligned label --- app/assets/stylesheets/dashboard.scss | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/assets/stylesheets/dashboard.scss b/app/assets/stylesheets/dashboard.scss index 82c9804df..ac9102f3d 100644 --- a/app/assets/stylesheets/dashboard.scss +++ b/app/assets/stylesheets/dashboard.scss @@ -101,6 +101,18 @@ margin-left: $line-height / 4; max-width: 90%; + h4 { + display: inline-block; + } + + .label { + margin-left: $line-height / 2; + } + + a { + display: block; + } + h4, p { margin-bottom: 0; @@ -168,6 +180,15 @@ width: rem-calc(36); } + .label { + position: absolute; + top: 20px; + left: 0; + } + + h4 { + margin-top: $line-height; + } .resource-description { min-height: $line-height * 4; } From c73aae96639e2632b21e63d8b2e825beabc23aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= Date: Thu, 27 Dec 2018 18:06:13 +0100 Subject: [PATCH 11/19] Remove before validation callback This was breaking nested poll_questions_answers when submitting more than one new answer at a time. --- app/models/poll/question/answer.rb | 6 --- db/dev_seeds/polls.rb | 63 ++++++++++++++------------ spec/factories/polls.rb | 1 + spec/features/officing/results_spec.rb | 8 ++-- spec/features/polls/answers_spec.rb | 2 +- spec/features/polls/polls_spec.rb | 6 +-- 6 files changed, 42 insertions(+), 44 deletions(-) diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb index 56655e457..48abdd9b8 100644 --- a/app/models/poll/question/answer.rb +++ b/app/models/poll/question/answer.rb @@ -17,8 +17,6 @@ class Poll::Question::Answer < ActiveRecord::Base validates :title, presence: true validates :given_order, presence: true, uniqueness: { scope: :question_id } - before_validation :set_order, on: :create - def description self[:description].try :html_safe end @@ -29,10 +27,6 @@ class Poll::Question::Answer < ActiveRecord::Base end end - def set_order - self.given_order = self.class.last_position(question_id) + 1 - end - def self.last_position(question_id) where(question_id: question_id).maximum('given_order') || 0 end diff --git a/db/dev_seeds/polls.rb b/db/dev_seeds/polls.rb index a7884d570..1026cb805 100644 --- a/db/dev_seeds/polls.rb +++ b/db/dev_seeds/polls.rb @@ -56,11 +56,12 @@ section "Creating Poll Questions & Answers" do end end question.save! - Faker::Lorem.words((2..4).to_a.sample).each do |title| + Faker::Lorem.words((2..4).to_a.sample).each_with_index do |title, index| description = "

      #{Faker::Lorem.paragraphs.join('

      ')}

      " answer = Poll::Question::Answer.new(question: question, title: title.capitalize, - description: description) + description: description, + given_order: index + 1) I18n.available_locales.map do |locale| Globalize.with_locale(locale) do answer.title = "#{title} (#{locale})" @@ -200,20 +201,7 @@ section "Creating Poll Questions from Proposals" do 3.times do proposal = Proposal.all.sample poll = Poll.current.first - question = Poll::Question.create(poll: poll) - Faker::Lorem.words((2..4).to_a.sample).each do |title| - description = "

      #{Faker::ChuckNorris.fact}

      " - answer = Poll::Question::Answer.new(question: question, - title: title.capitalize, - description: description) - I18n.available_locales.map do |locale| - Globalize.with_locale(locale) do - answer.title = "#{title} (#{locale})" - answer.description = "#{description} (#{locale})" - end - end - answer.save! - end + question = Poll::Question.new(poll: poll) question.copy_attributes_from_proposal(proposal) title = question.title I18n.available_locales.map do |locale| @@ -222,6 +210,20 @@ section "Creating Poll Questions from Proposals" do end end question.save! + Faker::Lorem.words((2..4).to_a.sample).each_with_index do |title, index| + description = "

      #{Faker::ChuckNorris.fact}

      " + answer = Poll::Question::Answer.new(question: question, + title: title.capitalize, + description: description, + given_order: index + 1) + I18n.available_locales.map do |locale| + Globalize.with_locale(locale) do + answer.title = "#{title} (#{locale})" + answer.description = "#{description} (#{locale})" + end + end + answer.save! + end end end @@ -229,20 +231,7 @@ section "Creating Successful Proposals" do 10.times do proposal = Proposal.all.sample poll = Poll.current.first - question = Poll::Question.create(poll: poll) - Faker::Lorem.words((2..4).to_a.sample).each do |title| - description = "

      #{Faker::ChuckNorris.fact}

      " - answer = Poll::Question::Answer.new(question: question, - title: title.capitalize, - description: description) - I18n.available_locales.map do |locale| - Globalize.with_locale(locale) do - answer.title = "#{title} (#{locale})" - answer.description = "#{description} (#{locale})" - end - end - answer.save! - end + question = Poll::Question.new(poll: poll) question.copy_attributes_from_proposal(proposal) title = question.title I18n.available_locales.map do |locale| @@ -251,5 +240,19 @@ section "Creating Successful Proposals" do end end question.save! + Faker::Lorem.words((2..4).to_a.sample).each_with_index do |title, index| + description = "

      #{Faker::ChuckNorris.fact}

      " + answer = Poll::Question::Answer.new(question: question, + title: title.capitalize, + description: description, + given_order: index + 1) + I18n.available_locales.map do |locale| + Globalize.with_locale(locale) do + answer.title = "#{title} (#{locale})" + answer.description = "#{description} (#{locale})" + end + end + answer.save! + end end end diff --git a/spec/factories/polls.rb b/spec/factories/polls.rb index 648a84afa..88e3b51aa 100644 --- a/spec/factories/polls.rb +++ b/spec/factories/polls.rb @@ -47,6 +47,7 @@ FactoryBot.define do association :question, factory: :poll_question sequence(:title) { |n| "Answer title #{n}" } sequence(:description) { |n| "Answer description #{n}" } + sequence(:given_order) { |n| n } end factory :poll_answer_video, class: 'Poll::Question::Answer::Video' do diff --git a/spec/features/officing/results_spec.rb b/spec/features/officing/results_spec.rb index 376629314..e46bef692 100644 --- a/spec/features/officing/results_spec.rb +++ b/spec/features/officing/results_spec.rb @@ -8,11 +8,11 @@ feature 'Officing Results', :with_frozen_time do @poll = @officer_assignment.booth_assignment.poll @poll.update(ends_at: 1.day.ago) @question_1 = create(:poll_question, poll: @poll) - create(:poll_question_answer, title: 'Yes', question: @question_1) - create(:poll_question_answer, title: 'No', question: @question_1) + create(:poll_question_answer, title: "Yes", question: @question_1, given_order: 1) + create(:poll_question_answer, title: "No", question: @question_1, given_order: 2) @question_2 = create(:poll_question, poll: @poll) - create(:poll_question_answer, title: 'Today', question: @question_2) - create(:poll_question_answer, title: 'Tomorrow', question: @question_2) + create(:poll_question_answer, title: "Today", question: @question_2, given_order: 1) + create(:poll_question_answer, title: "Tomorrow", question: @question_2, given_order: 2) login_as(@poll_officer.user) end diff --git a/spec/features/polls/answers_spec.rb b/spec/features/polls/answers_spec.rb index 6daffc0eb..8e5bb785e 100644 --- a/spec/features/polls/answers_spec.rb +++ b/spec/features/polls/answers_spec.rb @@ -16,7 +16,7 @@ feature 'Answers' do visit admin_question_path(question) expect(page).to have_css(".poll_question_answer", count: 2) - expect(page.body.index(answer1.title)).to be < page.body.index(answer2.title) + expect(answer2.title).to appear_before(answer1.title) within("#poll_question_answer_#{answer1.id}") do expect(page).to have_content answer1.title diff --git a/spec/features/polls/polls_spec.rb b/spec/features/polls/polls_spec.rb index 64d92eb14..69fbd68e5 100644 --- a/spec/features/polls/polls_spec.rb +++ b/spec/features/polls/polls_spec.rb @@ -28,7 +28,7 @@ feature 'Polls' do scenario "Proposal polls won't be listed" do proposal = create(:proposal) - _poll = create(:poll, related: proposal) + _poll = create(:poll, related: proposal) visit polls_path expect(page).to have_content('There are no open votings') @@ -164,7 +164,7 @@ feature 'Polls' do visit poll_path(poll) within("div#poll_question_#{question.id}") do - expect(page.body.index(answer1.title)).to be < page.body.index(answer2.title) + expect(answer2.title).to appear_before(answer1.title) end end @@ -176,7 +176,7 @@ feature 'Polls' do visit poll_path(poll) within('div.poll-more-info-answers') do - expect(page.body.index(answer1.title)).to be < page.body.index(answer2.title) + expect(answer2.title).to appear_before(answer1.title) end end From 9d9ad5003bbd5b7c7a9b57fdca4c621fe50caff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= Date: Thu, 27 Dec 2018 18:13:32 +0100 Subject: [PATCH 12/19] Add given_order to related poll question answers forms Since given order is no longer being generated automatically we need to add it to related forms and to strong parameters methods --- app/controllers/admin/poll/questions/answers_controller.rb | 3 ++- app/controllers/dashboard/polls_controller.rb | 5 +++-- app/views/admin/poll/questions/answers/_form.html.erb | 3 +++ app/views/dashboard/polls/_question_answer_fields.html.erb | 1 + spec/features/admin/poll/questions/answers/answers_spec.rb | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/controllers/admin/poll/questions/answers_controller.rb b/app/controllers/admin/poll/questions/answers_controller.rb index 225665b94..1d549ba3f 100644 --- a/app/controllers/admin/poll/questions/answers_controller.rb +++ b/app/controllers/admin/poll/questions/answers_controller.rb @@ -50,7 +50,8 @@ class Admin::Poll::Questions::AnswersController < Admin::Poll::BaseController def answer_params documents_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy] - attributes = [:title, :description, :question_id, documents_attributes: documents_attributes] + attributes = [:title, :description, :given_order, :question_id, + documents_attributes: documents_attributes] params.require(:poll_question_answer).permit(*attributes, *translation_params(Poll::Question::Answer)) end diff --git a/app/controllers/dashboard/polls_controller.rb b/app/controllers/dashboard/polls_controller.rb index 9f38c3459..40057230f 100644 --- a/app/controllers/dashboard/polls_controller.rb +++ b/app/controllers/dashboard/polls_controller.rb @@ -1,4 +1,4 @@ -class Dashboard::PollsController < Dashboard::BaseController +class Dashboard::PollsController < Dashboard::BaseController helper_method :poll def index @@ -62,7 +62,8 @@ class Dashboard::PollsController < Dashboard::BaseController end def question_answers_attributes - [:id, :_destroy, :title, :description, :question_id, documents_attributes: documents_attributes] + [:id, :_destroy, :title, :description, :given_order, :question_id, + documents_attributes: documents_attributes] end def documents_attributes diff --git a/app/views/admin/poll/questions/answers/_form.html.erb b/app/views/admin/poll/questions/answers/_form.html.erb index 1ca05d9ab..912e6bd42 100644 --- a/app/views/admin/poll/questions/answers/_form.html.erb +++ b/app/views/admin/poll/questions/answers/_form.html.erb @@ -4,6 +4,9 @@ <%= render 'shared/errors', resource: @answer %> + <%= f.hidden_field :given_order, + value: @answer.persisted? ? @answer.given_order : @answer.class.last_position(@answer.question_id || @question.id) + 1 %> + <%= f.hidden_field :question_id, value: @answer.question_id || @question.id %> <%= f.translatable_text_field :title %> diff --git a/app/views/dashboard/polls/_question_answer_fields.html.erb b/app/views/dashboard/polls/_question_answer_fields.html.erb index dd7e4e15a..e7bbce656 100644 --- a/app/views/dashboard/polls/_question_answer_fields.html.erb +++ b/app/views/dashboard/polls/_question_answer_fields.html.erb @@ -1,5 +1,6 @@
      + <%= f.hidden_field :given_order %>
      <%= f.text_field :title %> diff --git a/spec/features/admin/poll/questions/answers/answers_spec.rb b/spec/features/admin/poll/questions/answers/answers_spec.rb index dbcf8cf5c..36185743c 100644 --- a/spec/features/admin/poll/questions/answers/answers_spec.rb +++ b/spec/features/admin/poll/questions/answers/answers_spec.rb @@ -65,7 +65,7 @@ feature 'Answers' do expect(page).to have_content(new_title) expect(page).not_to have_content(old_title) - expect(page.body.index(new_title)).to be < page.body.index(answer2.title) + expect(answer2.title).to appear_before(new_title) end end From 045c950c3bc0365a8e5c627829204680e5c2da66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= Date: Thu, 27 Dec 2018 18:15:52 +0100 Subject: [PATCH 13/19] Add poll question answers script for nested fields Set poll questions answers given_order value dinamically with JS to allow to create multiple answers with correct given_order. --- app/assets/javascripts/answers.js.coffee | 9 +++++++++ app/assets/javascripts/application.js | 2 ++ 2 files changed, 11 insertions(+) create mode 100644 app/assets/javascripts/answers.js.coffee diff --git a/app/assets/javascripts/answers.js.coffee b/app/assets/javascripts/answers.js.coffee new file mode 100644 index 000000000..c36d09f12 --- /dev/null +++ b/app/assets/javascripts/answers.js.coffee @@ -0,0 +1,9 @@ +App.Answers = + + nestedAnswers: -> + $('.nested-answers').on 'cocoon:after-insert', (e, insertedItem) -> + nestedAnswersCount = $("input[type='hidden'][name$='[given_order]']").size() + $(insertedItem).find("input[type='hidden'][name$='[given_order]']").val(nestedAnswersCount) + + initialize: -> + App.Answers.nestedAnswers() diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index dd02ee6ee..ec9b05b7b 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -59,6 +59,7 @@ //= require markdown-it //= require markdown_editor //= require cocoon +//= require answers //= require legislation_admin //= require legislation //= require legislation_allegations @@ -87,6 +88,7 @@ //= require send_admin_notification_alert var initialize_modules = function() { + App.Answers.initialize(); App.Comments.initialize(); App.Users.initialize(); App.Votes.initialize(); From 0d3e20f370fbd5a12d06d1bb845974f69472aad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= Date: Fri, 28 Dec 2018 11:57:45 +0100 Subject: [PATCH 14/19] Fix dashboard poll form nested questions and answers Nested remove association links were not working properly because of missing Cocoon needed CSS class --- app/views/dashboard/polls/_form.html.erb | 7 +++++-- .../dashboard/polls/_question_answer_fields.html.erb | 2 +- app/views/dashboard/polls/_question_fields.html.erb | 9 ++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/views/dashboard/polls/_form.html.erb b/app/views/dashboard/polls/_form.html.erb index 0b856dd71..da0ab6e32 100644 --- a/app/views/dashboard/polls/_form.html.erb +++ b/app/views/dashboard/polls/_form.html.erb @@ -30,10 +30,13 @@ <%= render 'question_fields', f: question %> <% end %> - diff --git a/app/views/dashboard/polls/_question_answer_fields.html.erb b/app/views/dashboard/polls/_question_answer_fields.html.erb index e7bbce656..ff41fb479 100644 --- a/app/views/dashboard/polls/_question_answer_fields.html.erb +++ b/app/views/dashboard/polls/_question_answer_fields.html.erb @@ -1,4 +1,4 @@ -
      +
      <%= f.hidden_field :given_order %>
      diff --git a/app/views/dashboard/polls/_question_fields.html.erb b/app/views/dashboard/polls/_question_fields.html.erb index 50d752185..671bfa50e 100644 --- a/app/views/dashboard/polls/_question_fields.html.erb +++ b/app/views/dashboard/polls/_question_fields.html.erb @@ -1,4 +1,4 @@ -
      +
      <%= f.hidden_field :author_id, value: f.object.author_id || current_user.id %> <%= f.hidden_field :proposal_id, value: f.object.proposal_id || proposal.id %>
      @@ -17,10 +17,13 @@ <%= render 'question_answer_fields', f: answer %> <% end %> - From bc2d020d7e4814c007782be02bd176b750995be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= Date: Fri, 28 Dec 2018 15:02:39 +0100 Subject: [PATCH 15/19] Add nested specs Check remove behavior of poll nested resources: Questions and Answers --- spec/features/dashboard/polls_spec.rb | 67 +++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/spec/features/dashboard/polls_spec.rb b/spec/features/dashboard/polls_spec.rb index 2eafa6652..052766c16 100644 --- a/spec/features/dashboard/polls_spec.rb +++ b/spec/features/dashboard/polls_spec.rb @@ -52,7 +52,7 @@ feature 'Polls' do scenario 'Edit poll is allowed for upcoming polls' do poll = create(:poll, :incoming, related: proposal) - + visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do @@ -68,7 +68,7 @@ feature 'Polls' do scenario 'Edit poll redirects back when invalid data', js: true do poll = create(:poll, :incoming, related: proposal) - + visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do @@ -86,7 +86,7 @@ feature 'Polls' do scenario 'Edit poll is not allowed for current polls' do poll = create(:poll, :current, related: proposal) - + visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do @@ -96,7 +96,7 @@ feature 'Polls' do scenario 'Edit poll is not allowed for expired polls' do poll = create(:poll, :expired, related: proposal) - + visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do @@ -104,9 +104,58 @@ feature 'Polls' do end end + scenario 'Edit poll should allow to remove questions', :js do + poll = create(:poll, :incoming, related: proposal) + question1 = create(:poll_question, poll: poll) + question2 = create(:poll_question, poll: poll) + visit proposal_dashboard_polls_path(proposal) + within "div#poll_#{poll.id}" do + click_link 'Edit survey' + end + + within "#questions" do + expect(page).to have_css ".nested-fields", count: 2 + within first(".nested-fields") do + find('a.delete').click + end + expect(page).to have_css ".nested-fields", count: 1 + end + + click_button 'Update poll' + visit edit_proposal_dashboard_poll_path(proposal, poll) + + expect(page).to have_css ".nested-fields", count: 1 + end + + scenario 'Edit poll should allow to remove answers', :js do + poll = create(:poll, :incoming, related: proposal) + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question) + answer2 = create(:poll_question_answer, question: question) + visit proposal_dashboard_polls_path(proposal) + within "div#poll_#{poll.id}" do + click_link 'Edit survey' + end + + within "#questions #answers" do + expect(page).to have_css ".nested-fields", count: 2 + within first(".nested-fields") do + find('a.delete').click + end + expect(page).to have_css ".nested-fields", count: 1 + end + + click_button 'Update poll' + visit edit_proposal_dashboard_poll_path(proposal, poll) + + within "#questions #answers" do + expect(page).to have_css ".nested-fields", count: 1 + end + end + scenario 'View results not available for upcoming polls' do poll = create(:poll, :incoming, related: proposal) - + visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do @@ -116,7 +165,7 @@ feature 'Polls' do scenario 'View results available for current polls' do poll = create(:poll, :current, related: proposal) - + visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do @@ -126,7 +175,7 @@ feature 'Polls' do scenario 'View results available for expired polls' do poll = create(:poll, :expired, related: proposal) - + visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do @@ -136,7 +185,7 @@ feature 'Polls' do scenario 'View results redirects to results in public zone', js: true do poll = create(:poll, :expired, related: proposal) - + visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do @@ -150,7 +199,7 @@ feature 'Polls' do scenario 'Poll card' do poll = create(:poll, :expired, related: proposal) - + visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do From 5e98c23be5c24b5e5814a8e043a2664d339642ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= Date: Thu, 21 Mar 2019 10:48:25 +0100 Subject: [PATCH 16/19] Fix HTML markup We cannot use 'id' html attributes on nested answers because there will be many answers form each question so this would have generated invalid HTML. --- app/views/dashboard/polls/_form.html.erb | 9 ++---- .../polls/_question_answer_fields.html.erb | 2 +- .../dashboard/polls/_question_fields.html.erb | 9 ++---- spec/features/dashboard/polls_spec.rb | 30 +++++++++---------- 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/app/views/dashboard/polls/_form.html.erb b/app/views/dashboard/polls/_form.html.erb index da0ab6e32..fbdaf9265 100644 --- a/app/views/dashboard/polls/_form.html.erb +++ b/app/views/dashboard/polls/_form.html.erb @@ -25,18 +25,15 @@
      -
      +
      <%= f.fields_for :questions do |question| %> <%= render 'question_fields', f: question %> <% end %> - diff --git a/app/views/dashboard/polls/_question_answer_fields.html.erb b/app/views/dashboard/polls/_question_answer_fields.html.erb index ff41fb479..4f3dfae57 100644 --- a/app/views/dashboard/polls/_question_answer_fields.html.erb +++ b/app/views/dashboard/polls/_question_answer_fields.html.erb @@ -1,4 +1,4 @@ -
      +
      <%= f.hidden_field :given_order %>
      diff --git a/app/views/dashboard/polls/_question_fields.html.erb b/app/views/dashboard/polls/_question_fields.html.erb index 671bfa50e..d24fa511f 100644 --- a/app/views/dashboard/polls/_question_fields.html.erb +++ b/app/views/dashboard/polls/_question_fields.html.erb @@ -12,18 +12,15 @@
      -
      +
      <%= f.fields_for :question_answers do |answer| %> <%= render 'question_answer_fields', f: answer %> <% end %> - diff --git a/spec/features/dashboard/polls_spec.rb b/spec/features/dashboard/polls_spec.rb index 052766c16..add203101 100644 --- a/spec/features/dashboard/polls_spec.rb +++ b/spec/features/dashboard/polls_spec.rb @@ -104,51 +104,51 @@ feature 'Polls' do end end - scenario 'Edit poll should allow to remove questions', :js do + scenario "Edit poll should allow to remove questions", :js do poll = create(:poll, :incoming, related: proposal) - question1 = create(:poll_question, poll: poll) - question2 = create(:poll_question, poll: poll) + create(:poll_question, poll: poll) + create(:poll_question, poll: poll) visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do - click_link 'Edit survey' + click_link "Edit survey" end - within "#questions" do + within ".js-questions" do expect(page).to have_css ".nested-fields", count: 2 within first(".nested-fields") do - find('a.delete').click + find("a.delete").click end expect(page).to have_css ".nested-fields", count: 1 end - click_button 'Update poll' + click_button "Update poll" visit edit_proposal_dashboard_poll_path(proposal, poll) expect(page).to have_css ".nested-fields", count: 1 end - scenario 'Edit poll should allow to remove answers', :js do + scenario "Edit poll should allow to remove answers", :js do poll = create(:poll, :incoming, related: proposal) question = create(:poll_question, poll: poll) - answer1 = create(:poll_question_answer, question: question) - answer2 = create(:poll_question_answer, question: question) + create(:poll_question_answer, question: question) + create(:poll_question_answer, question: question) visit proposal_dashboard_polls_path(proposal) within "div#poll_#{poll.id}" do - click_link 'Edit survey' + click_link "Edit survey" end - within "#questions #answers" do + within ".js-questions .js-answers" do expect(page).to have_css ".nested-fields", count: 2 within first(".nested-fields") do - find('a.delete').click + find("a.delete").click end expect(page).to have_css ".nested-fields", count: 1 end - click_button 'Update poll' + click_button "Update poll" visit edit_proposal_dashboard_poll_path(proposal, poll) - within "#questions #answers" do + within ".js-questions .js-answers" do expect(page).to have_css ".nested-fields", count: 1 end end From 71887f291142da192b05f3b455a5eaddfc03e78d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= Date: Thu, 21 Mar 2019 14:03:38 +0100 Subject: [PATCH 17/19] Initialize answers 'after-insert' callback to keep answers order Also provide a function to initialize new set of answers 'affer-insert' callback after adding new questions. --- app/assets/javascripts/answers.js.coffee | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/answers.js.coffee b/app/assets/javascripts/answers.js.coffee index c36d09f12..183e1c628 100644 --- a/app/assets/javascripts/answers.js.coffee +++ b/app/assets/javascripts/answers.js.coffee @@ -1,9 +1,20 @@ App.Answers = + initializeAnswers: (answers) -> + $(answers).on 'cocoon:after-insert', (e, new_answer) -> + given_order = App.Answers.maxGivenOrder(answers) + 1 + $(new_answer).find("[name$='[given_order]']").val(given_order) + + maxGivenOrder: (answers) -> + max_order = 0 + $(answers).find("[name$='[given_order]']").each (index, answer) -> + value = parseFloat($(answer).val()) + max_order = if value > max_given_order then value else max_given_order + return max_given_order + nestedAnswers: -> - $('.nested-answers').on 'cocoon:after-insert', (e, insertedItem) -> - nestedAnswersCount = $("input[type='hidden'][name$='[given_order]']").size() - $(insertedItem).find("input[type='hidden'][name$='[given_order]']").val(nestedAnswersCount) + $('.js-answers').each (index, answers) -> + App.Answers.initializeAnswers(answers) initialize: -> App.Answers.nestedAnswers() From b417e9db31b83a1ce4e87c5a91f9ad892b31a322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= Date: Thu, 21 Mar 2019 14:04:57 +0100 Subject: [PATCH 18/19] Initialize new question answers coccon callback After adding new question we need to initialize new answers sets. --- app/assets/javascripts/application.js | 2 ++ app/assets/javascripts/questions.js.coffee | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 app/assets/javascripts/questions.js.coffee diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index ec9b05b7b..82b40edc9 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -60,6 +60,7 @@ //= require markdown_editor //= require cocoon //= require answers +//= require questions //= require legislation_admin //= require legislation //= require legislation_allegations @@ -89,6 +90,7 @@ var initialize_modules = function() { App.Answers.initialize(); + App.Questions.initialize(); App.Comments.initialize(); App.Users.initialize(); App.Votes.initialize(); diff --git a/app/assets/javascripts/questions.js.coffee b/app/assets/javascripts/questions.js.coffee new file mode 100644 index 000000000..72023a841 --- /dev/null +++ b/app/assets/javascripts/questions.js.coffee @@ -0,0 +1,8 @@ +App.Questions = + + nestedQuestions: -> + $('.js-questions').on 'cocoon:after-insert', (e, new_question) -> + App.Answers.initializeAnswers($(new_question).find('.js-answers')) + + initialize: -> + App.Questions.nestedQuestions() From af79330646c702c7c71e0df8fadbcc7ecb049b32 Mon Sep 17 00:00:00 2001 From: voodoorai2000 Date: Tue, 26 Mar 2019 12:13:56 +0100 Subject: [PATCH 19/19] Remove unnecessary spec This spec is giving some problems related to duplicity of records due to the way rake tasks are loaded. It will soon become part of seeds anyways. Removing for now. --- spec/lib/tasks/dashboards_spec.rb | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/spec/lib/tasks/dashboards_spec.rb b/spec/lib/tasks/dashboards_spec.rb index 745f267f4..434f2b249 100644 --- a/spec/lib/tasks/dashboards_spec.rb +++ b/spec/lib/tasks/dashboards_spec.rb @@ -80,27 +80,4 @@ describe "Dashboards Rake" do end - describe "#create_basic_dashboard_actions_template" do - - before do - Rake.application.rake_require "tasks/dashboards" - Rake::Task.define_task(:environment) - ActionMailer::Base.deliveries.clear - end - - let :run_rake_task do - Rake::Task["dashboards:create_basic_dashboard_actions_template"].reenable - Rake.application.invoke_task "dashboards:create_basic_dashboard_actions_template" - end - - it "Create 34 basic dashboad actions template" do - expect(Dashboard::Action.count).to eq 0 - - run_rake_task - - expect(Dashboard::Action.count).to eq 34 - end - - end - end