Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft case contact cleanup #5918

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Conversation

Smullz622
Copy link
Collaborator

@Smullz622 Smullz622 commented Jul 16, 2024

What github issue is this PR for, if any?

Resolves #5442

What changed, and why?

Added a scope to CaseContact to return drafts without an attached case as well as drafts older than one week. The rake task actually deletes the case contact. The task will need to be added to Heroku task scheduler.

How is this tested? (please write tests!) 💖💪

There's a model test on CaseContact that tests different scenarios and ensures only case contacts that should be cleaned up are returned.

Screenshots please :)

Before running rake task:
Screenshot 2024-07-16 at 11 20 22 AM
After running rake task:
Screenshot 2024-07-16 at 11 21 04 AM

Feelings gif (optional)

moira rose

@@ -0,0 +1,6 @@
desc "delete draft case contacts marked for clean up, run by heroku scheduler."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good desc

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for later PR thought - we may want to move away from heroku scheduler towards something like

      include Sidekiq::Job
      sidekiq_options queue: :general_12h

      def self.crontab
        "0 8 * * *"
      end

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not that familiar with heroku scheduler but would like to learn so I'm curious, what's the rationale for moving away from it in favor of Sidekiq?

Copy link
Collaborator

@elasticspoon elasticspoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of my feedback is pretty minor BUT I took a closer look at the production data and want to double check that stakeholders are fine with the deletion timelines. (The request for changes is mainly to ensure this does not get accidentally merged.)

There are a surprising amount of case contacts that are in draft state that have good info in them but are old. I am not sure we actually want to delete those.

Comment on lines 150 to 151
.where("case_contacts.created_at < ?", 1.week.ago)
.or(where(draft_case_ids: []).where("case_contacts.created_at < ?", 1.day.ago))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the best names would be but can we turn 1.week.ago into named constants as opposed to magic values?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

Comment on lines 148 to 153
scope :drafts_for_removal, -> {
where.not(status: "active")
.where("case_contacts.created_at < ?", 1.week.ago)
.or(where(draft_case_ids: []).where("case_contacts.created_at < ?", 1.day.ago))
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We aren't the most consistent with it in this code base but I think we should keep business logic out of our scopes. In this case the designation of what is "for removal" is that business logic.

Most of the other scopes are pretty generic.

scope :created_max_ago, ->(time_range = nil) {
  where("case_contacts.created_at > ?", time_range) if time_range.present?
}

I think this query belongs in a service object or maybe just some plain ruby object.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I'll move it into a service class and do the destroy there as well so the rake task just calls the service method. I'll update tests accordingly

let!(:new_draft_case_contact) { create(:case_contact, status: "started", created_at: Time.now, draft_case_ids: []) }
let!(:draft_case_contact_without_draft_case_id) { create(:case_contact, status: "started", created_at: Time.now - 2.days, draft_case_ids: []) }
let!(:draft_case_contact_with_draft_case_id) { create(:case_contact, status: "started", created_at: Time.now - 2.days, draft_case_ids: [1]) }
let!(:eight_day_draft_case_contact) { create(:case_contact, status: "details", created_at: Time.now - 8.days) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this also be status: "started" for consistency?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually did that intentionally to mimic a draft that was saved correctly after step one (moving it to 'details') but was then abandoned & was over a week old to cover the second half of the ticket (remove any drafts older than a week)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good test 👍

Comment on lines 5 to 6
let!(:past_expiration) { Time.now - 8.days }
let!(:past_exp_without_case) { Time.now - 2.days }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also do 8.days.ago, etc.

Copy link
Collaborator

@elasticspoon elasticspoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall it looks good to me. But I will pause on making a final merge decision until next week (we have a stakeholder meeting friday).

Comment on lines 3 to 4
draft_expiration = 1.week.ago
draft_expiration_without_case = 1.day.ago
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant constants like DRAFT_EXPIRATION_DELAY and ORPHANED_DRAFT_EXPIRATION_DELAY

Not sure if orphaned is a better name

@compwron
Copy link
Collaborator

Cool stuff :)

ORPHANED_DRAFT_EXPIRATION_DELAY_WITHOUT_CASE = 1.day.ago

def self.call
drafts_for_removal = CaseContact.where.not(status: "active").where("case_contacts.created_at < ?", ORPHANED_DRAFT_EXPIRATION_DELAY)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just looking at some enum things:

Suggested change
drafts_for_removal = CaseContact.where.not(status: "active").where("case_contacts.created_at < ?", ORPHANED_DRAFT_EXPIRATION_DELAY)
drafts_for_removal = CaseContact.not_active.where("case_contacts.created_at < ?", ORPHANED_DRAFT_EXPIRATION_DELAY)

status enum should provide this scope, and will not break if enum definition changes, such as switch to integer column. (Unlikely to happen, just thought this is useful to know). Same could be used below.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ruby Pull requests that update Ruby code Tests! 🎉💖👏
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Periodically cleanup case contact drafts
4 participants