Skip to content

Refactor: Store entity ID in Eck_Collection_Camp1 in directive#307

Merged
pokhiii merged 3 commits intodevelopfrom
feat/tab-fixes
Oct 2, 2024
Merged

Refactor: Store entity ID in Eck_Collection_Camp1 in directive#307
pokhiii merged 3 commits intodevelopfrom
feat/tab-fixes

Conversation

@belwalshubham
Copy link
Copy Markdown
Collaborator

@belwalshubham belwalshubham commented Oct 2, 2024

  1. Refactor Tabset code in CollectionCampService.php
  2. Store entity ID in Eck_Collection_Camp1 in directive

Summary by CodeRabbit

  • New Features

    • Introduced a new AngularJS component for enhanced dynamic content rendering in the Collection Camp Service template.
    • Streamlined tab management with a new configuration structure for easier maintenance and extensibility.
  • Bug Fixes

    • Improved organization and clarity in the Collection Camp Service functionality without altering core behaviors.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 2, 2024

Walkthrough

The changes involve modifications to the CollectionCampService class and its associated template file. The class has been reorganized to streamline the management of collection camp tabs using an associative array for configuration, enhancing readability and maintainability. Additionally, the template file introduces a new AngularJS component that incorporates dynamic content rendering based on module and entity data, facilitating a more interactive user experience.

Changes

File Change Summary
wp-content/civi-extensions/goonjcustom/Civi/CollectionCampService.php Reorganized import statements; restructured collectionCampTabset method to use an associative array for tab configurations; removed link properties from logistics and campOutcome tabs; redefined campFeedback tab.
wp-content/civi-extensions/goonjcustom/templates/CRM/Goonjcustom/Tabs/CollectionCampService.tpl Introduced a new AngularJS component with a <crm-angular-js> element, including a form and directive for dynamic content rendering.

Possibly related PRs

  • Change mmt to frontend #274: The changes in the CollectionCampService class in both PRs involve modifications to methods within the same class, indicating a direct relationship in terms of functionality related to collection camps.

Suggested labels

in review

Poem

🐇 In the fields where the bunnies hop,
Changes come, and we won't stop!
Tabs are neat, and logic's clear,
Angular's here to bring good cheer!
With every tweak, our code does bloom,
Making space for joy to zoom! 🌼


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.

@belwalshubham belwalshubham changed the title Refactor: Store entity ID in Eck_Collection_Camp1 in directive Refactor: Store entity ID in Eck_Collection_Camp1 in directive Oct 2, 2024
@belwalshubham belwalshubham self-assigned this Oct 2, 2024
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: 0

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

Line range hint 391-477: Improve error handling and consider refactoring generateCollectionCampCode

The changes to generateCollectionCampCode enhance the code generation process, but there are opportunities for further improvement:

  1. Consider extracting the code generation logic into a separate private method for better organization and testability.
  2. Implement more robust error handling, especially for database operations.
  3. The nested if statements could be simplified to reduce complexity.

Example refactoring:

private function generateCode($year, $stateCode, $eventCode, $campId) {
    return $year . '/' . $stateCode . '/' . $eventCode . '/' . $campId;
}

public static function generateCollectionCampCode(string $op, string $objectName, $objectId, &$objectRef) {
    if ($objectName != 'Eck_Collection_Camp' || !$objectId) {
        return;
    }

    $newStatus = $objectRef['Collection_Camp_Core_Details.Status'] ?? '';
    if ($newStatus !== 'authorized') {
        return;
    }

    // Fetch necessary data (year, state, event code, etc.)
    // ...

    $newTitle = self::generateCode($year, $stateCode, $eventCode, $campId);

    try {
        EckEntity::update('Collection_Camp')
            ->addWhere('id', '=', $campId)
            ->addValue('title', $newTitle)
            ->execute();
    } catch (\Exception $e) {
        \Civi::log()->error('Failed to update Collection Camp title: ' . $e->getMessage());
    }
}

This refactoring improves code organization, error handling, and testability.


Line range hint 704-793: Enhance error handling and refactor generateQrCode method

The generateQrCode method is well-implemented, but consider the following improvements:

  1. Implement more granular error handling for each step of the process.
  2. Consider extracting some of the logic into smaller, reusable methods.
  3. Use constants for magic strings and numbers.

Example refactoring:

private const QR_CODE_VERSION = 5;
private const QR_CODE_ECC_LEVEL = QRCode::ECC_L;
private const QR_CODE_SCALE = 10;

private static function generateQrCodeOptions(): QROptions {
    return new QROptions([
        'version'    => self::QR_CODE_VERSION,
        'outputType' => QRCode::OUTPUT_IMAGE_PNG,
        'eccLevel'   => self::QR_CODE_ECC_LEVEL,
        'scale'      => self::QR_CODE_SCALE,
    ]);
}

private static function saveQrCodeAttachment($collectionCampId, $qrcode, $fileName, $qrFieldId) {
    $params = [
        'entity_id' => $collectionCampId,
        'name' => $fileName,
        'mime_type' => 'image/png',
        'field_name' => $qrFieldId,
        'options' => [
            'move-file' => $qrcode,
        ],
    ];

    try {
        $result = civicrm_api3('Attachment', 'create', $params);
        if (empty($result['id'])) {
            throw new \Exception('Failed to create attachment');
        }
        return $result['values'][$result['id']]['url'];
    } catch (\CiviCRM_API3_Exception $e) {
        \Civi::log()->error('Error saving QR code attachment: ' . $e->getMessage());
        throw $e;
    }
}

public static function generateQrCode($collectionCampId) {
    try {
        $baseUrl = \CRM_Core_Config::singleton()->userFrameworkBaseURL;
        $url = "{$baseUrl}actions/collection-camp/{$collectionCampId}";

        $options = self::generateQrCodeOptions();
        $qrcode = (new QRCode($options))->render($url);

        $qrcode = str_replace('data:image/png;base64,', '', $qrcode);
        $qrcode = base64_decode($qrcode);

        $baseFileName = "qr_code_{$collectionCampId}.png";
        $fileName = \CRM_Utils_File::makeFileName($baseFileName);
        $tempFilePath = \CRM_Utils_File::tempnam($baseFileName);

        if (file_put_contents($tempFilePath, $qrcode) === false) {
            throw new \Exception('Failed to write QR code to temporary file');
        }

        $qrField = CustomField::get(FALSE)
            ->addSelect('id')
            ->addWhere('custom_group_id:name', '=', 'Collection_Camp_QR_Code')
            ->addWhere('name', '=', 'QR_Code')
            ->setLimit(1)
            ->execute()
            ->first();

        if (!$qrField) {
            throw new \Exception('No field to save QR Code');
        }

        $qrFieldId = 'custom_' . $qrField['id'];

        $attachmentUrl = self::saveQrCodeAttachment($collectionCampId, $tempFilePath, $fileName, $qrFieldId);

        return true;
    } catch (\Exception $e) {
        \Civi::log()->error('Error generating QR code: ' . $e->getMessage());
        return false;
    }
}

This refactoring improves error handling, code organization, and maintainability.


Line range hint 1075-1145: Enhance mailNotificationToMmt method for better error handling and security

The mailNotificationToMmt method is functional, but consider the following improvements:

  1. Implement more robust error handling, especially for database operations and email sending.
  2. Consider extracting some of the logic into smaller, reusable methods for better organization.
  3. Use parameterized queries or proper escaping for any user input used in database queries to prevent SQL injection.
  4. Validate email addresses before sending to prevent potential abuse.

Example improvements:

private static function getMMTEmail($goonjFieldId) {
    try {
        $coordinators = Relationship::get(FALSE)
            ->addWhere('contact_id_b', '=', $goonjFieldId)
            ->addWhere('relationship_type_id:name', '=', self::MATERIAL_RELATIONSHIP_TYPE_NAME)
            ->addWhere('is_current', '=', TRUE)
            ->execute()->first();

        if (empty($coordinators['contact_id_a'])) {
            throw new \Exception('No MMT coordinator found');
        }

        $email = Email::get(FALSE)
            ->addSelect('email')
            ->addWhere('contact_id', '=', $coordinators['contact_id_a'])
            ->execute()->single();

        if (empty($email['email'])) {
            throw new \Exception('No email found for MMT coordinator');
        }

        return $email['email'];
    } catch (\Exception $e) {
        \Civi::log()->error('Error retrieving MMT email: ' . $e->getMessage());
        return null;
    }
}

public static function mailNotificationToMmt($op, $groupID, $entityID, &$params) {
    if ($op !== 'create') {
        return;
    }

    try {
        $goonjField = self::findOfficeId($params);
        if (!$goonjField) {
            throw new \Exception('Office ID not found');
        }

        // Retrieve necessary information (camp details, etc.)
        // ...

        $mmtEmail = self::getMMTEmail($goonjField['value']);
        if (!$mmtEmail || !filter_var($mmtEmail, FILTER_VALIDATE_EMAIL)) {
            throw new \Exception('Invalid MMT email');
        }

        $fromEmail = OptionValue::get(FALSE)
            ->addSelect('label')
            ->addWhere('option_group_id:name', '=', 'from_email_address')
            ->addWhere('is_default', '=', TRUE)
            ->execute()->single();

        $mailParams = [
            'subject' => 'Material Acknowledgement for Camp: ' . $campCode . ' at ' . $campAddress,
            'from' => $fromEmail['label'],
            'toEmail' => $mmtEmail,
            'replyTo' => $fromEmail['label'],
            'html' => self::goonjcustom_material_management_email_html($collectionCampId, $campCode, $campAddress, $vehicleDispatchId),
        ];

        if (!\CRM_Utils_Mail::send($mailParams)) {
            throw new \Exception('Failed to send email');
        }

        EckEntity::update('Collection_Source_Vehicle_Dispatch', FALSE)
            ->addValue('Acknowledgement_For_Logistics.Filled_by', $coordinators['contact_id_a'])
            ->addWhere('Camp_Vehicle_Dispatch.Collection_Camp_Intent_Id', '=', $collectionCampId)
            ->execute();

    } catch (\Exception $e) {
        \Civi::log()->error('Error in mailNotificationToMmt: ' . $e->getMessage());
    }
}

These changes improve error handling, code organization, and add some basic security checks.


Line range hint 1214-1265: Optimize createActivityForCollectionCamp for better performance and error handling

The createActivityForCollectionCamp method is functional, but consider the following improvements:

  1. Implement more robust error handling, especially for database operations.
  2. Consider using batch operations for creating multiple activities to improve performance.
  3. Extract some of the logic into smaller, reusable methods for better organization.

Example improvements:

private static function getCollectionCampSubtype() {
    try {
        return OptionValue::get(TRUE)
            ->addSelect('value')
            ->addWhere('option_group_id:name', '=', 'eck_sub_types')
            ->addWhere('grouping', '=', 'Collection_Camp_Activity')
            ->addWhere('name', '=', 'Collection_Camp')
            ->execute()->single()['value'];
    } catch (\Exception $e) {
        \Civi::log()->error('Error retrieving Collection Camp subtype: ' . $e->getMessage());
        return null;
    }
}

private static function createActivities($campId, $activities, $startDate, $endDate, $initiator) {
    $subtype = self::getCollectionCampSubtype();
    if (!$subtype) {
        throw new \Exception('Failed to retrieve Collection Camp subtype');
    }

    $batchActivities = [];
    foreach ($activities as $activityName) {
        if ($activityName == 'Others') {
            $activityName = $objectRef['Collection_Camp_Intent_Details.Other_activity'] ?? '';
            if (!$activityName) {
                continue;
            }
        }

        $batchActivities[] = [
            'title' => $activityName,
            'subtype' => $subtype,
            'Collection_Camp_Activity.Collection_Camp_Id' => $campId,
            'Collection_Camp_Activity.Start_Date' => $startDate,
            'Collection_Camp_Activity.End_Date' => $endDate,
            'Collection_Camp_Activity.Organizing_Person' => $initiator,
        ];
    }

    if (!empty($batchActivities)) {
        EckEntity::save('Collection_Camp_Activity', FALSE)
            ->setRecords($batchActivities)
            ->execute();
    }
}

public static function createActivityForCollectionCamp(string $op, string $objectName, $objectId, &$objectRef) {
    if ($objectName != 'Eck_Collection_Camp' || !$objectId) {
        return;
    }

    $newStatus = $objectRef['Collection_Camp_Core_Details.Status'] ?? '';
    if (!$newStatus || $newStatus !== 'authorized') {
        return;
    }

    try {
        $collectionCamp = EckEntity::get('Collection_Camp', FALSE)
            ->addSelect('Collection_Camp_Core_Details.Status', 'Collection_Camp_Core_Details.Contact_Id', 'title')
            ->addWhere('id', '=', $objectId)
            ->execute()->single();

        if ($collectionCamp['Collection_Camp_Core_Details.Status'] === 'authorized') {
            return;
        }

        $activities = $objectRef['Collection_Camp_Intent_Details.Here_are_some_activities_to_pick_from_but_feel_free_to_invent_yo'];
        $startDate = $objectRef['Collection_Camp_Intent_Details.Start_Date'];
        $endDate = $objectRef['Collection_Camp_Intent_Details.End_Date'];
        $initiator = $objectRef['Collection_Camp_Core_Details.Contact_Id'];

        self::createActivities($objectId, $activities, $startDate, $endDate, $initiator);

    } catch (\Exception $e) {
        \Civi::log()->error('Error in createActivityForCollectionCamp: ' . $e->getMessage());
    }
}

These changes improve error handling, code organization, and potentially performance by using batch operations for creating activities.


Line range hint 1-1265: Overall improvements with opportunities for further refinement

The changes to the CollectionCampService class significantly enhance its functionality and maintainability. Key improvements include:

  1. Refactoring of the collectionCampTabset method for better organization and flexibility.
  2. Addition of QR code generation functionality.
  3. Enhanced logic for generating collection camp codes.
  4. Improved email notifications for material management.
  5. More robust activity creation for collection camps.

While these changes are positive, there are opportunities for further improvement:

  1. Implement more comprehensive error handling throughout the class.
  2. Consider extracting some complex logic into smaller, more focused methods.
  3. Optimize database operations, particularly when dealing with multiple entities or activities.
  4. Enhance security measures, especially when handling user input or generating URLs.
  5. Improve code documentation, particularly for complex methods.

These suggestions aim to further enhance the robustness, maintainability, and performance of the CollectionCampService class.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 44dcb2d and ab4f40d.

📒 Files selected for processing (2)
  • wp-content/civi-extensions/goonjcustom/Civi/CollectionCampService.php (2 hunks)
  • wp-content/civi-extensions/goonjcustom/templates/CRM/Goonjcustom/Tabs/CollectionCampService.tpl (1 hunks)
🔇 Additional comments (3)
wp-content/civi-extensions/goonjcustom/templates/CRM/Goonjcustom/Tabs/CollectionCampService.tpl (1)

1-5: Implementation aligns with PR objective, but consider potential improvements.

The changes successfully implement the PR objective of storing the entity ID in Eck_Collection_Camp1 within the directive. The code introduces an AngularJS component that passes the entity ID to a custom directive, which aligns with the refactoring goal.

However, there are a few points to consider:

  1. The use of {ldelim} and {rdelim} suggests this is a Smarty template. It might be beneficial to use Smarty's built-in syntax for JSON-like structures to improve readability.

  2. The options attribute is using a hardcoded key Eck_Collection_Camp1. Consider making this dynamic if there's a possibility of reusing this template for different entity types.

  3. The form with id "bootstrap-theme" is empty except for the directive. Ensure this is intentional and doesn't conflict with any existing Bootstrap themes.

Consider the following improvements:

  1. Use Smarty's JSON syntax:
<{$tabValue.directive} options='{literal}{"Eck_Collection_Camp1": {/literal}{$entity.id}}'></{$tabValue.directive}>
  1. If applicable, make the entity key dynamic:
<{$tabValue.directive} options='{literal}{{/literal}{$entityKey}: {$entity.id}{literal}}{/literal}'></{$tabValue.directive}>

To ensure the changes are consistent across the codebase, run the following script:

This will help identify any inconsistencies in how the entity ID is being used across different templates.

✅ Verification successful

Verified: Implementation aligns with existing codebase patterns. No issues found.

  • The usage of Eck_Collection_Camp1 and entity.id in the reviewed file matches other template files:
    • wp-content/civi-extensions/de.systopia.eck/templates/CRM/Eck/Page/Entity/Edit.tpl
    • wp-content/civi-extensions/goonjcustom/templates/CRM/Goonjcustom/Tabs/CollectionCamp.tpl
    • wp-content/civi-extensions/goonjcustom/templates/CRM/Goonjcustom/Tabs/CollectionCampService.tpl
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of Eck_Collection_Camp1 and entity.id in template files

# Test: Search for other occurrences of Eck_Collection_Camp1 in template files
echo "Occurrences of Eck_Collection_Camp1 in template files:"
rg --type html "Eck_Collection_Camp1" wp-content/civi-extensions

# Test: Search for other uses of entity.id in template files
echo "Occurrences of entity.id in template files:"
rg --type html "entity\.id" wp-content/civi-extensions

Length of output: 337


Script:

#!/bin/bash
# Description: Verify the usage of Eck_Collection_Camp1 and entity.id in .tpl template files

# Test: Search for occurrences of Eck_Collection_Camp1 in .tpl files
echo "Occurrences of Eck_Collection_Camp1 in .tpl files:"
rg "Eck_Collection_Camp1" --type tpl wp-content/civi-extensions

# Test: Search for occurrences of entity.id in .tpl files
echo "Occurrences of entity.id in .tpl files:"
rg "entity\.id" --type tpl wp-content/civi-extensions

Length of output: 375


Script:

#!/bin/bash
# Description: Verify the usage of Eck_Collection_Camp1 and entity.id in .tpl template files

# Test: Search for occurrences of Eck_Collection_Camp1 in .tpl files
echo "Occurrences of Eck_Collection_Camp1 in .tpl files:"
rg "Eck_Collection_Camp1" --glob "*.tpl" wp-content/civi-extensions

# Test: Search for occurrences of entity.id in .tpl files
echo "Occurrences of entity.id in .tpl files:"
rg "entity\.id" --glob "*.tpl" wp-content/civi-extensions

Length of output: 1134

wp-content/civi-extensions/goonjcustom/Civi/CollectionCampService.php (2)

20-21: LGTM: New QR code functionality added

The addition of QRCode and QROptions imports indicates new QR code generation functionality. This is a good enhancement for the CollectionCampService class.


76-114: Excellent refactoring of collectionCampTabset method

The changes to the collectionCampTabset method significantly improve code maintainability and readability:

  1. The new $tabConfigs array centralizes tab configurations, making it easier to add or modify tabs.
  2. The use of a loop to populate the $tabs array reduces code duplication.
  3. The structure is more flexible and easier to extend in the future.

These changes align well with the DRY (Don't Repeat Yourself) principle and improve overall code quality.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants