Create Legislation::PeopleProposal model

This commit is contained in:
lalo
2019-05-09 12:28:59 +02:00
committed by voodoorai2000
parent 3284a0848a
commit 46e5d6a9fa
13 changed files with 445 additions and 14 deletions

View File

@@ -4,8 +4,9 @@ class Comment < ApplicationRecord
include Graphqlable
include Notifiable
COMMENTABLE_TYPES = %w(Debate Proposal Budget::Investment Poll Topic Legislation::Question
Legislation::Annotation Legislation::Proposal).freeze
COMMENTABLE_TYPES = %w[Debate Proposal Budget::Investment Poll Topic
Legislation::Question Legislation::Annotation
Legislation::Proposal Legislation::PeopleProposal].freeze
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases

View File

@@ -0,0 +1,156 @@
class Legislation::PeopleProposal < ActiveRecord::Base
include ActsAsParanoidAliases
include Flaggable
include Taggable
include Conflictable
include Measurable
include Sanitizable
include Searchable
include Filterable
include Followable
include Communitable
include Documentable
include Notifiable
include Imageable
include Randomizable
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ]
accepts_nested_attributes_for :documents, allow_destroy: true
acts_as_votable
acts_as_paranoid column: :hidden_at
belongs_to :process, class_name: "Legislation::Process", foreign_key: "legislation_process_id"
belongs_to :author, -> { with_hidden }, class_name: "User", foreign_key: "author_id"
has_many :comments, as: :commentable
validates :title, presence: true
validates :summary, presence: true
validates :author, presence: true
validates :process, presence: true
validates :title, length: { in: 4..Legislation::PeopleProposal.title_max_length }
validates :description, length: { maximum: Legislation::PeopleProposal.description_max_length }
validates :terms_of_service, acceptance: { allow_nil: false }, on: :create
before_validation :set_responsible_name
before_save :calculate_hot_score, :calculate_confidence_score
scope :for_render, -> { includes(:tags) }
scope :sort_by_hot_score, -> { reorder(hot_score: :desc) }
scope :sort_by_confidence_score, -> { reorder(confidence_score: :desc) }
scope :sort_by_created_at, -> { reorder(created_at: :desc) }
scope :sort_by_most_commented, -> { reorder(comments_count: :desc) }
scope :sort_by_title, -> { reorder(title: :asc) }
scope :sort_by_id, -> { reorder(id: :asc) }
scope :sort_by_supports, -> { reorder(cached_votes_score: :desc) }
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
scope :last_week, -> { where("people_proposals.created_at >= ?", 7.days.ago)}
scope :validated, -> { where(validated: true) }
scope :selected, -> { where(selected: true) }
scope :winners, -> { selected.sort_by_confidence_score }
def to_param
"#{id}-#{title}".parameterize
end
def searchable_values
{ title => "A",
question => "B",
author.username => "B",
tag_list.join(" ") => "B",
summary => "C",
description => "D"}
end
def self.search(terms)
by_code = search_by_code(terms.strip)
by_code.present? ? by_code : pg_search(terms)
end
def self.search_by_code(terms)
matched_code = match_code(terms)
results = where(id: matched_code[1]) if matched_code
return results if results.present? && results.first.code == terms
end
def self.match_code(terms)
/\A#{Setting["proposal_code_prefix"]}-\d\d\d\d-\d\d-(\d*)\z/.match(terms)
end
def likes
cached_votes_up
end
def dislikes
cached_votes_down
end
def total_votes
cached_votes_total
end
def votes_score
cached_votes_score
end
def voters
User.active.where(id: votes_for.voters)
end
def editable?
total_votes <= Setting["max_votes_for_people_proposal_edit"].to_i
end
def editable_by?(user)
author_id == user.id && editable?
end
def votable_by?(user)
user && user.level_two_or_three_verified?
end
def register_vote(user, vote_value)
vote_by(voter: user, vote: vote_value) if votable_by?(user)
end
def code
"#{Setting["proposal_code_prefix"]}-#{created_at.strftime("%Y-%m")}-#{id}"
end
def after_commented
save # update cache when it has a new comment
end
def calculate_hot_score
self.hot_score = ScoreCalculator.hot_score(self)
end
def calculate_confidence_score
self.confidence_score = ScoreCalculator.confidence_score(total_votes, total_votes)
end
def after_hide
tags.each{ |t| t.decrement_custom_counter_for("LegislationPeopleProposal") }
end
def after_restore
tags.each{ |t| t.increment_custom_counter_for("LegislationPeopleProposal") }
end
def contact_info
[phone, email, website].compact
end
protected
def set_responsible_name
if author && author.document_number?
self.responsible_name = author.document_number
end
end
end

View File

@@ -17,7 +17,8 @@ class Legislation::Process < ApplicationRecord
include Globalizable
PHASES_AND_PUBLICATIONS = %i[homepage_phase draft_phase debate_phase allegations_phase
proposals_phase draft_publication result_publication].freeze
proposals_phase people_proposals_phase draft_publication
result_publication].freeze
CSS_HEX_COLOR = /\A#?(?:[A-F0-9]{3}){1,2}\z/i
@@ -31,6 +32,8 @@ class Legislation::Process < ApplicationRecord
foreign_key: "legislation_process_id", dependent: :destroy
has_many :proposals, -> { order(:id) }, class_name: "Legislation::Proposal",
foreign_key: "legislation_process_id", dependent: :destroy
has_many :people_proposals, -> { order(:id) }, class_name: "Legislation::PeopleProposal",
foreign_key: "legislation_process_id", dependent: :destroy
validates_translation :title, presence: true
validates :start_date, presence: true
@@ -42,6 +45,8 @@ class Legislation::Process < ApplicationRecord
validates :allegations_start_date, presence: true, if: :allegations_end_date?
validates :allegations_end_date, presence: true, if: :allegations_start_date?
validates :proposals_phase_end_date, presence: true, if: :proposals_phase_start_date?
validates :people_proposals_phase_end_date, presence: true,
if: :people_proposals_phase_start_date?
validate :valid_date_ranges
validates :background_color, format: { allow_blank: true, with: CSS_HEX_COLOR }
validates :font_color, format: { allow_blank: true, with: CSS_HEX_COLOR }
@@ -77,6 +82,11 @@ class Legislation::Process < ApplicationRecord
proposals_phase_end_date, proposals_phase_enabled)
end
def people_proposals_phase
Legislation::Process::Phase.new(people_proposals_phase_start_date,
people_proposals_phase_end_date, people_proposals_phase_enabled)
end
def draft_publication
Legislation::Process::Publication.new(draft_publication_date, draft_publication_enabled)
end

View File

@@ -23,6 +23,7 @@ class User < ApplicationRecord
has_many :identities, dependent: :destroy
has_many :debates, -> { with_hidden }, foreign_key: :author_id
has_many :proposals, -> { with_hidden }, foreign_key: :author_id
has_many :people_proposals, -> { with_hidden }, foreign_key: :author_id
has_many :budget_investments, -> { with_hidden }, foreign_key: :author_id, class_name: "Budget::Investment"
has_many :comments, -> { with_hidden }
has_many :failed_census_calls

View File

@@ -39,6 +39,7 @@ require_relative "dev_seeds/notifications"
require_relative "dev_seeds/widgets"
require_relative "dev_seeds/admin_notifications"
require_relative "dev_seeds/legislation_proposals"
require_relative "dev_seeds/legislation_people_proposals"
require_relative "dev_seeds/milestones"
require_relative "dev_seeds/pages"

View File

@@ -0,0 +1,13 @@
section "Creating legislation people proposals" do
10.times do
Legislation::PeopleProposal.create!(title: Faker::Lorem.sentence(3).truncate(60),
description: Faker::Lorem.paragraphs.join("\n\n"),
question: Faker::Lorem.sentence(3),
summary: Faker::Lorem.paragraph,
author: User.all.sample,
process: Legislation::Process.all.sample,
terms_of_service: "1",
validated: rand <= 2.0 / 3,
selected: rand <= 1.0 / 3)
end
end

View File

@@ -11,6 +11,8 @@ section "Creating collaborative legislation" do
debate_end_date: Date.current + (i - 5).days,
proposals_phase_start_date: Date.current + (i - 7).days,
proposals_phase_end_date: Date.current + (i - 5).days,
people_proposals_phase_start_date: Date.current + (i - 7).days,
people_proposals_phase_end_date: Date.current + (i - 5).days,
draft_publication_date: Date.current + (i - 3).days,
allegations_start_date: Date.current + (i - 2).days,
allegations_end_date: Date.current + (i - 1).days,
@@ -20,6 +22,7 @@ section "Creating collaborative legislation" do
draft_publication_enabled: true,
result_publication_enabled: true,
proposals_phase_enabled: true,
people_proposals_phase_enabled: true,
published: true)
end

View File

@@ -0,0 +1,7 @@
class AddPeopleProposalsPhaseToLegislationProcesses < ActiveRecord::Migration
def change
add_column :legislation_processes, :people_proposals_phase_start_date, :date
add_column :legislation_processes, :people_proposals_phase_end_date, :date
add_column :legislation_processes, :people_proposals_phase_enabled, :boolean
end
end

View File

@@ -0,0 +1,35 @@
class CreateLegislationPeopleProposalsTable < ActiveRecord::Migration
def change
create_table :legislation_people_proposals, force: :cascade do |t|
t.integer "legislation_process_id"
t.string "title", limit: 80
t.text "description"
t.string "question"
t.integer "author_id"
t.datetime "hidden_at"
t.integer "flags_count", default: 0
t.datetime "ignored_flag_at"
t.integer "cached_votes_up", default: 0
t.integer "comments_count", default: 0
t.datetime "confirmed_hide_at"
t.integer "hot_score", limit: 8, default: 0
t.integer "confidence_score", default: 0
t.string "responsible_name", limit: 60
t.text "summary"
t.string "video_url"
t.tsvector "tsv"
t.datetime "retired_at"
t.string "retired_reason"
t.text "retired_explanation"
t.integer "community_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "cached_votes_total", default: 0
t.integer "cached_votes_down", default: 0
t.boolean "selected"
t.boolean "validated"
t.integer "cached_votes_score", default: 0
end
add_index "legislation_people_proposals", ["cached_votes_score"], name: "index_legislation_people_proposals_on_cached_votes_score", using: :btree
end
end

View File

@@ -0,0 +1,11 @@
class AddContactFieldsToLegislationPeopleProposals < ActiveRecord::Migration
def change
add_column :legislation_people_proposals, :email, :string
add_column :legislation_people_proposals, :phone, :string
add_column :legislation_people_proposals, :twitter, :string
add_column :legislation_people_proposals, :facebook, :string
add_column :legislation_people_proposals, :instagram, :string
add_column :legislation_people_proposals, :youtube, :string
add_column :legislation_people_proposals, :website, :string
end
end

View File

@@ -667,6 +667,48 @@ ActiveRecord::Schema.define(version: 20190429125842) do
t.index ["status"], name: "index_legislation_draft_versions_on_status", using: :btree
end
create_table "legislation_people_proposals", force: :cascade do |t|
t.integer "legislation_process_id"
t.string "title", limit: 80
t.text "description"
t.string "question"
t.integer "author_id"
t.datetime "hidden_at"
t.integer "flags_count", default: 0
t.datetime "ignored_flag_at"
t.integer "cached_votes_up", default: 0
t.integer "comments_count", default: 0
t.datetime "confirmed_hide_at"
t.bigint "hot_score", default: 0
t.integer "confidence_score", default: 0
t.string "responsible_name", limit: 60
t.text "summary"
t.string "video_url"
t.tsvector "tsv"
t.datetime "retired_at"
t.string "retired_reason"
t.text "retired_explanation"
t.integer "community_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "cached_votes_total", default: 0
t.integer "cached_votes_down", default: 0
t.boolean "selected"
t.boolean "validated"
t.integer "cached_votes_score", default: 0
t.string "email"
t.string "phone"
t.string "twitter"
t.string "facebook"
t.string "instagram"
t.string "youtube"
t.string "website"
t.string "external_url"
t.integer "geozone_id"
t.index ["cached_votes_score"], name: "index_legislation_people_proposals_on_cached_votes_score", using: :btree
t.index ["geozone_id"], name: "index_legislation_people_proposals_on_geozone_id", using: :btree
end
create_table "legislation_process_translations", force: :cascade do |t|
t.integer "legislation_process_id", null: false
t.string "locale", null: false
@@ -695,24 +737,27 @@ ActiveRecord::Schema.define(version: 20190429125842) do
t.date "allegations_end_date"
t.date "result_publication_date"
t.datetime "hidden_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "summary"
t.boolean "debate_phase_enabled", default: false
t.boolean "allegations_phase_enabled", default: false
t.boolean "draft_publication_enabled", default: false
t.boolean "result_publication_enabled", default: false
t.boolean "published", default: true
t.boolean "debate_phase_enabled", default: false
t.boolean "allegations_phase_enabled", default: false
t.boolean "draft_publication_enabled", default: false
t.boolean "result_publication_enabled", default: false
t.boolean "published", default: true
t.date "proposals_phase_start_date"
t.date "proposals_phase_end_date"
t.boolean "proposals_phase_enabled"
t.text "proposals_description"
t.date "draft_start_date"
t.date "draft_end_date"
t.boolean "draft_phase_enabled", default: false
t.boolean "homepage_enabled", default: false
t.boolean "draft_phase_enabled", default: false
t.boolean "homepage_enabled", default: false
t.text "background_color"
t.text "font_color"
t.date "people_proposals_phase_start_date"
t.date "people_proposals_phase_end_date"
t.boolean "people_proposals_phase_enabled"
t.index ["allegations_end_date"], name: "index_legislation_processes_on_allegations_end_date", using: :btree
t.index ["allegations_start_date"], name: "index_legislation_processes_on_allegations_start_date", using: :btree
t.index ["debate_end_date"], name: "index_legislation_processes_on_debate_end_date", using: :btree
@@ -1423,10 +1468,10 @@ ActiveRecord::Schema.define(version: 20190429125842) do
t.boolean "created_from_signature", default: false
t.integer "failed_email_digests_count", default: 0
t.text "former_users_data_log", default: ""
t.integer "balloted_heading_id"
t.boolean "public_interests", default: false
t.boolean "recommended_debates", default: true
t.boolean "recommended_proposals", default: true
t.integer "balloted_heading_id"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
t.index ["geozone_id"], name: "index_users_on_geozone_id", using: :btree

View File

@@ -13,10 +13,13 @@ FactoryBot.define do
allegations_end_date { Date.current + 3.days }
proposals_phase_start_date { Date.current }
proposals_phase_end_date { Date.current + 2.days }
people_proposals_phase_start_date { Date.current }
people_proposals_phase_end_date { Date.current + 2.days }
result_publication_date { Date.current + 5.days }
debate_phase_enabled true
allegations_phase_enabled true
proposals_phase_enabled true
people_proposals_phase_enabled true
draft_publication_enabled true
result_publication_enabled true
published true
@@ -63,6 +66,18 @@ FactoryBot.define do
proposals_phase_enabled true
end
trait :in_people_proposals_phase do
people_proposals_phase_start_date { Date.current - 1.day }
people_proposals_phase_end_date { Date.current + 2.days }
people_proposals_phase_enabled true
end
trait :upcoming_people_proposals_phase do
people_proposals_phase_start_date { Date.current + 1.day }
people_proposals_phase_end_date { Date.current + 2.days }
people_proposals_phase_enabled true
end
trait :published do
published true
end
@@ -86,15 +101,17 @@ FactoryBot.define do
allegations_end_date nil
proposals_phase_start_date nil
proposals_phase_end_date nil
people_proposals_phase_start_date nil
people_proposals_phase_end_date nil
result_publication_date nil
debate_phase_enabled false
allegations_phase_enabled false
proposals_phase_enabled false
people_proposals_phase_enabled false
draft_publication_enabled false
result_publication_enabled false
published true
end
end
factory :legislation_draft_version, class: "Legislation::DraftVersion" do
@@ -162,4 +179,27 @@ LOREM_IPSUM
process factory: :legislation_process
author factory: :user
end
factory :legislation_people_proposal, class: "Legislation::PeopleProposal" do
sequence(:title) { |n| "People and group #{n} for a legislation" }
summary "This law should be implemented by..."
terms_of_service "1"
process factory: :legislation_process
author factory: :user
validated false
trait :with_contact_info do
email "proposal@test.com"
website "https://proposal.io"
phone "666666666"
facebook "facebook.id"
twitter "TwitterId"
youtube "youtubechannelid"
instagram "instagramid"
end
trait :validated do
validated true
end
end
end

View File

@@ -0,0 +1,108 @@
require "rails_helper"
describe Legislation::PeopleProposal do
let(:people_proposal) { build(:legislation_people_proposal) }
it "is valid" do
expect(people_proposal).to be_valid
end
it "is not valid without a process" do
people_proposal.process = nil
expect(people_proposal).not_to be_valid
end
it "is not valid without an author" do
people_proposal.author = nil
expect(people_proposal).not_to be_valid
end
it "is not valid without a title" do
people_proposal.title = nil
expect(people_proposal).not_to be_valid
end
it "is not valid without a summary" do
people_proposal.summary = nil
expect(people_proposal).not_to be_valid
end
describe "#hot_score" do
let(:now) { Time.current }
it "period is correctly calculated to get exact votes per day" do
new_people_proposal = create(:legislation_people_proposal, created_at: 23.hours.ago)
2.times { new_people_proposal.vote_by(voter: create(:user), vote: "yes") }
expect(new_people_proposal.hot_score).to be 2
old_people_proposal = create(:legislation_people_proposal, created_at: 25.hours.ago)
2.times { old_people_proposal.vote_by(voter: create(:user), vote: "yes") }
expect(old_people_proposal.hot_score).to be 1
older_people_proposal = create(:legislation_people_proposal, created_at: 49.hours.ago)
3.times { older_people_proposal.vote_by(voter: create(:user), vote: "yes") }
expect(old_people_proposal.hot_score).to be 1
end
it "remains the same for not voted people_proposals" do
new = create(:legislation_people_proposal, created_at: now)
old = create(:legislation_people_proposal, created_at: 1.day.ago)
older = create(:legislation_people_proposal, created_at: 2.month.ago)
expect(new.hot_score).to be 0
expect(old.hot_score).to be 0
expect(older.hot_score).to be 0
end
it "increases for people_proposals with more positive votes" do
more_positive_votes = create(:legislation_people_proposal)
2.times { more_positive_votes.vote_by(voter: create(:user), vote: "yes") }
less_positive_votes = create(:legislation_people_proposal)
less_positive_votes.vote_by(voter: create(:user), vote: "yes")
expect(more_positive_votes.hot_score).to be > less_positive_votes.hot_score
end
it "increases for people_proposals with the same amount of positive votes within less days" do
newer_people_proposal = create(:legislation_people_proposal, created_at: now)
5.times { newer_people_proposal.vote_by(voter: create(:user), vote: "yes") }
older_people_proposal = create(:legislation_people_proposal, created_at: 1.day.ago)
5.times { older_people_proposal.vote_by(voter: create(:user), vote: "yes") }
expect(newer_people_proposal.hot_score).to be > older_people_proposal.hot_score
end
it "increases for people_proposals voted within the period (last month by default)" do
newer_people_proposal = create(:legislation_people_proposal, created_at: 2.months.ago)
20.times { create(:vote, votable: newer_people_proposal, created_at: 3.days.ago) }
older_people_proposal = create(:legislation_people_proposal, created_at: 2.months.ago)
20.times { create(:vote, votable: older_people_proposal, created_at: 40.days.ago) }
expect(newer_people_proposal.hot_score).to be > older_people_proposal.hot_score
end
describe "actions which affect it" do
let(:people_proposal) { create(:legislation_people_proposal) }
before do
5.times { people_proposal.vote_by(voter: create(:user), vote: "yes") }
2.times { people_proposal.vote_by(voter: create(:user), vote: "no") }
end
it "increases with positive votes" do
previous = people_proposal.hot_score
3.times { people_proposal.vote_by(voter: create(:user), vote: "yes") }
expect(previous).to be < people_proposal.hot_score
end
it "decreases with negative votes" do
previous = people_proposal.hot_score
3.times { people_proposal.vote_by(voter: create(:user), vote: "no") }
expect(previous).to be > people_proposal.hot_score
end
end
end
end