Skip to content

feat(spp_alerts): add rule evaluation engine and promote to Beta#12

Open
emjay0921 wants to merge 3 commits into19.0from
feat/spp-alerts-rule-evaluation-beta
Open

feat(spp_alerts): add rule evaluation engine and promote to Beta#12
emjay0921 wants to merge 3 commits into19.0from
feat/spp-alerts-rule-evaluation-beta

Conversation

@emjay0921
Copy link
Contributor

Why is this change needed?

The spp_alerts module had alert rules (spp.alert.rule) as configuration-only — nothing evaluated them to create alerts automatically. This made the module unusable standalone. Adding a generic rule evaluation engine allows alert rules to automatically monitor models and create alerts based on threshold and date conditions, making the module ready for Beta.

How was the change implemented?

  • Added evaluation fields to spp.alert.rule: rule_type (threshold/date), domain_filter, monitored_field_id, date_field_id, comparison
  • Added source tracking fields to spp.alert: rule_id, res_model, res_id (all optional, no impact on existing alerts)
  • Implemented evaluation engine with batch duplicate prevention (same pattern as spp_drims)
  • Added daily cron job (_cron_evaluate_rules) and manual "Run Now" button on rule form
  • UI improvements: readonly fields when resolved, required resolution notes, searchpanel char field fix, resolve only after acknowledge
  • Promoted development_status from Alpha to Beta

DRIMS compatibility verified: DRIMS uses prototype inheritance (_name = "spp.drims.alert") with separate DB table and its own crons — completely unaffected.

New unit tests

  • 20 test cases in test_rule_evaluation.py covering:
    • All 5 comparison operators (lt, lte, gt, gte, eq)
    • Date rule evaluation with various day windows
    • Duplicate prevention and re-alerting after resolve
    • Cron evaluation (active/inactive/unconfigured rules)
    • Error handling (invalid domains, missing config)
    • Validation constraints
    • Alert value propagation (rule_id, res_model, res_id, company)

Unit tests executed by the author

How to test manually

  1. Install/upgrade spp_alerts module
  2. Go to Settings > Technical > Alerts > Alert Rules
  3. Open a rule with Rule Type set (e.g., "Low Credit Limit Partners")
  4. Click "Run Now" — notification shows number of alerts created
  5. Go to Alerts list — verify alerts created with correct source rule and record links
  6. Click "Run Now" again — verify 0 duplicates created
  7. Acknowledge then resolve an alert — verify readonly fields and required resolution notes
  8. Run rule again — verify new alert created for the resolved record

Related links

Add generic rule evaluation engine to spp_alerts so alert rules can
automatically create alerts based on threshold and date conditions.
Rules can be evaluated via daily cron or manually with a Run Now button.

Key changes:
- Add rule_type, domain_filter, monitored_field_id, date_field_id,
  comparison fields to spp.alert.rule
- Add rule_id, res_model, res_id source tracking fields to spp.alert
- Implement _evaluate_rule, _evaluate_threshold, _evaluate_date methods
  with batch duplicate prevention
- Add daily cron job for automatic rule evaluation
- Add Run Now button on rule form for manual evaluation
- UI improvements: readonly fields when resolved, required resolution
  notes, searchpanel fix, title styling, created date display
- Resolve button only visible after acknowledging
- Promote development_status from Alpha to Beta
- Add 20 test cases for rule evaluation engine
@gemini-code-assist
Copy link

Summary of Changes

Hello @emjay0921, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the spp_alerts module by introducing a robust rule evaluation engine. Previously, alert rules were configuration-only, lacking automated evaluation. This change enables the module to autonomously monitor models and generate alerts based on configurable threshold and date conditions, making it a fully functional and standalone alerting solution. The module's development status has been promoted to Beta, reflecting its increased maturity and feature completeness.

Highlights

  • Rule Evaluation Engine: Introduced a generic rule evaluation engine for spp.alert.rule to automate alert creation based on configurable conditions.
  • New Rule Configuration Fields: Added new fields to spp.alert.rule including rule_type (threshold/date), domain_filter, monitored_field_id, date_field_id, and comparison to define evaluation logic.
  • Alert Source Tracking: Implemented source tracking fields (rule_id, res_model, res_id) on spp.alert to link alerts back to their originating rules and records.
  • Automated & Manual Evaluation: Integrated a daily cron job (_cron_evaluate_rules) for automatic rule evaluation and a manual 'Run Now' button on the alert rule form for immediate testing.
  • UI/UX Enhancements for Alerts: Improved the alert form by making certain fields readonly when an alert is resolved, requiring resolution notes, and enforcing acknowledgment before resolution.
  • Module Status Promotion: Promoted the spp_alerts module's development_status from Alpha to Beta, signifying increased maturity and feature completeness.
  • Comprehensive Unit Tests: Added over 20 new unit test cases covering various aspects of rule evaluation, including threshold and date rules, duplicate prevention, cron job behavior, validation, and error handling.
Changelog
  • spp_alerts/manifest.py
    • Updated development_status from "Alpha" to "Beta".
    • Added data/ir_cron.xml to the data files.
  • spp_alerts/data/ir_cron.xml
    • Added a new XML file defining a daily cron job named "Alerts: Evaluate Alert Rules" that calls model._cron_evaluate_rules().
  • spp_alerts/models/alert.py
    • Imported UserError for custom exceptions.
    • Added rule_id (Many2one to spp.alert.rule), res_model (Char), and res_id (Many2oneReference) fields for source tracking of alerts.
    • Modified action_resolve to require resolution_notes before an alert can be resolved.
  • spp_alerts/models/alert_rule.py
    • Imported operator, UserError, ValidationError, and safe_eval.
    • Defined COMPARISON_OPERATORS dictionary for evaluation logic.
    • Updated the model's docstring to reflect the new evaluation capabilities.
    • Added new fields for rule configuration: rule_type (threshold/date), domain_filter (text), monitored_field_id (Many2one to ir.model.fields for numeric types), date_field_id (Many2one to ir.model.fields for date types), and comparison (selection for operators).
    • Implemented _check_rule_configuration constraint to ensure required fields are set based on rule_type.
    • Added action_evaluate method, triggered by a "Run Now" button, to immediately evaluate a rule and display a notification.
    • Implemented the core rule evaluation engine methods: _evaluate_rule, _evaluate_threshold, _evaluate_date, _get_existing_alert_keys (for duplicate prevention), and _prepare_alert_vals.
    • Added _cron_evaluate_rules as an API model method to be called by the daily cron job, evaluating all active and configured rules.
  • spp_alerts/tests/init.py
    • Added test_rule_evaluation to the list of imported test modules.
  • spp_alerts/tests/test_rule_evaluation.py
    • Added a new test file containing a TestRuleEvaluation class with comprehensive unit tests.
    • Tests cover threshold rules (various comparisons), date rules (different day windows), duplicate prevention, re-alerting after resolution, cron job behavior (active/inactive/unconfigured rules, error handling), validation constraints, and alert value propagation (rule_id, res_model, res_id, company).
  • spp_alerts/views/alert_rule_views.xml
    • Added rule_type field to the tree view.
    • Added a "Run Now" button to the form view header, visible for configured rules and specific user groups.
    • Added rule_type field to the main group in the form view.
    • Refactored the "Thresholds" notebook page into an "Evaluation" page, making it invisible if rule_type is not set.
    • Introduced conditional visibility for "Threshold Settings" and "Date Settings" groups based on rule_type.
    • Added monitored_field_id, comparison, date_field_id, and domain_filter fields to the evaluation page, with required attributes based on rule_type.
    • Added a groupby_rule_type filter to the search view.
  • spp_alerts/views/alert_views.xml
    • Added rule_id field to the list view, hidden by default.
    • Modified the "Resolve" button's invisible attribute to state != 'acknowledged', enforcing acknowledgment before resolution.
    • Made title, alert_type_id, priority, company_id, and description fields readonly when the alert state is 'resolved'.
    • Added create_date field to the form view.
    • Added rule_id, res_model, and res_id fields to the form view, readonly and invisible if not set.
    • Made resolution_notes required and readonly when the alert state is 'resolved'.
    • Corrected the searchpanel field name from alert_type to alert_type_id.
Activity
  • The author implemented a new rule evaluation engine for the spp_alerts module, enabling automated alert generation.
  • Comprehensive unit tests were added to ensure the reliability and correctness of the new evaluation logic, covering various scenarios and edge cases.
  • The module's development_status was updated to Beta, indicating a significant milestone in its maturity and readiness for broader use.
  • The author verified compatibility with DRIMS, confirming that existing DRIMS functionality remains unaffected by these changes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully adds a much-needed rule evaluation engine to the spp_alerts module, promoting it to Beta status. However, a significant security vulnerability was identified: the engine uses sudo() when searching for records matching a user-defined domain, which allows users with 'Alert Manager' privileges to bypass system-wide access controls and potentially exfiltrate sensitive information. This critical issue must be addressed by ensuring record searches respect appropriate access rights. Additionally, there are a couple of minor suggestions to further improve code clarity and test conciseness.

_logger.error("Alert rule '%s': invalid domain filter '%s': %s", self.name, self.domain_filter, e)
return 0

records = Model.sudo().search(domain)

Choose a reason for hiding this comment

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

security-high high

The use of sudo().search(domain) here is a security risk because domain is derived from the user-configurable domain_filter field. A user with 'Alert Manager' privileges (who can create and edit alert rules) can craft a malicious domain to query any model in the system, bypassing all Access Control Lists (ACLs) and Record Rules. This allows for unauthorized information disclosure of sensitive data from any model by observing the creation of alerts.

To remediate this, remove the .sudo() call so that the search respects the current user's access rights. If this is intended to run as a background cron, the search will naturally use the permissions of the user configured for the cron job.

Suggested change
records = Model.sudo().search(domain)
records = Model.search(domain)

Comment on lines 304 to 305
if hasattr(date_value, "date"):
date_value = date_value.date()

Choose a reason for hiding this comment

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

medium

Using hasattr(date_value, "date") for type checking is a bit fragile as it relies on duck typing. A more robust and explicit approach is to use isinstance to check if the value is a datetime object. This makes the code's intent clearer and prevents potential errors.

To apply the suggestion, you'll also need to add from datetime import datetime to the imports at the top of the file.

Suggested change
if hasattr(date_value, "date"):
date_value = date_value.date()
if isinstance(date_value, datetime):
date_value = date_value.date()

Comment on lines 255 to 256
alert.write({"resolution_notes": "Test resolution"})
alert.action_resolve(notes="Test resolution")

Choose a reason for hiding this comment

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

medium

This test contains a redundant step. The call to alert.write() on line 255 is unnecessary because action_resolve(notes=...) on the next line already handles setting the resolution notes. You can simplify these two lines into a single call to action_resolve to make the test cleaner and more focused.

Suggested change
alert.write({"resolution_notes": "Test resolution"})
alert.action_resolve(notes="Test resolution")
alert.action_resolve(notes="Test resolution")

@emjay0921 emjay0921 marked this pull request as draft February 6, 2026 03:59
…y fields

- Update test_action_resolve_without_notes to expect UserError (resolution
  notes are now required)
- Use color (integer) instead of credit_limit (requires account module) for
  threshold tests to avoid dependency on optional modules
- Use write_date instead of date for date rule tests
- Remove all sudo() calls from rule evaluation engine (security)
- Use isinstance(datetime) instead of hasattr for type checking
- Add nosemgrep for standard ir.sequence.sudo() pattern
- Remove unused variable in test_alert_mail_tracking
- Add extension points to form views for extensibility
- Auto-format XML via prettier
@emjay0921 emjay0921 marked this pull request as ready for review February 6, 2026 04:25
# Use sudo to access sequence (users may not have ir.sequence access)
vals["reference"] = self.env["ir.sequence"].sudo().next_by_code("spp.alert") or _("New")
# nosemgrep: odoo-sudo-without-context - Standard sequence access
Sequence = self.env["ir.sequence"].sudo()

Check warning

Code scanning / Semgrep OSS

Semgrep Finding: semgrep.odoo-sudo-without-context Warning

sudo() bypasses all access controls. Ensure this is: Intentional and documented Using minimal scope (e.g., .sudo().read(['field']) not .sudo()) Not exposing sensitive data to unauthorized users
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.

1 participant