Skip to content

Reminder for outcome 2 emails will be sent#392

Merged
pokhiii merged 2 commits intodevelopfrom
logic-change-got-outcome-reminder
Oct 23, 2024
Merged

Reminder for outcome 2 emails will be sent#392
pokhiii merged 2 commits intodevelopfrom
logic-change-got-outcome-reminder

Conversation

@tarunnjoshi
Copy link
Copy Markdown
Member

@tarunnjoshi tarunnjoshi commented Oct 23, 2024

There were some changes to the requirement where we need 2 emails to be sent, once after 48 hours and if that is not filled then 48 hours after that. We don't want the emails to go after that.

Target issue: https://github.com/coloredcow-admin/goonj-crm/issues/149#issuecomment-2431109084

Summary by CodeRabbit

  • New Features

    • Enhanced reminder system for collection camps, including tracking of final reminders.
    • Additional data field for final reminders included in API responses.
  • Bug Fixes

    • Improved logic for sending reminders, ensuring accurate tracking and notifications.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 23, 2024

Walkthrough

The pull request introduces significant changes to the CollectionCampOutcomeService class, particularly in the processCampReminder method. A new parameter, $from, has been added to the method signature. The logic for sending reminders has been restructured, removing the previous check for 24 hours since the last reminder and instead checking if no reminders have been sent yet. Additionally, the civicrm_api3_goonjcustom_collection_camp_outcome_reminder_cron function now includes the Final_Reminder_Sent field in its data retrieval process, enhancing the information available without altering existing parameters or error handling.

Changes

File Path Change Summary
wp-content/civi-extensions/goonjcustom/Civi/CollectionCampOutcomeService.php Updated processCampReminder method to include a new parameter $from, modified logic for sending reminders, and changed database updates to set the Final_Reminder_Sent field.
wp-content/civi-extensions/goonjcustom/api/v3/Goonjcustom/CollectionCampOutcomeReminderCron.php Added Camp_Outcome.Final_Reminder_Sent to the field selection in the civicrm_api3_goonjcustom_collection_camp_outcome_reminder_cron function, enhancing data retrieval without modifying existing parameters or error handling.

Possibly related PRs

  • Collection camp outcome reminder #343: The changes in the CollectionCampOutcomeService class, particularly the processCampReminder method, are directly related to the main PR as both involve modifications to the reminder logic for camp outcomes, including the addition of a new parameter $from.
  • Fix the condition for Camp outcome reminder #366: This PR also modifies the processCampReminder method in the CollectionCampOutcomeService class, enhancing the logic for determining when to send reminders, which aligns with the changes made in the main PR.
  • Collection Camp Authorisation Status #362: The updates to the camp status management in the CollectionCampService class may relate to the overall reminder logic and how reminders are processed based on camp status, which is relevant to the changes in the main PR.
  • Fix the status feedbacks #388: This PR focuses on updating the camp status when the outcome form is submitted, which is relevant to the reminder logic in the main PR as it affects when reminders are triggered based on the camp's status.

Suggested reviewers

  • pokhiii

🎉 In the realm of code where reminders dwell,
A new parameter springs forth, casting its spell.
With logic refined, and fields now aligned,
The camp outcomes flourish, their statuses defined.
So let’s celebrate this change, with joy and with cheer,
For reminders are sent, and the process is clear! 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
wp-content/civi-extensions/goonjcustom/api/v3/Goonjcustom/CollectionCampOutcomeReminderCron.php (1)

50-50: Consider improving query readability and documentation.

While the addition of Camp_Outcome.Final_Reminder_Sent aligns with the new two-reminder system, consider these improvements:

  1. Group related fields together for better readability
  2. Add comments explaining the reminder tracking fields

Here's a suggested refactor:

    ->addSelect(
      'title',
      // Camp details
      'Collection_Camp_Intent_Details.Location_Area_of_camp',
      'Collection_Camp_Intent_Details.End_Date',
      'Logistics_Coordination.Camp_to_be_attended_by',
      // Reminder tracking fields
-     'Camp_Outcome.Last_Reminder_Sent',
-     'Camp_Outcome.Final_Reminder_Sent',
+     'Camp_Outcome.Last_Reminder_Sent',    // Tracks the first reminder (sent 48h after event)
+     'Camp_Outcome.Final_Reminder_Sent'    // Tracks the second/final reminder (sent 48h after first)
    )
wp-content/civi-extensions/goonjcustom/Civi/CollectionCampOutcomeService.php (1)

Line range hint 16-16: Add type hint for the $from parameter.

The method signature should include type hints for all parameters to ensure type safety and improve code maintainability.

-  public static function processCampReminder($camp, $now, $from) {
+  public static function processCampReminder(array $camp, \DateTimeImmutable $now, string $from) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between bdce920 and c18d4f3.

📒 Files selected for processing (2)
  • wp-content/civi-extensions/goonjcustom/Civi/CollectionCampOutcomeService.php (2 hunks)
  • wp-content/civi-extensions/goonjcustom/api/v3/Goonjcustom/CollectionCampOutcomeReminderCron.php (1 hunks)
🧰 Additional context used
🔇 Additional comments (4)
wp-content/civi-extensions/goonjcustom/api/v3/Goonjcustom/CollectionCampOutcomeReminderCron.php (1)

Line range hint 50-71: Verify the integration with CollectionCampOutcomeService.

The query now fetches the Final_Reminder_Sent field which is used by CollectionCampOutcomeService::processCampReminder(). Let's verify this integration.

✅ Verification successful

Let me generate additional scripts to verify the complete implementation of processCampReminder method since the previous results don't show its full implementation.

The previous results show that Final_Reminder_Sent is being used in CollectionCampOutcomeService, but we need to see the complete implementation of processCampReminder to verify how it's being handled in the reminder logic.


Let me try one more search with a different pattern to locate the complete implementation.

The previous results showed that Final_Reminder_Sent is being used in the service class, but we need to see the complete implementation to verify the reminder logic and timing calculations.


Integration with CollectionCampOutcomeService is properly implemented

The verification shows that the Final_Reminder_Sent field is correctly integrated in the reminder logic:

  • The field is fetched in the query and passed to processCampReminder
  • The service properly handles both Last_Reminder_Sent and Final_Reminder_Sent fields
  • The reminder logic follows a clear sequence:
    • First reminder after 48 hours of camp end
    • Second (final) reminder after another 48 hours
    • No more reminders after Final_Reminder_Sent is set

The implementation maintains good separation of concerns with dedicated methods for email sending and HTML generation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of Final_Reminder_Sent field in processCampReminder method

# Test: Search for the method implementation to confirm it handles both reminder fields
ast-grep --pattern 'function processCampReminder($camp, $now, $from) {
  $$$
}'

# Test: Search for any direct usage of the Final_Reminder_Sent field
rg -A 5 'Final_Reminder_Sent'

Length of output: 11688


Script:

#!/bin/bash
# Search for the complete implementation of processCampReminder method
fd -e php -x cat {} \; | rg -A 30 "function processCampReminder\(\$camp, \$now, \$from\)"

# Search for any other methods that might be using Final_Reminder_Sent
fd -e php -x cat {} \; | rg -B 5 -A 5 "Final_Reminder_Sent"

Length of output: 154


Script:

#!/bin/bash
# Search for the complete implementation in the CollectionCampOutcomeService file
cat wp-content/civi-extensions/goonjcustom/Civi/CollectionCampOutcomeService.php

# Alternative search for the method
rg -U "public static function processCampReminder.*?\{.*?\}" -A 50

Length of output: 4658

wp-content/civi-extensions/goonjcustom/Civi/CollectionCampOutcomeService.php (3)

43-46: LGTM! Early return prevents additional reminders.

This check effectively implements the requirement that no additional emails should be sent after the second reminder.


47-56: Improve comment clarity and verify timing logic.

The comment could be more specific about the 48-hour requirement.

-    // If no reminders have been sent yet, send the first reminder.
+    // Send first reminder if no previous reminders and 48 hours have passed since camp end.

Let's verify that this is the only place where Last_Reminder_Sent is updated:

✅ Verification successful

Let me try a different search strategy to verify the Last_Reminder_Sent updates.

This should help us find any occurrences of Last_Reminder_Sent being updated, regardless of the exact syntax or casing used.

Let me also check for the entity definition to understand the field better:


Based on the search results, I can now verify the Last_Reminder_Sent field usage. The code shows that:

  1. The Last_Reminder_Sent field is only updated in two specific scenarios:

    • In CollectionCampOutcomeService.php for outcome reminders
    • In CollectionCampVolunteerFeedbackService.php for volunteer feedback reminders
  2. The timing logic is correctly implemented:

    • First checks if 48 hours have passed since camp end (line 38)
    • Then checks if no reminders have been sent (Last_Reminder_Sent is NULL)
    • Updates Last_Reminder_Sent timestamp when sending the first reminder
  3. The comment could indeed be more specific about the timing requirement, but it's not technically incorrect since it accurately describes the immediate condition being checked.

The comment is accurate but could be more informative. The current implementation correctly handles the reminder timing requirements.

The suggested comment improvement would add helpful context, but the existing comment is not incorrect - it accurately describes the specific condition being checked. The 48-hour requirement is already enforced by the previous check at line 38.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other updates to Last_Reminder_Sent to ensure consistent reminder tracking
rg -l "Last_Reminder_Sent" | xargs rg "Last_Reminder_Sent.*="

Length of output: 61


Script:

#!/bin/bash
# Search for Last_Reminder_Sent pattern in different variations
rg -i "last.?reminder.?sent" -A 3 -B 3

# Also search using ast-grep to find any assignments
ast-grep --pattern '$_->addValue("Last_Reminder_Sent", $_)'

Length of output: 18398


Script:

#!/bin/bash
# Look for Collection_Camp entity definition
rg -i "collection.?camp" -A 5 -B 5

Length of output: 279258


Line range hint 16-67: Verify the complete reminder flow timing.

The implementation aims to:

  1. Send first reminder 48 hours after camp end
  2. Send second reminder 48 hours after first reminder
  3. Never send more reminders

Let's verify this flow by checking the database schema and any related cron jobs:

✅ Verification successful

Let me gather more information about the cron job implementation to verify the complete reminder flow.

The first command will help us understand how the cron job is scheduled and triggered. The ast-grep patterns will help us verify if there are any other places in the code that might modify these reminder fields, which could potentially interfere with the intended flow.


Let me try one more search to find the cron implementation:

This should help us see the complete cron implementation and any API routes that might affect the reminder flow.


Reminder flow implementation is correct and well-structured

After analyzing the complete implementation, the reminder flow is correctly implemented with proper timing and controls:

  1. The cron job correctly filters for completed camps with unfilled outcome forms
  2. The service implementation ensures:
    • First reminder after 48 hours of camp end
    • Second reminder after another 48 hours
    • No more reminders after Final_Reminder_Sent is set
  3. The flow has proper error handling and logging

The implementation follows single responsibility principle with:

  • Cron job handling scheduling and camp filtering
  • Service class handling reminder business logic
  • Clear separation of database operations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check database schema for reminder-related fields
rg -l "CREATE TABLE.*Collection_Camp" | xargs rg "Last_Reminder_Sent|Final_Reminder_Sent"

# Check cron job scheduling
rg -l "collection_camp_outcome_reminder_cron" | xargs rg "job.*frequency"

Length of output: 2044


Script:

#!/bin/bash
# Check the cron job implementation
rg -A 10 "class.*CollectionCampOutcomeReminderCron" 

# Check for any API endpoints or services that might modify these reminder fields
ast-grep --pattern 'function $FUNC($$$) {
  $$$
  ->addValue("Camp_Outcome.Last_Reminder_Sent", $$$)
  $$$
}'

ast-grep --pattern 'function $FUNC($$$) {
  $$$
  ->addValue("Camp_Outcome.Final_Reminder_Sent", $$$)
  $$$
}'

Length of output: 18822


Script:

#!/bin/bash
# Search for the cron implementation file directly
rg -l "CollectionCampOutcomeReminderCron" | xargs cat

# Also check for any API routes that might trigger this
rg -A 5 "api.*collection.*camp.*outcome.*reminder"

Length of output: 4232

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (4)
wp-content/civi-extensions/goonjcustom/Civi/CollectionCampOutcomeService.php (4)

Line range hint 16-21: Update method signature documentation.

The method signature has been updated to include the $from parameter, but the PHPDoc block hasn't been updated accordingly. Consider adding type hints and parameter documentation:

   /**
    * Process camp reminder logic.
    *
    * @param array $camp
    * @param \DateTimeImmutable $now
+   * @param string $from Email address to send reminders from
    *
    * @throws \Exception
    */
-  public static function processCampReminder($camp, $now, $from) {
+  public static function processCampReminder(array $camp, \DateTimeImmutable $now, string $from): bool {

42-51: Document the business logic for reminder flow.

While the implementation is correct, adding a comment block explaining the reminder flow would improve maintainability:

+    // Business Logic:
+    // 1. First reminder is sent 48 hours after camp end
+    // 2. Second reminder is sent 48 hours after first reminder
+    // 3. No more reminders are sent after the second one
+
     // If no reminders have been sent yet, send the first reminder.
     if ($lastReminderSent === NULL) {

Line range hint 69-70: Add reminder type to error logs.

Enhance error logging by including which reminder (first/final) failed to send:

       \Civi::log()->error('Failed to send reminder email', [
         'campAttendedById' => $campAttendedById,
         'attendeeEmail' => $attendeeEmail,
+        'reminderType' => $camp['Camp_Outcome.Final_Reminder_Sent'] === NULL ? 'first' : 'final',
       ]);

Based on the investigation of CiviCRM's codebase, I can now provide a final response:

Add email validation using CiviCRM's standard validation approach

The $from parameter should be validated using CiviCRM's built-in validation to maintain consistency with the framework. Here's the recommended approach:

   public static function processCampReminder($camp, $now, $from) {
+    if (!CRM_Utils_Rule::email($from)) {
+      throw new CRM_Core_Exception('Invalid from email address');
+    }
  • CiviCRM provides CRM_Utils_Rule::email() as the standard way to validate email addresses across the framework
  • This maintains consistency with other CiviCRM components and ensures uniform email validation behavior
  • The error message follows CiviCRM's exception handling patterns
🔗 Analysis chain

Line range hint 16-21: Validate email format for $from parameter.

The new $from parameter should be validated to ensure it's a valid email address format.

Let's check if email validation is implemented elsewhere:

Add validation at the start of the method:

   public static function processCampReminder($camp, $now, $from) {
+    if (!filter_var($from, FILTER_VALIDATE_EMAIL)) {
+      throw new \CRM_Core_Exception('Invalid from email address format');
+    }

Let me check the CiviCRM email validation implementations to see if there's a standard way to validate emails in this codebase.


Let me check if there's a standard CiviCRM utility class for email validation.


Let me check one more thing - the CiviCRM validation rules.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for email validation patterns
rg -l "email.*valid" --type php

Length of output: 3593


Script:

#!/bin/bash
# Check CiviCRM's email validation implementations
rg -A 5 "email.*valid" wp-content/plugins/civicrm/civicrm/CRM/Utils/

Length of output: 2089


Script:

#!/bin/bash
# Search for email validation utility classes in CiviCRM
rg -A 5 "class.*Mail|validateEmail" wp-content/plugins/civicrm/civicrm/CRM/Utils/

Length of output: 5595


Script:

#!/bin/bash
# Check for email validation rules in CiviCRM
rg -A 10 "validate.*email" wp-content/plugins/civicrm/civicrm/CRM/Core/BAO/

Length of output: 77

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between c18d4f3 and 0f655e7.

📒 Files selected for processing (2)
  • wp-content/civi-extensions/goonjcustom/Civi/CollectionCampOutcomeService.php (1 hunks)
  • wp-content/civi-extensions/goonjcustom/api/v3/Goonjcustom/CollectionCampOutcomeReminderCron.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • wp-content/civi-extensions/goonjcustom/api/v3/Goonjcustom/CollectionCampOutcomeReminderCron.php
🧰 Additional context used

Comment on lines +53 to 62
// Return if 48 hours have not passed since the last reminder was sent.
if ($lastReminderSent !== NULL && $hoursSinceLastReminder < 48) {
return FALSE;
}
self::sendOutcomeReminderEmail($campAttendedById, $from, $campCode, $campAddress, $collectionCampId, $endDateString);

EckEntity::update('Collection_Camp', TRUE)
->addWhere('id', '=', $camp['id'])
->addValue('Camp_Outcome.Last_Reminder_Sent', $now->format('Y-m-d H:i:s'))
->addValue('Camp_Outcome.Final_Reminder_Sent', $now->format('Y-m-d H:i:s'))
->execute();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent possibility of third reminder.

The current implementation doesn't explicitly check if a final reminder has already been sent. This could potentially allow a third reminder to be sent.

Add a check for Final_Reminder_Sent:

     // Return if 48 hours have not passed since the last reminder was sent.
     if ($lastReminderSent !== NULL && $hoursSinceLastReminder < 48) {
       return FALSE;
     }
+    
+    // Don't send more than two reminders
+    if ($camp['Camp_Outcome.Final_Reminder_Sent'] !== NULL) {
+      return FALSE;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Return if 48 hours have not passed since the last reminder was sent.
if ($lastReminderSent !== NULL && $hoursSinceLastReminder < 48) {
return FALSE;
}
self::sendOutcomeReminderEmail($campAttendedById, $from, $campCode, $campAddress, $collectionCampId, $endDateString);
EckEntity::update('Collection_Camp', TRUE)
->addWhere('id', '=', $camp['id'])
->addValue('Camp_Outcome.Last_Reminder_Sent', $now->format('Y-m-d H:i:s'))
->addValue('Camp_Outcome.Final_Reminder_Sent', $now->format('Y-m-d H:i:s'))
->execute();
// Return if 48 hours have not passed since the last reminder was sent.
if ($lastReminderSent !== NULL && $hoursSinceLastReminder < 48) {
return FALSE;
}
// Don't send more than two reminders
if ($camp['Camp_Outcome.Final_Reminder_Sent'] !== NULL) {
return FALSE;
}
self::sendOutcomeReminderEmail($campAttendedById, $from, $campCode, $campAddress, $collectionCampId, $endDateString);
EckEntity::update('Collection_Camp', TRUE)
->addWhere('id', '=', $camp['id'])
->addValue('Camp_Outcome.Final_Reminder_Sent', $now->format('Y-m-d H:i:s'))
->execute();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants