mysociety/alaveteli

View on GitHub
app/models/track_thing.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# -*- encoding : utf-8 -*-
# == Schema Information
#
# Table name: track_things
#
#  id               :integer          not null, primary key
#  tracking_user_id :integer          not null
#  track_query      :string(500)      not null
#  info_request_id  :integer
#  tracked_user_id  :integer
#  public_body_id   :integer
#  track_medium     :string           not null
#  track_type       :string           default("internal_error"), not null
#  created_at       :datetime
#  updated_at       :datetime
#

# models/track_thing.rb:
# When somebody is getting alerts for something.
#
# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
# Email: hello@mysociety.org; WWW: http://www.mysociety.org/

require 'set'

# TODO: TrackThing looks like a good candidate for single table inheritance

class TrackThing < ActiveRecord::Base

  TRACK_MEDIUMS = %w(email_daily feed)

  belongs_to :info_request,
             :inverse_of => :track_things
  belongs_to :public_body,
             :inverse_of => :track_things
  belongs_to :tracking_user,
             :class_name => 'User',
             :inverse_of => :track_things,
             :counter_cache => true
  belongs_to :tracked_user,
             :class_name => 'User',
             :inverse_of => :track_things
  has_many :track_things_sent_emails,
           :inverse_of => :track_thing,
           :dependent => :destroy

  validates_presence_of :track_query, :message => _("Query can't be blank")
  validates_presence_of :track_type
  validates_inclusion_of :track_type,
                         :in => TranslatedConstants.track_types.keys
  validates_inclusion_of :track_medium, :in => TRACK_MEDIUMS
  validates_length_of :track_query, :maximum => 500, :message => _("Query is too long")

  # When constructing a new track, use this to avoid duplicates / double
  # posting
  def self.find_existing(tracking_user, track)
    return nil if tracking_user.nil?
    where(:tracking_user_id => tracking_user.id,
          :track_query => track.track_query,
          :track_type => track.track_type).first
  end

  def self.track_type_description(track_type)
    TranslatedConstants.track_types.
      fetch(track_type) { raise "internal error #{ track_type }" }
  end

  def self.create_track_for_request(info_request)
    new(:track_type => 'request_updates',
        :info_request => info_request,
        :track_query => "request:#{ info_request.url_title }")
  end

  def self.create_track_for_all_new_requests
    new(:track_type => 'all_new_requests',
        :track_query => 'variety:sent')
  end

  def self.create_track_for_all_successful_requests
    new(:track_type => 'all_successful_requests',
        :track_query => 'variety:response ' \
        '(status:successful OR status:partially_successful)')
  end

  def self.create_track_for_public_body(public_body, event_type = nil)
    query = "requested_from:#{ public_body.url_name }"
    if InfoRequestEvent::EVENT_TYPES.include?(event_type)
      query += " variety:#{ event_type }"
    end
    new(:track_type => 'public_body_updates',
        :public_body => public_body,
        :track_query => query)
  end

  def self.create_track_for_user(user)
    new(:track_type => 'user_updates',
        :tracked_user => user,
        :track_query => "requested_by:#{ user.url_name }" \
        " OR commented_by: #{ user.url_name }")
  end

  def self.create_track_for_search_query(query, variety_postfix = nil)
    # TODO: should extract requested_by:, request:, requested_from:
    # and stick their values into the respective relations.
    # Should also update "params" to make the list_description
    # nicer and more generic.  It will need to do some clever
    # parsing of the query to do this nicely
    unless query =~ /variety:/
      case variety_postfix
      when "requests"
        query += " variety:sent"
      when "users"
        query += " variety:user"
      when "bodies"
        query += " variety:authority"
      end
    end
    new(:track_type => 'search_query',
        :track_query => query)
  end

  def track_type_description
    TrackThing.track_type_description(track_type)
  end

  def track_query_description
    filter_description = query_filter_description('(variety:sent OR variety:followup_sent OR variety:response OR variety:comment)',
                                                  :no_query => N_("all requests or comments"),
                                                  :query => N_("all requests or comments matching text '{{query}}'"))
    return filter_description if filter_description

    filter_description = query_filter_description('(latest_status:successful OR latest_status:partially_successful)',
                                                  :no_query => N_("requests which are successful"),
                                                  :query => N_("requests which are successful matching text '{{query}}'"))
    return filter_description if filter_description

    _("anything matching text '{{query}}'", :query => track_query)
  end

  # Return a readable query description for queries involving commonly used
  # filter clauses
  def query_filter_description(string, options)
    parsed_query = track_query.gsub(string, '')
    if parsed_query != track_query
      parsed_query.strip!
      if parsed_query.empty?
        _(options[:no_query])
      else
        _(options[:query], :query => parsed_query)
      end
    end
  end

  # Return hash of text parameters based on the track_type describing the
  # request etc.
  def params
    @params ||= params_for(track_type)
  end

  private

  def params_for(track_type)
    if respond_to?("#{ track_type }_params", true)
      send("#{ track_type }_params")
    else
      raise "unknown tracking type #{ track_type }"
    end
  end

  def request_updates_params
    { # Website
      :verb_on_page => _("Follow this request"),
      :verb_on_page_already => _("You are already following this request"),
      # Email
      :title_in_email => _("New updates for the request '{{request_title}}'",
                           :request_title => info_request.title.html_safe),
      :title_in_rss => _("New updates for the request '{{request_title}}'",
                         :request_title => info_request.title),
      # Authentication
      :web => _("To follow the request '{{request_title}}'",
                :request_title => info_request.title.html_safe),
      :email => _("Then you will be updated whenever the request '{{request_title}}' is updated.",
                  :request_title => info_request.title),
      :email_subject => _("Confirm you want to follow the request '{{request_title}}'",
                          :request_title => info_request.title),
      # RSS sorting
      :feed_sortby => 'newest'
      }
  end

  def all_new_requests_params
    { # Website
      :verb_on_page => _("Follow all new requests"),
      :verb_on_page_already => _("You are already following new requests"),
      # Email
      :title_in_email => _("New Freedom of Information requests"),
      :title_in_rss => _("New Freedom of Information requests"),
      # Authentication
      :web => _("To follow new requests"),
      :email => _("Then you will be following all new FOI requests."),
      :email_subject => _("Confirm you want to follow new requests"),
      # RSS sorting
      :feed_sortby => 'newest'
      }
  end

  def all_successful_requests_params
    { # Website
      :verb_on_page => _("Follow new successful responses"),
      :verb_on_page_already => _("You are following all new successful responses"),
      # Email
      :title_in_email => _("Successful Freedom of Information requests"),
      :title_in_rss => _("Successful Freedom of Information requests"),
      # Authentication
      :web => _("To follow all successful requests"),
      :email => _("Then you will be notified whenever an FOI request succeeds."),
      :email_subject => _("Confirm you want to follow all successful FOI requests"),
      # RSS sorting - used described date, as newest would give a
      # date for responses possibly days before description, so
      # wouldn't appear at top of list when description (known
      # success) causes match.
      :feed_sortby => 'described'
      }
  end

  def public_body_updates_params
    { # Website
      :verb_on_page => _("Follow requests to {{public_body_name}}",
                         :public_body_name => public_body.name),
      :verb_on_page_already => _("Following"),
      # Email
      :title_in_email => _("{{foi_law}} requests to '{{public_body_name}}'",
                           :foi_law => public_body.law_only_short,
                           :public_body_name => public_body.name),
      :title_in_rss => _("{{foi_law}} requests to '{{public_body_name}}'",
                         :foi_law => public_body.law_only_short,
                         :public_body_name => public_body.name),
      # Authentication
      :web => _("To follow requests made using {{site_name}} to the public authority '{{public_body_name}}'",
                :site_name => AlaveteliConfiguration.site_name.html_safe,
                :public_body_name => public_body.name.html_safe),
      :email => _("Then you will be notified whenever someone requests something or gets a response from '{{public_body_name}}'.",
                  :public_body_name => public_body.name),
      :email_subject => _("Confirm you want to follow requests to '{{public_body_name}}'",
                          :public_body_name => public_body.name),
      # RSS sorting
      :feed_sortby => 'newest'
      }
  end

  def user_updates_params
    { # Website
      :verb_on_page => _("Follow this person"),
      :verb_on_page_already => _("You are already following this person"),
      # Email
      :title_in_email => _("FOI requests by '{{user_name}}'",
                           :user_name => tracked_user.name.html_safe),
      :title_in_rss => _("FOI requests by '{{user_name}}'",
                         :user_name => tracked_user.name),
      # Authentication
      :web => _("To follow requests by '{{user_name}}'",
                :user_name => tracked_user.name.html_safe),
      :email => _("Then you will be notified whenever '{{user_name}}' requests something or gets a response.",
                  :user_name => tracked_user.name),
      :email_subject => _("Confirm you want to follow requests by '{{user_name}}'",
                          :user_name => tracked_user.name),
      # RSS sorting
      :feed_sortby => 'newest'
      }
  end

  def search_query_params
    { # Website
      :verb_on_page => _("Follow things matching this search"),
      :verb_on_page_already => _("You are already following things matching this search"),
      # Email
      :title_in_email => _("Requests or responses matching your saved search"),
      :title_in_rss => _("Requests or responses matching your saved search"),
      # Authentication
      :web => _("To follow requests and responses matching your search"),
      :email => _("Then you will be notified whenever a new request or response matches your search."),
      :email_subject => _("Confirm you want to follow new requests or responses matching your search"),
      # RSS sorting - TODO: hmmm, we don't really know which to use
      # here for sorting. Might be a query term (e.g. 'cctv'), in
      # which case newest is good, or might be something like
      # all refused requests in which case want to sort by
      # described (when we discover criteria is met). Rather
      # conservatively am picking described, as that will make
      # things appear in feed more than the should, rather than less.
      :feed_sortby => 'described'
      }
  end

end