customer_usage.rb

 1# frozen_string_literal: true
 2
 3require "digest"
 4
 5require_relative "./usage_report"
 6
 7class CustomerUsage
 8	def initialize(customer_id)
 9		@customer_id = customer_id
10	end
11
12	def usage_report(range)
13		EMPromise.all([
14			messages_by_day(range),
15			minutes_by_day(range)
16		]).then do |args|
17			UsageReport.new(range, *args)
18		end
19	end
20
21	def expire_message_usage
22		today = Time.now.utc.to_date
23		REDIS.zremrangebylex(
24			"jmp_customer_outbound_messages-#{@customer_id}",
25			"-",
26			# Store message counts per day for 1 year
27			"[#{(today << 12).strftime('%Y%m%d')}"
28		)
29	end
30
31	def incr_message_usage(amount=1, body=nil)
32		today = Time.now.utc.to_date
33		EMPromise.all([
34			expire_message_usage,
35			REDIS.zincrby(
36				"jmp_customer_outbound_messages-#{@customer_id}",
37				amount,
38				today.strftime("%Y%m%d")
39			),
40			incr_body(amount, body)
41		]).then { |result| { today: result[1].to_i, body: result[2].to_i } }
42	end
43
44	def incr_body(amount, body)
45		return 0 unless body
46
47		hash = Digest::SHA2.hexdigest(body[0..100])
48
49		EMPromise.all([
50			REDIS.incrby("jmp_outbound_body-#{hash}", amount),
51			REDIS.expire("jmp_outbound_body-#{hash}", 180)
52		]).then(&:first)
53	end
54
55	def message_usage(range)
56		messages_by_day(range).then do |by_day|
57			by_day.values.sum
58		end
59	end
60
61	def messages_by_day(range)
62		EMPromise.all(range.first.downto(range.last).map { |day|
63			REDIS.zscore(
64				"jmp_customer_outbound_messages-#{@customer_id}",
65				day.strftime("%Y%m%d")
66			).then { |c| [day, c.to_i] if c }
67		}).then { |r| r.compact.to_h.tap { |h| h.default = 0 } }
68	end
69
70	QUERY_FOR_MINUTES = <<~SQL
71		SELECT
72			date_trunc('day', start)::date as day,
73			CEIL(SUM(billsec)/60.0)::integer as minutes
74		FROM cdr
75		WHERE customer_id=$1 and start >= $3 and date_trunc('day', start) <= $2
76		GROUP BY date_trunc('day', start);
77	SQL
78
79	def minutes_by_day(range)
80		DB.query_defer(
81			QUERY_FOR_MINUTES,
82			[@customer_id, range.first, range.last]
83		).then do |result|
84			result.each_with_object(Hash.new(0)) do |row, minutes|
85				minutes[row["day"]] = row["minutes"]
86			end
87		end
88	end
89
90	def calling_charges_this_month
91		DB.query_one(<<~SQL, @customer_id).then { |r| r[:charges] }
92			SELECT COALESCE(SUM(charge), 0) AS charges
93			FROM cdr_with_charge
94			WHERE customer_id=$1 AND start >= DATE_TRUNC('month', LOCALTIMESTAMP)
95		SQL
96	end
97end