aboutsummaryrefslogtreecommitdiffstats
path: root/app/models/track_mailer.rb
blob: 6901a834d6d25ef9cf4512b644a994ce65d6ed9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# models/track_mailer.rb:
# Emails which go to users who are tracking things.
#
# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
# Email: francis@mysociety.org; WWW: http://www.mysociety.org/
#
# $Id: track_mailer.rb,v 1.23 2009-10-03 02:50:11 francis Exp $

class TrackMailer < ApplicationMailer
    def event_digest(user, email_about_things)
        post_redirect = PostRedirect.new(
            :uri => main_url(user_url(user)) + "#email_subscriptions",
            :user_id => user.id)
        post_redirect.save!
        unsubscribe_url = confirm_url(:email_token => post_redirect.email_token)

        @from = contact_from_name_and_email
        headers 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834
                'Precedence' => 'bulk' # http://www.vbulletin.com/forum/project.php?issueid=27687 (Exchange hack)
        # 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces for tracks
        # (We let it return bounces for now, so we can manually kill the tracks that bounce so Yahoo
        # etc. don't decide we are spammers.)

        @recipients = user.name_and_email
        @subject = "Your WhatDoTheyKnow.com email alert"
        @body = { :user => user, :email_about_things => email_about_things, :unsubscribe_url => unsubscribe_url }
    end

    # Send email alerts for tracked things.  Never more than one email
    # a day, nor about events which are more than a week old, nor
    # events about which emails have been sent within the last two
    # weeks.

    # Useful query to run by hand to see how many alerts are due:
    #   User.find(:all, :conditions => [ "last_daily_track_email < ?", Time.now - 1.day ]).size
    def self.alert_tracks
        now = Time.now()
        users = User.find(:all, :conditions => [ "last_daily_track_email < ?", now - 1.day ])
        if users.empty?
            return false
        end
        for user in users
            email_about_things = []
            track_things = TrackThing.find(:all, :conditions => [ "tracking_user_id = ? and track_medium = ?", user.id, 'email_daily' ])
            for track_thing in track_things
                # What have we alerted on already?
                #
                # We only use track_things_sent_emails records which are less than 14 days old.
                # In the search query loop below, we also only use items described in last 7 days.
                # An item described that recently definitely can't appear in track_things_sent_emails 
                # earlier, so this is safe (with a week long margin of error). If the alerts break
                # for a whole week, then they will miss some items. Tough.
                done_info_request_events = {}
                tt_sent = track_thing.track_things_sent_emails.find(:all, :conditions => ['created_at > ?', now - 14.days])
                for t in tt_sent
                    if not t.info_request_event_id.nil?
                        done_info_request_events[t.info_request_event_id] = 1
                    end
                end

                # Query for things in this track. We use described_at for the
                # ordering, so we catch anything new (before described), or
                # anything whose new status has been described.
                xapian_object = InfoRequest.full_search([InfoRequestEvent], track_thing.track_query, 'described_at', true, nil, 100, 1) 
                # Go through looking for unalerted things
                alert_results = []
                for result in xapian_object.results
                    if result[:model].class.to_s != "InfoRequestEvent"
                        raise "need to add other types to TrackMailer.alert_tracks (unalerted)"
                    end

                    next if track_thing.created_at >= result[:model].described_at # made before the track was created
                    next if result[:model].described_at < now - 7.days # older than 1 week (see 14 days / 7 days in comment above)
                    next if done_info_request_events.include?(result[:model].id) # definitely already done

                    # OK alert this one
                    alert_results.push(result)
                end
                # If there were more alerts for this track, then store them
                if alert_results.size > 0 
                    email_about_things.push([track_thing, alert_results, xapian_object])
                end
            end

            # If we have anything to send, then send everything for the user in one mail
            if email_about_things.size > 0
                # Send the email
                TrackMailer.deliver_event_digest(user, email_about_things)
            end

            # Record that we've now sent those alerts to that user
            for track_thing, alert_results in email_about_things
                for result in alert_results
                    track_things_sent_email = TrackThingsSentEmail.new
                    track_things_sent_email.track_thing_id = track_thing.id
                    if result[:model].class.to_s == "InfoRequestEvent"
                        track_things_sent_email.info_request_event_id = result[:model].id
                    else
                        raise "need to add other types to TrackMailer.alert_tracks (mark alerted)"
                    end
                    track_things_sent_email.save!
                end
            end
            user.last_daily_track_email = now
            user.no_xapian_reindex = true
            user.save!
        end
        return true
    end

    def self.alert_tracks_loop
        # Run alert_tracks in an endless loop, sleeping when there is nothing to do
        while true
            sleep_seconds = 1
            while !alert_tracks
                sleep sleep_seconds
                sleep_seconds *= 2
                sleep_seconds = 300 if sleep_seconds > 300
            end
        end
    end

end