aboutsummaryrefslogtreecommitdiffstats
path: root/app/mailers/track_mailer.rb
blob: 1ddce4a7ebebd9fa4b3b11ab1787f8e80e154d95 (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
126
127
128
129
130
131
132
133
134
135
# 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/

class TrackMailer < ApplicationMailer
    def event_digest(user, email_about_things)
        @user, @email_about_things = 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)

        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.)

        mail(:from => contact_from_name_and_email,
             :to => user.name_and_email,
             :subject => _("Your {{site_name}} email alert", :site_name => site_name))
    end

    def contact_from_name_and_email
        "#{Configuration::track_sender_name} <#{Configuration::track_sender_email}>"
    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
        done_something = false
        now = Time.now()
        users = User.find(:all, :conditions => [ "last_daily_track_email < ?", now - 1.day ])
        if users.empty?
            return done_something
        end
        for user in users
            next if !user.should_be_emailed? || !user.receive_email_alerts

            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

                I18n.with_locale(user.get_locale) do
                    TrackMailer.event_digest(user, email_about_things).deliver
                end
            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!
            done_something = true
        end
        return done_something
    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