# == Schema Information
#
# Table name: track_things
#
# id :integer not null, primary key
# tracking_user_id :integer not null
# track_query :string(255) not null
# info_request_id :integer
# tracked_user_id :integer
# public_body_id :integer
# track_medium :string(255) not null
# track_type :string(255) 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
belongs_to :tracking_user, :class_name => 'User'
validates_presence_of :track_query
validates_presence_of :track_type
belongs_to :info_request
belongs_to :public_body
belongs_to :tracked_user, :class_name => 'User'
has_many :track_things_sent_emails
validates_inclusion_of :track_type, :in => [
'request_updates',
'all_new_requests',
'all_successful_requests',
'public_body_updates',
'user_updates',
'search_query'
]
validates_inclusion_of :track_medium, :in => [
'email_daily',
'feed'
]
def TrackThing.track_type_description(track_type)
if track_type == 'request_updates'
_("Individual requests")
elsif track_type == 'all_new_requests' || track_type == "all_successful_requests"
_("Many requests")
elsif track_type == 'public_body_updates'
_("Public authorities")
elsif track_type == 'user_updates'
_("People")
elsif track_type == 'search_query'
_("Search queries")
else
raise "internal error " + track_type
end
end
def track_type_description
TrackThing.track_type_description(self.track_type)
end
def track_query_description
# XXX this is very brittle... we should probably ask users
# simply to name their tracks when they make them?
original_text = parsed_text = self.track_query.gsub(/([()]|OR)/, "")
filters = parsed_text.scan /\b\S+:\S+\b/
varieties = Set.new
date = ""
statuses = Set.new
for filter in filters
parsed_text = parsed_text.sub(filter, "")
if filter =~ /variety:user/
varieties << _("users")
end
if filter =~ /variety:comment/
varieties << _("comments")
end
if filter =~ /variety:authority/
varieties << _("authorities")
end
if filter =~ /(variety:(sent|followup_sent|response)|latest_status)/
varieties << _("requests")
end
if filter =~ /[0-9\/]+\.\.[0-9\/]+/
date = _("between two dates")
end
if filter =~ /(rejected|not_held)/
statuses << _("unsuccessful")
end
if filter =~ /(:successful|:partially_successful)/
statuses << _("successful")
end
if filter =~ /waiting/
statuses << _("awaiting a response")
end
end
if filters.empty?
parsed_text = original_text
end
descriptions = []
if varieties.include? _("requests")
if statuses.empty?
# HACK: Relies on the 'descriptions.sort' below to luckily put this first
descriptions << _("all requests")
else
descriptions << _("requests which are {{list_of_statuses}}", :list_of_statuses => Array(statuses).sort.join(_(' or ')))
end
varieties -= [_("requests")]
end
if descriptions.empty? and varieties.empty?
varieties << _("anything")
end
descriptions += Array(varieties)
parsed_text = parsed_text.strip
descriptions = descriptions.sort.join(_(" or "))
if !parsed_text.empty?
descriptions += _("{{list_of_things}} matching text '{{search_query}}'", :list_of_things => "", :search_query => parsed_text)
end
return descriptions
end
def TrackThing.create_track_for_request(info_request)
track_thing = TrackThing.new
track_thing.track_type = 'request_updates'
track_thing.info_request = info_request
track_thing.track_query = "request:" + info_request.url_title
return track_thing
end
def TrackThing.create_track_for_all_new_requests
track_thing = TrackThing.new
track_thing.track_type = 'all_new_requests'
track_thing.track_query = "variety:sent"
return track_thing
end
def TrackThing.create_track_for_all_successful_requests
track_thing = TrackThing.new
track_thing.track_type = 'all_successful_requests'
track_thing.track_query = 'variety:response (status:successful OR status:partially_successful)'
return track_thing
end
def TrackThing.create_track_for_public_body(public_body, event_type = nil)
track_thing = TrackThing.new
track_thing.track_type = 'public_body_updates'
track_thing.public_body = public_body
query = "requested_from:" + public_body.url_name
if InfoRequestEvent.enumerate_event_types.include?(event_type)
query += " variety:" + event_type
end
track_thing.track_query = query
return track_thing
end
def TrackThing.create_track_for_user(user)
track_thing = TrackThing.new
track_thing.track_type = 'user_updates'
track_thing.tracked_user = user
track_thing.track_query = "requested_by:" + user.url_name + " OR commented_by:" + user.url_name
return track_thing
end
def TrackThing.create_track_for_search_query(query, variety_postfix = nil)
track_thing = TrackThing.new
track_thing.track_type = 'search_query'
if !(query =~ /variety:/)
case variety_postfix
when "requests"
query += " variety:sent"
when "users"
query += " variety:user"
when "bodies"
query += " variety:authority"
end
end
track_thing.track_query = query
# XXX 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
return track_thing
end
# Return hash of text parameters describing the request etc.
include LinkToHelper
def params
if @params.nil?
if self.track_type == 'request_updates'
@params = {
# Website
:list_description => _("'{{link_to_request}}', a request",
:link_to_request => ("" + CGI.escapeHTML(self.info_request.title) + "").html_safe), # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how
: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 => self.info_request.title.html_safe),
:title_in_rss => _("New updates for the request '{{request_title}}'", :request_title => self.info_request.title),
# Authentication
:web => _("To follow the request '{{request_title}}'", :request_title => CGI.escapeHTML(self.info_request.title)),
:email => _("Then you will be updated whenever the request '{{request_title}}' is updated.", :request_title => CGI.escapeHTML(self.info_request.title)),
:email_subject => _("Confirm you want to follow the request '{{request_title}}'", :request_title => self.info_request.title),
# RSS sorting
:feed_sortby => 'newest'
}
elsif self.track_type == 'all_new_requests'
@params = {
# Website
:list_description => _("any new requests"),
: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'
}
elsif self.track_type == 'all_successful_requests'
@params = {
# Website
:list_description => _("any successful requests"),
: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'
}
elsif self.track_type == 'public_body_updates'
@params = {
# Website
:list_description => _("'{{link_to_authority}}', a public authority", :link_to_authority => ("" + CGI.escapeHTML(self.public_body.name) + "").html_safe), # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how
:verb_on_page => _("Follow requests to {{public_body_name}}",:public_body_name=>CGI.escapeHTML(self.public_body.name)),
:verb_on_page_already => _("You are already following requests to {{public_body_name}}", :public_body_name=>CGI.escapeHTML(self.public_body.name)),
# Email
:title_in_email => self.public_body.law_only_short + " requests to '" + self.public_body.name + "'",
:title_in_rss => self.public_body.law_only_short + " requests to '" + self.public_body.name + "'",
# Authentication
:web => _("To follow requests made using {{site_name}} to the public authority '{{public_body_name}}'", :site_name=>AlaveteliConfiguration::site_name, :public_body_name=>CGI.escapeHTML(self.public_body.name)),
:email => _("Then you will be notified whenever someone requests something or gets a response from '{{public_body_name}}'.", :public_body_name=>CGI.escapeHTML(self.public_body.name)),
:email_subject => _("Confirm you want to follow requests to '{{public_body_name}}'", :public_body_name=>self.public_body.name),
# RSS sorting
:feed_sortby => 'newest'
}
elsif self.track_type == 'user_updates'
@params = {
# Website
:list_description => _("'{{link_to_user}}', a person", :link_to_user => ("" + CGI.escapeHTML(self.tracked_user.name) + "").html_safe), # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how
: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=>self.tracked_user.name.html_safe),
:title_in_rss => _("FOI requests by '{{user_name}}'", :user_name=>self.tracked_user.name),
# Authentication
:web => _("To follow requests by '{{user_name}}'", :user_name=>CGI.escapeHTML(self.tracked_user.name)),
:email => _("Then you will be notified whenever '{{user_name}}' requests something or gets a response.", :user_name=>CGI.escapeHTML(self.tracked_user.name)),
:email_subject => _("Confirm you want to follow requests by '{{user_name}}'", :user_name=>self.tracked_user.name),
# RSS sorting
:feed_sortby => 'newest'
}
elsif self.track_type == 'search_query'
@params = {
# Website
:list_description => ("" + CGI.escapeHTML(self.track_query_description) + "").html_safe, # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how
: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 - XXX 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'
}
else
raise "unknown tracking type " + self.track_type
end
end
return @params
end
# When constructing a new track, use this to avoid duplicates / double posting
def TrackThing.find_by_existing_track(tracking_user, track)
if tracking_user.nil?
return nil
end
return TrackThing.find(:first, :conditions => [ 'tracking_user_id = ? and track_query = ? and track_type = ?', tracking_user.id, track.track_query, track.track_type ] )
end
end