Files
grecia/app/models/legislation/proposal.rb
Javi Martín 660c59016b Fix random proposals order in the same session
Using `setseed` and ordering by `RAND()` doesn't always return the same
results because, although the generated random numbers will always be
the same, PostgreSQL doesn't guarantee the order of the rows it will
apply those random numbers to, similar to the way it doesn't guarantee
an order when the `ORDER BY` clause isn't specified.

Using something like `reorder("legislation_proposals.id % #{seed}")`,
like we do in budget investments, is certainly more elegant but it makes
the test checking two users get different results fail sometimes, so
that approach might need some adjustments in order to make the results
more random.
2019-02-20 11:51:32 +01:00

165 lines
4.6 KiB
Ruby

class Legislation::Proposal < 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
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'
belongs_to :geozone
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::Proposal.title_max_length }
validates :description, length: { maximum: Legislation::Proposal.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("proposals.created_at >= ?", 7.days.ago)}
scope :selected, -> { where(selected: true) }
scope :random, -> (seed) { sort_by_random(seed) }
scope :winners, -> { selected.sort_by_confidence_score }
def self.sort_by_random(seed)
ids = pluck(:id).shuffle(random: Random.new(seed))
return all if ids.empty?
ids_with_order = ids.map.with_index { |id, order| "(#{id}, #{order})" }.join(", ")
joins("LEFT JOIN (VALUES #{ids_with_order}) AS ids(id, ordering) ON #{table_name}.id = ids.id")
.order("ids.ordering")
end
def to_param
"#{id}-#{title}".parameterize
end
def searchable_values
{ title => 'A',
question => 'B',
author.username => 'B',
tag_list.join(' ') => 'B',
geozone.try(:name) => '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_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('LegislationProposal') }
end
def after_restore
tags.each{ |t| t.increment_custom_counter_for('LegislationProposal') }
end
protected
def set_responsible_name
if author && author.document_number?
self.responsible_name = author.document_number
end
end
end