diff --git a/app/controllers/stat_created_plans_by_template_controller.rb b/app/controllers/stat_created_plans_by_template_controller.rb new file mode 100644 index 0000000..f48d758 --- /dev/null +++ b/app/controllers/stat_created_plans_by_template_controller.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class StatCreatedPlansByTemplateController < ApplicationController + + def index + check_authorized! + + data = StatCreatedPlan.monthly_range(index_filter).order(date: :desc) + + if params[:format] == "csv" + data_csvified = StatCreatedPlan.to_csv(data, details: { by_template: true }) + send_data(data_csvified, filename: "created_plan_by_template.csv") + else + render(json: data.as_json(only: [:date, :count], methods: :by_template)) + end + end + + private + + def index_filter + { + org: current_user.org, + start_date: params[:start_date], + end_date: params[:end_date] + } + end + + def check_authorized! + unless current_user.present? && + current_user.can_org_admin? + raise Pundit::NotAuthorizedError + end + end + +end diff --git a/app/controllers/usage_controller.rb b/app/controllers/usage_controller.rb index cb186f7..3ba84b4 100644 --- a/app/controllers/usage_controller.rb +++ b/app/controllers/usage_controller.rb @@ -4,6 +4,7 @@ def index check_authorized! + render("index", locals: { orgs: Org.all, diff --git a/app/javascript/views/usage/index.js b/app/javascript/views/usage/index.js index 9b7245a..e7ff4de 100644 --- a/app/javascript/views/usage/index.js +++ b/app/javascript/views/usage/index.js @@ -149,3 +149,77 @@ }; initialise(); }); + +$(() => { + const jQuerySelectorSelect = $('select[name=monthly_plans_by_template]'); + let drawnChart = null; + const randomRgb = () => { + const { round, random } = Math; + const max = 255; + const f = () => round(random() * max); + return `rgb(${f()},${f()},${f()})`; + }; + const yAxisLabel = date => moment(date).format('MMM-YY'); + + const drawHorizontalBar = (canvasSelector, data) => { + const chart = new Chart(canvasSelector, { // eslint-disable-line no-new + type: 'horizontalBar', + data, + options: { + scales: { + xAxes: [{ + ticks: { beginAtZero: true }, + precision: 1, + }], + }, + }, + }); + return chart; + }; + + const buildData = (data) => { + const labels = data.map(current => yAxisLabel(current.date)); + + const datasetsMap = data.reduce((acc, statCreatedPlan) => { + statCreatedPlan.by_template.forEach((template) => { + if (!acc[template.name]) { + acc[template.name] = { label: template.name, data: [], backgroundColor: randomRgb() }; + } + acc[template.name].data.push({ x: template.count, y: yAxisLabel(statCreatedPlan.date) }); + }); + return acc; + }, {}); + + const datasets = Object.keys(datasetsMap).map(key => datasetsMap[key]); + + return { labels, datasets }; + }; + + const fetch = (lastDayOfMonth) => { + const baseUrl = $('select[name="monthly_plans_by_template"]').attr('data-url'); + $.ajax({ + url: `${baseUrl}?start_date=${lastDayOfMonth}`, + }).then((data) => { + const chartData = buildData(data); + const canvasSelector = '#monthly_plans_by_template_canvas'; + if (drawnChart) { + drawnChart.destroy(); + } + drawnChart = drawHorizontalBar($(canvasSelector), chartData); + }); + }; + + const handler = () => { + const selectedMonth = jQuerySelectorSelect.val(); + if (selectedMonth) { + fetch(selectedMonth); + } + }; + + jQuerySelectorSelect.on('change', (e) => { + e.preventDefault(); + handler(); + }); + + handler(); +}); diff --git a/app/models/stat.rb b/app/models/stat.rb index acc61db..08b4f71 100644 --- a/app/models/stat.rb +++ b/app/models/stat.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # == Schema Information # # Table name: stats @@ -5,6 +7,7 @@ # id :integer not null, primary key # count :integer default(0) # date :date not null +# details :text # type :string not null # created_at :datetime not null # updated_at :datetime not null @@ -12,14 +15,22 @@ # class Stat < ActiveRecord::Base + + extend OrgDateRangeable + belongs_to :org + validates_uniqueness_of :type, scope: [:date, :org_id] + class << self + def to_csv(stats) data = stats.map do |stat| { date: stat.date, count: stat.count } end Csvable.from_array_of_hashes(data) end + end + end diff --git a/app/models/stat_created_plan.rb b/app/models/stat_created_plan.rb index 43a9f2a..0f0bd35 100644 --- a/app/models/stat_created_plan.rb +++ b/app/models/stat_created_plan.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # == Schema Information # # Table name: stats @@ -5,18 +7,64 @@ # id :integer not null, primary key # count :integer default(0) # date :date not null +# details :text # type :string not null # created_at :datetime not null # updated_at :datetime not null # org_id :integer # +require "set" + class StatCreatedPlan < Stat - extend OrgDateRangeable + + serialize :details, JSON + + def by_template + by_template = self.details["by_template"] + return [] unless by_template.present? + by_template + end class << self - def to_csv(created_plans) - Stat.to_csv(created_plans) + + def to_csv(created_plans, details: { by_template: false }) + if details[:by_template] + to_csv_by_template(created_plans) + else + super(created_plans) + end end + + private + + def to_csv_by_template(created_plans) + template_names = lambda do |created_plans| + unique = Set.new + created_plans.each do |created_plan| + created_plan.details&.fetch("by_template", [])&.each do |name_count| + unique.add(name_count.fetch("name")) + end + end + unique.to_a + end.call(created_plans) + + data = created_plans.map do |created_plan| + tuple = { date: created_plan.date } + template_names.reduce(tuple) do |acc, name| + acc[name] = 0 + acc + end + created_plan.details&.fetch("by_template", [])&.each do |name_count| + tuple[name_count.fetch("name")] = name_count.fetch("count") + end + tuple[:count] = created_plan.count + tuple + end + + Csvable.from_array_of_hashes(data) + end + end + end diff --git a/app/models/stat_created_plan/create_or_update.rb b/app/models/stat_created_plan/create_or_update.rb new file mode 100644 index 0000000..d312499 --- /dev/null +++ b/app/models/stat_created_plan/create_or_update.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +class StatCreatedPlan + + class CreateOrUpdate + + class << self + + def do(start_date:, end_date:, org:) + count = count_plans(start_date: start_date, end_date: end_date, org: org) + by_template = by_template(start_date: start_date, end_date: end_date, org: org) + attrs = { + date: end_date.to_date, + org_id: org.id, + count: count, + details: { by_template: by_template } + } + stat_created_plan = StatCreatedPlan.find_by( + date: attrs[:date], + org_id: attrs[:org_id] + ) + + if stat_created_plan.present? + stat_created_plan.update(attrs) + else + StatCreatedPlan.create(attrs) + end + end + + private + + def users(org) + User.where(users: { org_id: org.id }) + end + + def plans(start_date:, end_date:) + Plan.where(plans: { created_at: start_date..end_date }) + end + + def creator_admin + Role.with_access_flags(:creator, :administrator) + end + + def count_plans(start_date:, end_date:, org:) + users = users(org) + plans = plans(start_date: start_date, end_date: end_date) + + Role.joins([:plan, :user]) + .merge(creator_admin) + .merge(users) + .merge(plans) + .select(:plan_id) + .distinct + .count + end + + def by_template(start_date:, end_date:, org:) + users = users(org) + plans = plans(start_date: start_date, end_date: end_date) + roleable_plan_ids = Role.joins([:plan, :user]) + .merge(creator_admin) + .merge(users) + .merge(plans) + .select(:plan_id) + .distinct + template_counts = Plan.where(id: roleable_plan_ids).group(:template_id).count + template_names = Template.where(id: template_counts.keys).pluck(:id, :title) + template_names.map do |t| + { name: t[1], count: template_counts[t[0]] } + end + end + + end + + end + +end diff --git a/app/models/stat_joined_user.rb b/app/models/stat_joined_user.rb index ec9820b..62dd3b1 100644 --- a/app/models/stat_joined_user.rb +++ b/app/models/stat_joined_user.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # == Schema Information # # Table name: stats @@ -5,18 +7,21 @@ # id :integer not null, primary key # count :integer default(0) # date :date not null +# details :text # type :string not null # created_at :datetime not null # updated_at :datetime not null # org_id :integer # -class StatJoinedUser < Stat - extend OrgDateRangeable +class StatJoinedUser < Stat class << self + def to_csv(joined_users) Stat.to_csv(joined_users) end + end + end diff --git a/app/models/stat_joined_user/create_or_update.rb b/app/models/stat_joined_user/create_or_update.rb new file mode 100644 index 0000000..46117d8 --- /dev/null +++ b/app/models/stat_joined_user/create_or_update.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class StatJoinedUser + + class CreateOrUpdate + + class << self + + def do(start_date:, end_date:, org:) + count = count_users(start_date: start_date, end_date: end_date, org_id: org.id) + attrs = { date: end_date.to_date, count: count, org_id: org.id } + + stat_joined_user = StatJoinedUser.find_by( + date: attrs[:date], + org_id: attrs[:org_id] + ) + + if stat_joined_user.present? + stat_joined_user.update(attrs) + else + StatJoinedUser.create(attrs) + end + end + + private + + def count_users(start_date:, end_date:, org_id:) + User.where(created_at: start_date..end_date, org_id: org_id).count + end + + end + + end + +end diff --git a/app/services/org/create_created_plan_service.rb b/app/services/org/create_created_plan_service.rb index 842382a..c668a77 100644 --- a/app/services/org/create_created_plan_service.rb +++ b/app/services/org/create_created_plan_service.rb @@ -1,30 +1,27 @@ +# frozen_string_literal: true + class Org + class CreateCreatedPlanService + class << self + def call(org = nil) orgs = org.nil? ? Org.all : [org] orgs.each do |org| OrgDateRangeable.split_months_from_creation(org) do |start_date, end_date| - create_count_for_date(start_date: start_date, end_date: end_date, org: org) + StatCreatedPlan::CreateOrUpdate.do( + start_date: start_date, + end_date: end_date, + org: org + ) end end end - private - - def count_plans(start_date: , end_date: , org:) - users = User.where('users.org_id = ?', org.id) - plans = Plan.where('plans.created_at >= ? AND plans.created_at <= ?', start_date, end_date) - creator_admon = Role.with_access_flags(:creator, :administrator) - - Role.joins([:plan, :user]).merge(creator_admon).merge(users).merge(plans).select(:plan_id).distinct.count - end - - def create_count_for_date(start_date:, end_date:, org:) - count = count_plans(start_date: start_date, end_date: end_date, org: org) - StatCreatedPlan.create(date: end_date.to_date, count: count, org_id: org.id) - end end + end + end diff --git a/app/services/org/create_joined_user_service.rb b/app/services/org/create_joined_user_service.rb index 02dbbb2..e4842eb 100644 --- a/app/services/org/create_joined_user_service.rb +++ b/app/services/org/create_joined_user_service.rb @@ -1,25 +1,26 @@ +# frozen_string_literal: true + class Org + class CreateJoinedUserService + class << self + def call(org = nil) - orgs = org.nil? ? ::Org.all : [org] + orgs = org.nil? ? Org.all : [org] orgs.each do |org| OrgDateRangeable.split_months_from_creation(org) do |start_date, end_date| - create_count_for_date(start_date: start_date, end_date: end_date, org: org) + StatJoinedUser::CreateOrUpdate.do( + start_date: start_date, + end_date: end_date, + org: org + ) end end end - private - - def count_users(start_date: , end_date: , org_id: ) - User.where('created_at >= ? AND created_at <= ? AND org_id = ?', start_date, end_date, org_id).count - end - - def create_count_for_date(start_date:, end_date:, org:) - count = count_users(start_date: start_date, end_date: end_date, org_id: org.id) - ::StatJoinedUser.create(date: end_date.to_date, count: count, org_id: org.id) - end end + end + end diff --git a/app/services/org/create_last_month_created_plan_service.rb b/app/services/org/create_last_month_created_plan_service.rb index 3f42100..8093862 100644 --- a/app/services/org/create_last_month_created_plan_service.rb +++ b/app/services/org/create_last_month_created_plan_service.rb @@ -1,32 +1,29 @@ +# frozen_string_literal: true + class Org + class CreateLastMonthCreatedPlanService + class << self + def call(org = nil) - orgs = org.nil? ? ::Org.all : [org] + orgs = org.nil? ? Org.all : [org] orgs.each do |org| months = OrgDateRangeable.split_months_from_creation(org) last = months.last if last.present? - create_count_for_date(start_date: last[:start_date], end_date: last[:end_date], org: org) + StatCreatedPlan::CreateOrUpdate.do( + start_date: last[:start_date], + end_date: last[:end_date], + org: org + ) end end end - private - - def count_plans(start_date: , end_date: , org:) - users = User.where('users.org_id = ?', org.id) - plans = Plan.where('plans.created_at >= ? AND plans.created_at <= ?', start_date, end_date) - creator_admon = Role.with_access_flags(:creator, :administrator) - - Role.joins([:plan, :user]).merge(creator_admon).merge(users).merge(plans).select(:plan_id).distinct.count - end - - def create_count_for_date(start_date:, end_date:, org:) - count = count_plans(start_date: start_date, end_date: end_date, org: org) - ::StatCreatedPlan.create(date: end_date.to_date, count: count, org_id: org.id) - end end + end + end diff --git a/app/services/org/create_last_month_joined_user_service.rb b/app/services/org/create_last_month_joined_user_service.rb index 58817d2..026dfd8 100644 --- a/app/services/org/create_last_month_joined_user_service.rb +++ b/app/services/org/create_last_month_joined_user_service.rb @@ -1,27 +1,28 @@ +# frozen_string_literal: true + class Org + class CreateLastMonthJoinedUserService + class << self + def call(org = nil) orgs = org.nil? ? ::Org.all : [org] orgs.each do |org| months = OrgDateRangeable.split_months_from_creation(org) last = months.last if last.present? - create_count_for_date(start_date: last[:start_date], end_date: last[:end_date], org: org) + StatJoinedUser::CreateOrUpdate.do( + start_date: last[:start_date], + end_date: last[:end_date], + org: org + ) end end end - private - - def count_users(start_date: , end_date: , org_id: ) - User.where('created_at >= ? AND created_at <= ? AND org_id = ?', start_date, end_date, org_id).count - end - - def create_count_for_date(start_date:, end_date:, org:) - count = count_users(start_date: start_date, end_date: end_date, org_id: org.id) - ::StatJoinedUser.create(date: end_date.to_date, count: count, org_id: org.id) - end end + end + end diff --git a/app/services/org/total_count_created_plan_service.rb b/app/services/org/total_count_created_plan_service.rb index 41d8a4e..b5f7720 100644 --- a/app/services/org/total_count_created_plan_service.rb +++ b/app/services/org/total_count_created_plan_service.rb @@ -1,6 +1,11 @@ +# frozen_string_literal: true + class Org + class TotalCountCreatedPlanService + class << self + def call(org = nil) return for_orgs unless org.present? for_org(org) @@ -9,7 +14,11 @@ private def for_orgs - result = ::StatCreatedPlan.includes(:org).select(:"orgs.name", :count).group(:"orgs.name").sum(:count) + result = ::StatCreatedPlan + .includes(:org) + .select(:"orgs.name", :count) + .group(:"orgs.name") + .sum(:count) result.each_pair.map do |pair| build_model(org_name: pair[0], count: pair[1].to_i) end @@ -20,9 +29,12 @@ build_model(org_name: org.name, count: result) end - def build_model(org_name: , count: ) + def build_model(org_name:, count:) { org_name: org_name, count: count } end + end + end + end diff --git a/app/services/org/total_count_joined_user_service.rb b/app/services/org/total_count_joined_user_service.rb index 0f63d41..21efde4 100644 --- a/app/services/org/total_count_joined_user_service.rb +++ b/app/services/org/total_count_joined_user_service.rb @@ -1,6 +1,11 @@ +# frozen_string_literal: true + class Org + class TotalCountJoinedUserService + class << self + def call(org = nil) return for_orgs unless org.present? for_org(org) @@ -9,7 +14,11 @@ private def for_orgs - result = ::StatJoinedUser.includes(:org).select(:"orgs.name", :count).group(:"orgs.name").sum(:count) + result = ::StatJoinedUser + .includes(:org) + .select(:"orgs.name", :count) + .group(:"orgs.name") + .sum(:count) result.each_pair.map do |pair| build_model(org_name: pair[0], count: pair[1].to_i) end @@ -20,9 +29,12 @@ build_model(org_name: org.name, count: result) end - def build_model(org_name: , count: ) + def build_model(org_name:, count:) { org_name: org_name, count: count } end + end + end + end diff --git a/app/services/org/total_count_stat_service.rb b/app/services/org/total_count_stat_service.rb index 064a466..4a3466b 100644 --- a/app/services/org/total_count_stat_service.rb +++ b/app/services/org/total_count_stat_service.rb @@ -1,6 +1,11 @@ +# frozen_string_literal: true + class Org + class TotalCountStatService + class << self + def call total = build_from_joined_user build_from_created_plan(total) @@ -45,6 +50,9 @@ reducer_body(acc, count, :total_plans) end end + end + end + end diff --git a/app/views/usage/index.html.erb b/app/views/usage/index.html.erb index dd356b0..8152720 100644 --- a/app/views/usage/index.html.erb +++ b/app/views/usage/index.html.erb @@ -132,4 +132,38 @@ +
+
+

<%= _('No. plans by template') %>

+
+
+
+
+
    +
  • +
    + <%= label_tag('monthly_plans_by_template', _('Time picker')) %> + +
    +
  • +
  • + +
  • +
+
+
+ +
+
+
diff --git a/config/routes.rb b/config/routes.rb index b7eb668..d28e94a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -127,6 +127,8 @@ resources :usage_downloads, only: [:index] + resources :stat_created_plans_by_template, only: [:index] + resources :roles, only: [:create, :update, :destroy] do member do put :deactivate diff --git a/db/migrate/20181025220743_add_details_to_stats.rb b/db/migrate/20181025220743_add_details_to_stats.rb new file mode 100644 index 0000000..506161d --- /dev/null +++ b/db/migrate/20181025220743_add_details_to_stats.rb @@ -0,0 +1,5 @@ +class AddDetailsToStats < ActiveRecord::Migration + def change + add_column :stats, :details, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index 96d379c..0b0529e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20181024120747) do +ActiveRecord::Schema.define(version: 20181025220743) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -326,6 +326,7 @@ t.integer "org_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.text "details" end create_table "templates", force: :cascade do |t| diff --git a/lib/csvable.rb b/lib/csvable.rb index f9f5627..867a145 100644 --- a/lib/csvable.rb +++ b/lib/csvable.rb @@ -3,8 +3,8 @@ module Csvable require "csv" - class << self + def from_array_of_hashes(data = []) return "" unless data.first&.keys headers = data.first.keys @@ -17,6 +17,7 @@ end end end + end end diff --git a/lib/org_date_rangeable.rb b/lib/org_date_rangeable.rb index 67c96e5..08739e1 100644 --- a/lib/org_date_rangeable.rb +++ b/lib/org_date_rangeable.rb @@ -1,13 +1,31 @@ +# frozen_string_literal: true + module OrgDateRangeable - def monthly_range(org:, start_date: Date.today.end_of_month, end_date: Date.today.end_of_month) - where("org_id = :org_id and date >= :start_date and date <= :end_date", org_id: org&.id, start_date: start_date, end_date: end_date) + + def monthly_range(org:, start_date: nil, end_date: Date.today.end_of_month) + query_string = "org_id = :org_id" + query_hash = { org_id: org.id } + + unless start_date.nil? + query_string += " and date >= :start_date" + query_hash[:start_date] = start_date + end + + unless end_date.nil? + query_string += " and date <= :end_date" + query_hash[:end_date] = end_date + end + where(query_string, query_hash) end class << self + def split_months_from_creation(org, &block) starts_at = org.created_at ends_at = starts_at.end_of_month - callable = block.nil? ? Proc.new {} : lambda{ | start_date, end_date| block.call(start_date, end_date) } + callable = block.nil? ? + Proc.new {} : + lambda { | start_date, end_date| block.call(start_date, end_date) } enumerable = [] while !(starts_at.future? || ends_at.future?) do @@ -19,5 +37,7 @@ enumerable end + end + end diff --git a/spec/models/stat_created_plan_spec.rb b/spec/models/stat_created_plan_spec.rb index edf322e..d1a0a9c 100644 --- a/spec/models/stat_created_plan_spec.rb +++ b/spec/models/stat_created_plan_spec.rb @@ -11,20 +11,66 @@ end context 'when instances' do let(:org) { FactoryBot.create(:org) } - it 'returns instances in a comma-separated row' do - may = FactoryBot.create(:stat_created_plan, date: Date.new(2018, 05, 31), org: org, count: 20) - june = FactoryBot.create(:stat_created_plan, date: Date.new(2018, 06, 30), org: org, count: 10) - data = [may, june] - csv = described_class.to_csv(data) + context 'when no details' do + it 'returns counts in a comma-separated row' do + may = FactoryBot.create(:stat_created_plan, date: Date.new(2018, 05, 31), org: org, count: 20) + june = FactoryBot.create(:stat_created_plan, date: Date.new(2018, 06, 30), org: org, count: 10) + data = [may, june] - expected_csv = <<~HERE + csv = described_class.to_csv(data) + + expected_csv = <<~HERE Date,Count 2018-05-31,20 2018-06-30,10 - HERE - expect(csv).to eq(expected_csv) + HERE + expect(csv).to eq(expected_csv) + end end + + context 'when details by template is true' do + it 'returns counts by_template in a comma-separated row' do + may = FactoryBot.create(:stat_created_plan, date: Date.new(2018, 05, 31), org: org, count: 20, details: { by_template: [ + { name: 'Template1', count: 5 }, + { name: 'Template2', count: 15 } + ]}) + june = FactoryBot.create(:stat_created_plan, date: Date.new(2018, 06, 30), org: org, count: 10, details: { by_template: [ + { name: 'Template1', count: 2 }, + { name: 'Template3', count: 8 } + ]}) + july = FactoryBot.create(:stat_created_plan, date: Date.new(2018, 07, 31), org: org, count: 0) + data = [may, june, july] + + csv = described_class.to_csv(data, details: { by_template: true }) + + expected_csv = <<~HERE + Date,Template1,Template2,Template3,Count + 2018-05-31,5,15,0,20 + 2018-06-30,2,0,8,10 + 2018-07-31,0,0,0,0 + HERE + expect(csv).to eq(expected_csv) + end + end + end + end + + describe '.serialize' do + let(:org) { FactoryBot.create(:org, name: 'An Org', contact_email: 'foo@bar.com', contact_name: 'Foo') } + let(:details) do + { 'by_template' => [ + { 'name' => 'Template 1', 'count' => 10 }, + { 'name' => 'Template 2', 'count' => 10 } + ]} + end + + it 'retrieves JSON details as a hash object' do + september = FactoryBot.create(:stat_created_plan, date: '2018-09-30', org: org, count: 20, details: details) + + json_details = described_class.find_by_date('2018-09-30').details + + expect(json_details).to eq(details) end end end diff --git a/spec/models/stat_joined_user_spec.rb b/spec/models/stat_joined_user_spec.rb index 022358a..59b06c3 100644 --- a/spec/models/stat_joined_user_spec.rb +++ b/spec/models/stat_joined_user_spec.rb @@ -5,27 +5,6 @@ @org = FactoryBot.create(:org) end - describe '.monthly_range' do - context 'when org is missing' do - it 'raises ArgumentError' do - expect do - described_class.monthly_range - end.to raise_error(ArgumentError) - end - end - it 'returns matching instances' do - start_date = Date.new(2018, 04, 30) - end_date = Date.new(2018, 05, 31) - june = FactoryBot.create(:stat_joined_user, date: Date.new(2018, 06, 30), org: @org) - may = FactoryBot.create(:stat_joined_user, date: end_date, org: @org) - april = FactoryBot.create(:stat_joined_user, date: start_date, org: @org) - - april_to_may = described_class.monthly_range(org: @org, start_date: start_date, end_date: end_date) - - expect(april_to_may).to include(april, may) - end - end - describe '.to_csv' do context 'when no instances' do it 'returns empty' do diff --git a/spec/org_date_rangeable_spec.rb b/spec/org_date_rangeable_spec.rb index 70c00ca..8d3e929 100644 --- a/spec/org_date_rangeable_spec.rb +++ b/spec/org_date_rangeable_spec.rb @@ -1,11 +1,57 @@ require 'rails_helper' RSpec.describe OrgDateRangeable do - describe '.split_months_from_creation' do - let(:org) do - FactoryBot.create(:org, created_at: DateTime.new(2018,05,28,0,0,0)) + let(:org) do + FactoryBot.create(:org, created_at: DateTime.new(2018,05,28,0,0,0)) + end + + describe '.monthly_range' do + context 'when org keyword param is missing' do + it 'returns ArgumentError' do + expect do + StatJoinedUser.monthly_range + end.to raise_error(ArgumentError, /missing keyword: org/) + end end + context 'when start_date is nil' do + it 'returns every record whose date <= end_date' do + FactoryBot.create(:stat_joined_user, date: '2018-06-30', org: org, count: 10) + FactoryBot.create(:stat_joined_user, date: '2018-07-31', org: org, count: 10) + + result = StatJoinedUser.monthly_range(org: org, end_date: '2018-06-30') + + expected_result = StatJoinedUser.where(org: org, date: '2018-06-30') + expect(result.map(&:attributes)).to eq(expected_result.map(&:attributes)) + end + end + + context 'when end_date is nil' do + it 'returns every record whose date >= start_date' do + FactoryBot.create(:stat_joined_user, date: '2018-06-30', org: org, count: 10) + FactoryBot.create(:stat_joined_user, date: '2018-07-31', org: org, count: 10) + + result = StatJoinedUser.monthly_range(org: org, start_date: '2018-07-31') + + expected_result = StatJoinedUser.where(org: org, date: '2018-07-31') + expect(result.map(&:attributes)).to eq(expected_result.map(&:attributes)) + end + end + + context 'when all keyword are passed' do + it 'returns every record within start_date and end_date' do + FactoryBot.create(:stat_joined_user, date: '2018-06-30', org: org, count: 10) + FactoryBot.create(:stat_joined_user, date: '2018-07-31', org: org, count: 10) + + result = StatJoinedUser.monthly_range(org: org, start_date: '2018-06-30', end_date: '2018-07-31') + + expected_result = StatJoinedUser.where('org_id = ? and date >= ? and date <= ?', org.id, '2018-06-30', '2018-07-31') + expect(result.map(&:attributes)).to eq(expected_result.map(&:attributes)) + end + end + end + + describe '.split_months_from_creation' do it "starts at org's created_at" do expected_date = DateTime.new(2018,05,28,0,0,0) diff --git a/spec/requests/stat_created_plans_by_template_controller_spec.rb b/spec/requests/stat_created_plans_by_template_controller_spec.rb new file mode 100644 index 0000000..e8d9ce4 --- /dev/null +++ b/spec/requests/stat_created_plans_by_template_controller_spec.rb @@ -0,0 +1,104 @@ +require 'rails_helper' + +RSpec.describe '/stat_created_plan_by_template', type: :request do + def parsed_response + JSON.parse(response.body, symbolize_names: true) + end + + describe '#index' do + let(:path) { '/stat_created_plans_by_template' } + + it 'redirects when non-authorized user' do + get path + + expect(response).to have_http_status(:redirect) + end + + context 'when org_admin user' do + let(:org) { create(:org) } + let(:org_admin) { create(:user, :org_admin, org: org) } + before(:each) do + sign_in(org_admin) + end + + it 'returns 200 status' do + get path + + expect(response.content_type).to eq('application/json') + expect(response).to have_http_status(:ok) + end + + context 'when there are no stats' do + it 'returns empty' do + get path + + expect(parsed_response).to eq([]) + end + + it 'returns empty csv file' do + get "#{path}.csv" + + expect(response.content_type).to eq('text/csv') + expect(response.body).to eq('') + end + end + + context "when there are stats" do + before do + create(:stat_created_plan, date: '2018-07-31', count: 5, org: org, details: { by_template: [{ name: 'Template1', count: 3 }, { name: 'Template2', count: 2 }]}) + create(:stat_created_plan, date: '2018-08-31', count: 10, org: org, details: { by_template: [{ name: 'Template1', count: 6 }, { name: 'Template2', count: 4 }]}) + create(:stat_created_plan, date: '2018-09-30', count: 10, org: org, details: { by_template: [{ name: 'Template1', count: 6 }, { name: 'Template2', count: 4 }]}) + end + + it "returns all stats" do + get path + + expect(parsed_response).to eq([ + { date: '2018-09-30', count: 10, by_template: [{ name: 'Template1', count: 6 }, { name: 'Template2', count: 4 }]}, + { date: '2018-08-31', count: 10, by_template: [{ name: 'Template1', count: 6 }, { name: 'Template2', count: 4 }]}, + { date: '2018-07-31', count: 5, by_template: [{ name: 'Template1', count: 3 }, { name: 'Template2', count: 2 }]} + ]) + end + + it 'returns all stats csv formatted' do + get "#{path}.csv" + + expected_csv = <<~HERE + Date,Template1,Template2,Count + 2018-09-30,6,4,10 + 2018-08-31,6,4,10 + 2018-07-31,3,2,5 + HERE + expect(response.body).to eq(expected_csv) + end + + it 'returns stats for start_date and end_date passed' do + get path, { start_date: '2018-08-31', end_date: '2018-09-30' } + + expect(parsed_response).to eq([ + { date: '2018-09-30', count: 10, by_template: [{ name: 'Template1', count: 6 }, { name: 'Template2', count: 4 }]}, + { date: '2018-08-31', count: 10, by_template: [{ name: 'Template1', count: 6 }, { name: 'Template2', count: 4 }]} + ]) + end + + it 'returns stats from start_date passed' do + get path, { start_date: '2018-08-31' } + + expect(parsed_response).to eq([ + { date: '2018-09-30', count: 10, by_template: [{ name: 'Template1', count: 6 }, { name: 'Template2', count: 4 }]}, + { date: '2018-08-31', count: 10, by_template: [{ name: 'Template1', count: 6 }, { name: 'Template2', count: 4 }]}, + ]) + end + + it 'returns stats until end_date passed' do + get path, { end_date: '2018-08-31' } + + expect(parsed_response).to eq([ + { date: '2018-08-31', count: 10, by_template: [{ name: 'Template1', count: 6 }, { name: 'Template2', count: 4 }]}, + { date: '2018-07-31', count: 5, by_template: [{ name: 'Template1', count: 3 }, { name: 'Template2', count: 2 }]} + ]) + end + end + end + end +end diff --git a/spec/services/org/create_created_plan_service_spec.rb b/spec/services/org/create_created_plan_service_spec.rb index 2f83bae..19a2199 100644 --- a/spec/services/org/create_created_plan_service_spec.rb +++ b/spec/services/org/create_created_plan_service_spec.rb @@ -7,6 +7,9 @@ let(:template) do FactoryBot.create(:template, org: org) end + let(:template2) do + FactoryBot.create(:template, org: org) + end let(:user1) do FactoryBot.create(:user, org: org) end @@ -15,56 +18,153 @@ end let(:creator) { Role.access_values_for(:creator).first } let(:administrator) { Role.access_values_for(:administrator).first } + before(:each) do + plan = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,04,01)) + plan2 = FactoryBot.create(:plan, template: template2, created_at: DateTime.new(2018,04,03)) + plan3 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,05,02)) + plan4 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,06,02)) + plan5 = FactoryBot.create(:plan, template: template2, created_at: DateTime.new(2018,06,03)) + FactoryBot.create(:role, plan: plan, user: user1, access: creator) + FactoryBot.create(:role, plan: plan, user: user2, access: administrator) + FactoryBot.create(:role, plan: plan2, user: user1, access: creator) + FactoryBot.create(:role, plan: plan3, user: user1, access: creator) + FactoryBot.create(:role, plan: plan4, user: user2, access: administrator) + FactoryBot.create(:role, plan: plan5, user: user2, access: administrator) + end + + def find_by_dates(dates: , org_id:) + dates.map do |date| + StatCreatedPlan.find_by(date: date, org_id: org_id) + end + end describe '.call' do context 'when org is passed' do - it "generates monthly aggregates since org's creation" do - plan = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,04,01)) - plan2 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,04,03)) - plan3 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,05,02)) - plan4 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,06,02)) - plan5 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,06,03)) - FactoryBot.create(:role, plan: plan, user: user1, access: creator) - FactoryBot.create(:role, plan: plan, user: user2, access: administrator) - FactoryBot.create(:role, plan: plan2, user: user1, access: creator) - FactoryBot.create(:role, plan: plan3, user: user1, access: creator) - FactoryBot.create(:role, plan: plan4, user: user2, access: administrator) - FactoryBot.create(:role, plan: plan5, user: user2, access: administrator) + it "generates monthly counts since org's creation" do + described_class.call(org) + + april, may, june, july = find_by_dates(dates: ['2018-04-30', '2018-05-31', '2018-06-30', '2018-07-31'], org_id: org.id) + counts = [april, may, june, july].map(&:count) + expect(counts).to eq([2,1,2,0]) + end + + it "generates monthly counts by template since org's creation" do + described_class.call(org) + + april, may, june, july = find_by_dates(dates: ['2018-04-30', '2018-05-31', '2018-06-30', '2018-07-31'], org_id: org.id) + expect(april.details).to eq( + { + 'by_template' => [ + { 'name' => template.title, 'count' => 1 }, + { 'name' => template2.title, 'count' => 1 }, + ] + } + ) + expect(may.details).to eq( + { + 'by_template' => [ + { 'name' => template.title, 'count' => 1 }, + ] + } + ) + expect(june.details).to eq( + { + 'by_template' => [ + { 'name' => template.title, 'count' => 1 }, + { 'name' => template2.title, 'count' => 1 }, + ] + } + ) + expect(july.details).to eq( + { + 'by_template' => [] + } + ) + end + + it "monthly records are either created or updated" do + described_class.call(org) + + april = StatCreatedPlan.where(date: '2018-04-30', org: org) + expect(april).to have(1).items + expect(april.first.count).to eq(2) + + new_plan = FactoryBot.create(:plan, template: template2, created_at: DateTime.new(2018,04,03)) + FactoryBot.create(:role, plan: new_plan, user: user1, access: creator) described_class.call(org) - april = StatCreatedPlan.find_by(date: '2018-04-30', org_id: org.id).count - may = StatCreatedPlan.find_by(date: '2018-05-31', org_id: org.id).count - june = StatCreatedPlan.find_by(date: '2018-06-30', org_id: org.id).count - july = StatCreatedPlan.find_by(date: '2018-07-31', org_id: org.id).count - - expect([april, may, june, july]).to eq([2,1,2,0]) + april = StatCreatedPlan.where(date: '2018-04-30', org: org) + expect(april).to have(1).items + expect(april.first.count).to eq(3) end end context 'when no org is passed' do - it 'generates monthly aggregates for each org since their creation' do + it 'generates monthly counts for each org since their creation' do Org.stubs(:all).returns([org]) - plan = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,04,01)) - plan2 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,04,03)) - plan3 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,05,02)) - plan4 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,06,02)) - plan5 = FactoryBot.create(:plan, template: template, created_at: DateTime.new(2018,06,03)) - FactoryBot.create(:role, plan: plan, user: user1, access: creator) - FactoryBot.create(:role, plan: plan, user: user2, access: administrator) - FactoryBot.create(:role, plan: plan2, user: user1, access: creator) - FactoryBot.create(:role, plan: plan3, user: user1, access: creator) - FactoryBot.create(:role, plan: plan4, user: user2, access: administrator) - FactoryBot.create(:role, plan: plan5, user: user2, access: administrator) described_class.call - april = StatCreatedPlan.find_by(date: '2018-04-30', org_id: org.id).count - may = StatCreatedPlan.find_by(date: '2018-05-31', org_id: org.id).count - june = StatCreatedPlan.find_by(date: '2018-06-30', org_id: org.id).count - july = StatCreatedPlan.find_by(date: '2018-07-31', org_id: org.id).count + april, may, june, july = find_by_dates(dates: ['2018-04-30', '2018-05-31', '2018-06-30', '2018-07-31'], org_id: org.id) - expect([april, may, june, july]).to eq([2,1,2,0]) + counts = [april, may, june, july].map(&:count) + expect(counts).to eq([2,1,2,0]) + end + + it 'generates montly counts by template for each org since their creation' do + Org.stubs(:all).returns([org]) + + described_class.call + + april, may, june, july = find_by_dates(dates: ['2018-04-30', '2018-05-31', '2018-06-30', '2018-07-31'], org_id: org.id) + expect(april.details).to eq( + { + 'by_template' => [ + { 'name' => template.title, 'count' => 1 }, + { 'name' => template2.title, 'count' => 1 }, + ] + } + ) + expect(may.details).to eq( + { + 'by_template' => [ + { 'name' => template.title, 'count' => 1 }, + ] + } + ) + expect(june.details).to eq( + { + 'by_template' => [ + { 'name' => template.title, 'count' => 1 }, + { 'name' => template2.title, 'count' => 1 }, + ] + } + ) + expect(july.details).to eq( + { + 'by_template' => [] + } + ) + end + + it "monthly records are either created or updated" do + Org.stubs(:all).returns([org]) + + described_class.call + + april = StatCreatedPlan.where(date: '2018-04-30', org: org) + expect(april).to have(1).items + expect(april.first.count).to eq(2) + + new_plan = FactoryBot.create(:plan, template: template2, created_at: DateTime.new(2018,04,03)) + FactoryBot.create(:role, plan: new_plan, user: user1, access: creator) + + described_class.call + + april = StatCreatedPlan.where(date: '2018-04-30', org: org) + expect(april).to have(1).items + expect(april.first.count).to eq(3) end end end diff --git a/spec/services/org/create_joined_user_service_spec.rb b/spec/services/org/create_joined_user_service_spec.rb index 5f6611a..3faab7c 100644 --- a/spec/services/org/create_joined_user_service_spec.rb +++ b/spec/services/org/create_joined_user_service_spec.rb @@ -4,37 +4,65 @@ let(:org) do FactoryBot.create(:org, created_at: DateTime.new(2018,04,01)) end + before(:each) do + FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,04,03,0,0,0)) + FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,04,04,0,0,0)) + FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,05,03,0,0,0)) + FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,06,03,0,0,0)) + FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,06,04,0,0,0)) + end + + def find_by_dates(dates: , org_id:) + dates.map do |date| + StatJoinedUser.find_by(date: date, org_id: org_id) + end + end describe '.call' do context 'when an org is passed' do it "generates monthly aggregates since org's creation" do - april = [FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,04,03,0,0,0)), FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,04,04,0,0,0))] - may = [FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,05,03,0,0,0))] - june = [FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,06,03,0,0,0)), FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,06,04,0,0,0))] + described_class.call(org) + + april, may, june, july = find_by_dates(dates: ['2018-04-30', '2018-05-31', '2018-06-30', '2018-07-31'], org_id: org.id) + counts = [april, may, june, july].map(&:count) + expect(counts).to eq([2,1,2,0]) + end + + it 'monthly records are either created or updated' do + described_class.call(org) + + april_updated = FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,04,05,0,0,0)) described_class.call(org) - april = StatJoinedUser.find_by(date: '2018-04-30', org_id: org.id).count - may = StatJoinedUser.find_by(date: '2018-05-31', org_id: org.id).count - june = StatJoinedUser.find_by(date: '2018-06-30', org_id: org.id).count - july = StatJoinedUser.find_by(date: '2018-07-31', org_id: org.id).count - expect([april, may, june, july]).to eq([2,1,2,0]) + stat_joined_user = StatJoinedUser.where(date: '2018-04-30', org_id: org.id) + expect(stat_joined_user).to have(1).items + expect(stat_joined_user.first.count).to eq(3) end end context 'when no org is passed' do it "generates monthly aggregates for each org since their creation" do - Org.expects(:all).returns([org]) - april = [FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,04,03,0,0,0)), FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,04,04,0,0,0))] - may = [FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,05,03,0,0,0))] - june = [FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,06,03,0,0,0)), FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,06,04,0,0,0))] + Org.stubs(:all).returns([org]) described_class.call - april = StatJoinedUser.find_by(date: '2018-04-30', org_id: org.id).count - may = StatJoinedUser.find_by(date: '2018-05-31', org_id: org.id).count - june = StatJoinedUser.find_by(date: '2018-06-30', org_id: org.id).count - july = StatJoinedUser.find_by(date: '2018-07-31', org_id: org.id).count - expect([april, may, june, july]).to eq([2,1,2,0]) + april, may, june, july = find_by_dates(dates: ['2018-04-30', '2018-05-31', '2018-06-30', '2018-07-31'], org_id: org.id) + counts = [april, may, june, july].map(&:count) + expect(counts).to eq([2,1,2,0]) + end + + it 'monthly records are either created or updated' do + Org.stubs(:all).returns([org]) + + described_class.call + + april_updated = FactoryBot.create(:user, org: org, created_at: DateTime.new(2018,04,05,0,0,0)) + + described_class.call + + stat_joined_user = StatJoinedUser.where(date: '2018-04-30', org_id: org.id) + expect(stat_joined_user).to have(1).items + expect(stat_joined_user.first.count).to eq(3) end end end diff --git a/spec/services/org/create_last_month_created_plan_service_spec.rb b/spec/services/org/create_last_month_created_plan_service_spec.rb index f899680..59a7d68 100644 --- a/spec/services/org/create_last_month_created_plan_service_spec.rb +++ b/spec/services/org/create_last_month_created_plan_service_spec.rb @@ -4,6 +4,12 @@ let(:org) do FactoryBot.create(:org, created_at: DateTime.new(2018,04,01)) end + let(:template) do + FactoryBot.create(:template, org: org) + end + let(:template2) do + FactoryBot.create(:template, org: org) + end let(:user1) do FactoryBot.create(:user, org: org) end @@ -12,40 +18,100 @@ end let(:creator) { Role.access_values_for(:creator).first } let(:administrator) { Role.access_values_for(:administrator).first } + before(:each) do + plan = FactoryBot.create(:plan, template: template, created_at: Date.today.last_month) + plan2 = FactoryBot.create(:plan, template: template, created_at: Date.today.last_month) + plan3 = FactoryBot.create(:plan, template: template2, created_at: Date.today.last_month) + FactoryBot.create(:role, plan: plan, user: user1, access: creator) + FactoryBot.create(:role, plan: plan, user: user1, access: administrator) + FactoryBot.create(:role, plan: plan2, user: user1, access: creator) + FactoryBot.create(:role, plan: plan3, user: user2, access: creator) + end describe '.call' do context 'when org is passed' do - it "returns aggregates from today's last month" do - plan = FactoryBot.create(:plan, created_at: Date.today.last_month) - plan2 = FactoryBot.create(:plan, created_at: Date.today.last_month) - plan3 = FactoryBot.create(:plan, created_at: Date.today.last_month) - FactoryBot.create(:role, plan: plan, user: user1, access: creator) - FactoryBot.create(:role, plan: plan, user: user1, access: administrator) - FactoryBot.create(:role, plan: plan2, user: user1, access: creator) - FactoryBot.create(:role, plan: plan3, user: user2, access: creator) + it "generates counts from today's last month" do + described_class.call(org) + + last_month_count = StatCreatedPlan.find_by(date: Date.today.last_month.end_of_month, org_id: org.id).count + expect(last_month_count).to eq(3) + end + + it "generates counts by template from today's last month" do + described_class.call(org) + + last_month_details = StatCreatedPlan.find_by(date: Date.today.last_month.end_of_month, org_id: org.id).details + expect(last_month_details).to eq( + { + 'by_template' => [ + { 'name' => template.title, 'count' => 2 }, + { 'name' => template2.title, 'count' => 1 }, + ] + } + ) + end + + it "monthly records are either created or updated" do + described_class.call(org) + + last_month = StatCreatedPlan.where(date: Date.today.last_month.end_of_month, org_id: org.id) + expect(last_month).to have(1).items + expect(last_month.first.count).to eq(3) + + new_plan = FactoryBot.create(:plan, template: template2, created_at: Date.today.last_month.end_of_month) + FactoryBot.create(:role, plan: new_plan, user: user1, access: creator) described_class.call(org) - last_month = StatCreatedPlan.find_by(date: Date.today.last_month.end_of_month, org_id: org.id).count - expect(last_month).to eq(3) + last_month = StatCreatedPlan.where(date: Date.today.last_month.end_of_month, org_id: org.id) + expect(last_month).to have(1).items + expect(last_month.first.count).to eq(4) end end context 'when no org is passed' do - it "returns aggregates from today's last month" do + it "generates counts from today's last month" do Org.expects(:all).returns([org]) - plan = FactoryBot.create(:plan, created_at: Date.today.last_month) - plan2 = FactoryBot.create(:plan, created_at: Date.today.last_month) - plan3 = FactoryBot.create(:plan, created_at: Date.today.last_month) - FactoryBot.create(:role, plan: plan, user: user1, access: creator) - FactoryBot.create(:role, plan: plan, user: user1, access: administrator) - FactoryBot.create(:role, plan: plan2, user: user1, access: creator) - FactoryBot.create(:role, plan: plan3, user: user2, access: creator) described_class.call - last_month = StatCreatedPlan.find_by(date: Date.today.last_month.end_of_month, org_id: org.id).count - expect(last_month).to eq(3) + last_month_count = StatCreatedPlan.find_by(date: Date.today.last_month.end_of_month, org_id: org.id).count + expect(last_month_count).to eq(3) + end + + it "generates counts by template from today's last month" do + Org.expects(:all).returns([org]) + + described_class.call + + last_month_details = StatCreatedPlan.find_by(date: Date.today.last_month.end_of_month, org_id: org.id).details + expect(last_month_details).to eq( + { + 'by_template' => [ + { 'name' => template.title, 'count' => 2 }, + { 'name' => template2.title, 'count' => 1 }, + ] + } + ) + end + + it "monthly records are either created or updated" do + Org.stubs(:all).returns([org]) + + described_class.call + + last_month = StatCreatedPlan.where(date: Date.today.last_month.end_of_month, org: org) + expect(last_month).to have(1).items + expect(last_month.first.count).to eq(3) + + new_plan = FactoryBot.create(:plan, template: template2, created_at: Date.today.last_month.end_of_month) + FactoryBot.create(:role, plan: new_plan, user: user1, access: creator) + + described_class.call + + last_month = StatCreatedPlan.where(date: Date.today.last_month.end_of_month, org: org) + expect(last_month).to have(1).items + expect(last_month.first.count).to eq(4) end end end diff --git a/spec/services/org/create_last_month_joined_user_service_spec.rb b/spec/services/org/create_last_month_joined_user_service_spec.rb index 126c650..3c8955a 100644 --- a/spec/services/org/create_last_month_joined_user_service_spec.rb +++ b/spec/services/org/create_last_month_joined_user_service_spec.rb @@ -4,32 +4,56 @@ let(:org) do FactoryBot.create(:org, created_at: DateTime.new(2018,04,01)) end + before(:each) do + 5.times do + FactoryBot.create(:user, org: org, created_at: Date.today.last_month) + end + end describe '.call' do context 'when an org is passed' do - it "generates aggregates from today's last month" do - 5.times do - FactoryBot.create(:user, org: org, created_at: Date.today.last_month) - end - + it "generates counts from today's last month" do described_class.call(org) last_month = StatJoinedUser.find_by(date: Date.today.last_month.end_of_month, org_id: org.id).count expect(last_month).to eq(5) end + + it "monthly records are either created or updated" do + described_class.call(org) + + FactoryBot.create(:user, org: org, created_at: Date.today.last_month) + + described_class.call(org) + + last_month_updated = StatJoinedUser.where(date: Date.today.last_month.end_of_month, org: org.id) + expect(last_month_updated).to have(1).items + expect(last_month_updated.first.count).to eq(6) + end end context 'when no org is passed' do - it "generates aggregates from today's last month" do - Org.expects(:all).returns([org]) - 5.times do - FactoryBot.create(:user, org: org, created_at: Date.today.last_month) - end + it "generates counts from today's last month" do + Org.stubs(:all).returns([org]) described_class.call last_month = StatJoinedUser.find_by(date: Date.today.last_month.end_of_month, org_id: org.id).count expect(last_month).to eq(5) end + + it "generates counts by template from today's last month" do + Org.stubs(:all).returns([org]) + + described_class.call(org) + + FactoryBot.create(:user, org: org, created_at: Date.today.last_month) + + described_class.call(org) + + last_month_updated = StatJoinedUser.where(date: Date.today.last_month.end_of_month, org: org.id) + expect(last_month_updated).to have(1).items + expect(last_month_updated.first.count).to eq(6) + end end end end