Skip to content

Instantly share code, notes, and snippets.

@data-doge
Last active March 29, 2016 04:42
  • Select an option

Select an option

Revisions

  1. data-doge revised this gist Mar 29, 2016. 6 changed files with 182 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions cobudget.rake
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,6 @@
    # ./lib/tasks/cobudget.rake

    # scheduled to run every 10 minutes on our heroku server
    namespace :cobudget do
    desc "delivers recent activity digest emails to users"
    task deliver_recent_activity_digest: :environment do
    16 changes: 16 additions & 0 deletions deliver_recent_activity_digest.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    # ./app/extras/deliver_recent_activity_digest.rb

    # goes through every user with active memberships, and
    # 1. sends them an email containing recent activity for their groups
    # 2. sets the time range for the next, scheduled fetch of recent activity
    class DeliverRecentActivityDigest
    def self.run!
    current_time = DateTime.now.utc
    User.with_active_memberships.each do |user|
    if user.subscription_tracker.ready_to_fetch?(current_time: current_time)
    UserService.send_recent_activity_email(user: user)
    user.subscription_tracker.update_next_fetch_time_range!
    end
    end
    end
    end
    53 changes: 53 additions & 0 deletions deliver_recent_activity_digest_spec.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,53 @@
    # ./spec/extras/deliver_recent_activity_digest_spec.rb

    require 'rails_helper'

    describe "DeliverRecentActivityDigest" do
    after { Timecop.return }

    describe "#run!" do
    before do
    allow(UserService).to receive(:send_recent_activity_email)
    end

    context "user has no active memberships" do
    before do
    create(:user)
    end

    it "does nothing" do
    expect(UserService).not_to receive(:send_recent_activity_email)
    DeliverRecentActivityDigest.run!
    end
    end

    context "user has at least one active membership" do
    before do
    make_user_group_member
    end

    context "current_time is before user's next scheduled fetch" do
    it "does nothing" do
    time = (user.subscription_tracker.next_recent_activity_fetch_scheduled_at - 1.minute).utc
    Timecop.freeze(time) do
    expect(UserService).not_to receive(:send_recent_activity_email)
    DeliverRecentActivityDigest.run!
    end
    end
    end

    context "current_time is after user's next scheduled fetch" do
    it "calls fetches activity for user and updates their subscription_tracker's next fetch_time_range" do
    old_next_recent_activity_scheduled_at = user.subscription_tracker.next_recent_activity_fetch_scheduled_at

    time = (old_next_recent_activity_scheduled_at + 1.minute).utc
    Timecop.freeze(time) do
    expect(UserService).to receive(:send_recent_activity_email)
    DeliverRecentActivityDigest.run!
    expect(user.subscription_tracker.reload.recent_activity_last_fetched_at).to eq(old_next_recent_activity_scheduled_at)
    end
    end
    end
    end
    end
    end
    54 changes: 54 additions & 0 deletions subscription_tracker.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,54 @@
    # ./app/models/subscription_tracker.rb

    class SubscriptionTracker < ActiveRecord::Base
    belongs_to :user
    after_update :update_recent_activity_last_fetched_at

    def next_recent_activity_fetch_scheduled_at
    interval = case notification_frequency
    when "hourly" then 1.hour
    when "daily" then 1.day
    when "weekly" then 1.week
    end
    recent_activity_last_fetched_at + interval if interval
    end

    def ready_to_fetch?(current_time: )
    notification_frequency != "never" && current_time >= next_recent_activity_fetch_scheduled_at
    end

    def subscribed_to_any_activity?
    comment_on_your_bucket || comment_on_bucket_you_participated_in || bucket_idea_created || bucket_started_funding || bucket_fully_funded || funding_for_your_bucket || funding_for_a_bucket_you_participated_in || your_bucket_fully_funded || recent_activity_last_fetched_at
    end

    def last_fetched_at_formatted
    case notification_frequency
    when "hourly" then recent_activity_last_fetched_at.in_time_zone((user.utc_offset || 0) / 60).strftime("%l:%M%P").strip
    when "daily" then "yesterday"
    when "weekly" then "last week"
    end
    end

    def next_fetch_time_range
    if recent_activity_last_fetched_at && next_recent_activity_fetch_scheduled_at
    recent_activity_last_fetched_at..next_recent_activity_fetch_scheduled_at
    end
    end

    def update_next_fetch_time_range!
    update(recent_activity_last_fetched_at: next_recent_activity_fetch_scheduled_at)
    end

    private
    def update_recent_activity_last_fetched_at
    if self.notification_frequency_changed?
    time_now = DateTime.now.utc
    datetime = case self.notification_frequency
    when "hourly" then time_now.beginning_of_hour.utc
    when "daily" then (time_now.in_time_zone((self.user.utc_offset || 0) / 60).beginning_of_day + 6.hours).utc
    when "weekly" then (time_now.in_time_zone((self.user.utc_offset || 0) / 60).beginning_of_week + 6.hours).utc
    end
    self.update_columns(recent_activity_last_fetched_at: datetime)
    end
    end
    end
    1 change: 1 addition & 0 deletions subscription_tracker_spec.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # ./spec/models/subscription_tracker.rb
    55 changes: 55 additions & 0 deletions user.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,55 @@
    # ./app/models/user.rb

    require 'securerandom'

    class User < ActiveRecord::Base

    devise :database_authenticatable, :registerable,
    :recoverable, :rememberable, :trackable, :validatable

    include DeviseTokenAuth::Concerns::User

    ...

    after_create :create_default_subscription_tracker

    has_many :groups, through: :memberships
    has_many :memberships, foreign_key: "member_id", dependent: :destroy
    has_one :subscription_tracker, dependent: :destroy
    has_many :allocations, dependent: :destroy
    has_many :comments, dependent: :destroy
    has_many :contributions, dependent: :destroy
    has_many :buckets, dependent: :destroy

    scope :with_active_memberships, -> { joins(:memberships).where(memberships: {archived_at: nil}) }

    validates :name, presence: true
    validates_format_of :email, :with => /\A[^@]+@([^@\.]+\.)+[^@\.]+\z/

    def name_and_email
    "#{name} <#{email}>"
    end

    ...

    def membership_for(group)
    memberships.find_by(group_id: group.id)
    end

    ...

    def active_groups
    Group.joins(:memberships).where(memberships: {member: self, archived_at: nil})
    end

    private
    ...

    def create_default_subscription_tracker
    SubscriptionTracker.create(
    user: self,
    notification_frequency: "hourly",
    recent_activity_last_fetched_at: DateTime.now.utc.beginning_of_hour
    )
    end
    end
  2. data-doge created this gist Mar 29, 2016.
    6 changes: 6 additions & 0 deletions cobudget.rake
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    namespace :cobudget do
    desc "delivers recent activity digest emails to users"
    task deliver_recent_activity_digest: :environment do
    DeliverRecentActivityDigest.run!
    end
    end