Skip to content

feat: upgrade spp_cel_event from Beta to Production/Stable #103

Open
gonzalesedwin1123 wants to merge 12 commits into19.0from
feat/spp-cel-event-stable-release
Open

feat: upgrade spp_cel_event from Beta to Production/Stable #103
gonzalesedwin1123 wants to merge 12 commits into19.0from
feat/spp-cel-event-stable-release

Conversation

@gonzalesedwin1123
Copy link
Member

Summary

  • Upgrade spp_cel_event module from Beta to Production/Stable for public release
  • Fix critical events_exists gap where generated expressions were unparseable by the translator
  • Remove dead code (EventsCollection dataclass), consolidate duplicate period parsing
  • Add 39 translator unit tests, fill 6 integration test stubs with real tests (147 total)
  • Improve UI/UX: expression preview, required field enforcement, onchange cleanup, better labels
  • Harden security: field name length cap, narrow exception handlers, remove unimplemented where parameter from profiles and expression generation
  • Simplify code: extract helpers, flatten branches, dict-based dispatch (~8% line reduction in executor)
  • Fix auto_install from True to explicit dependency list per module-visibility principle
  • Update stale SPEC_COMPLIANCE.md and IMPLEMENTATION_NOTES.md

Plan

Execution followed the 13-item plan at docs/plans/SPP_CEL_EVENT_STABLE_RELEASE_PLAN.md, with two rounds of staff engineer + UX expert review.

Test plan

  • All 147 tests pass (./scripts/test_single_module.sh spp_cel_event)
  • Linters pass (ruff, ruff-format, prettier)
  • Module installs cleanly on fresh database
  • Verify event aggregation variable form: create count, sum, exists variables and confirm generated expressions
  • Verify auto_install triggers only when all three dependencies are present

… release

- Remove unused EventsCollection dataclass (dead code)
- Consolidate duplicate _parse_period into wrapper around shared parse_period()
- Fix f-string logging to use lazy %s formatting
- Move import re to top-level in executor and translator
- Fix fragile relative import to use odoo.addons path
- Add missing copyright header to cel_variable_event_agg.py
- Add security invariant comment on _validate_field_name regex
- Add explicit comment on empty states list behavior
- Narrow broad except Exception to specific types in _compare_value
- Add Python fallback size warning for large candidate sets (>10k)
- Remove unsupported 'where' parameter from cel_profiles.yaml signatures
- Fix _compute_cel_expression to call super() and only override events case
- Add expression preview with monospace display and incomplete config warning
- Add required constraint on event_agg_type_id when aggregate_target is events
- Wrap event fields in named group with single visibility control
- Add onchange methods for aggregate_type and temporal clearing
- Improve field labels and help text
- Add hint about using aggregate filter for event filtering
- Remove empty cel_event_profiles.yaml placeholder
- Remove models/README.md (duplicated docstrings)
- Fix stale paths and descriptions in IMPLEMENTATION_NOTES.md
- Add comment explaining header-only ir.model.access.csv
- Replace pass stubs with real tests for sum, avg, min, max aggregation
- Add test for latest_active selection mode
- Add test for within_months temporal filter
- Replace unused function-style test with income range comparison test
- Add comments explaining why CEL service tests use "true" expression
- Create test_cel_event_translator.py with 39 unit tests covering all
  translator methods: _is_event_field_access, _handle_event_call,
  _handle_has_event, _handle_events_aggregate, _handle_aggregate_compare,
  _extract_event_parameters, _eval_literal, _handle_period_function
- Change development_status from Beta to Production/Stable
- Change auto_install from True to explicit dependency list
  ["spp_cel_domain", "spp_event_data", "spp_studio"] per module-visibility
  principle for multi-dependency bridge modules
Odoo's convert_csv_import doesn't support comment lines in CSV files.
The comment explaining the header-only nature of this file was causing
a module installation failure.
- Fix test_event_within_months_filter: use within_months=3 instead of 2
  to avoid PostgreSQL INTERVAL boundary issue with 60-day-old records
- Fix test_event_aggregation_without_type: expect ValidationError since
  the new constraint from Item 7b rejects missing event_agg_type_id
Code simplification (code-simplifier):
- Extract _warn_if_limit_reached() helper to deduplicate 3 call sites
- Flatten _build_selection_sql() and _get_default_states() branches
- Replace if/elif chains with dict lookups in _compute_aggregation,
  _handle_period_function, and _build_event_aggregate_cel
- Simplify _compare_value() string fallback

Expert review fixes (code-reviewer):
- Add 128-char length cap on field names in _validate_field_name
- Add encoding="utf-8" to open() in hooks.py
- Remove stale EventsCollection reference from queryplan docstring

UX review fixes (ux-expert):
- Add required attribute on event_agg_field for sum/avg/min/max
- Add required attribute on event_agg_temporal_value for within_days/months
- Add name="event_agg_expression_preview" on preview div
Critical:
- Map "exists" aggregate type to has_event() instead of events_exists()
  which the translator does not handle. Previously generated unparseable
  CEL expressions for exists-type event variables.

Important:
- Add source_type/aggregate_target guard to _check_temporal_value
  constraint to prevent false positives on non-event variables
- Update SPEC_COMPLIANCE.md: mark YYYY-HN and YYYY-WNN period formats
  as implemented (they were already working in cel_event_functions),
  remove stale "Not Implemented" section, update compliance to 97%
- Remove where= generation from _build_event_aggregate_cel since
  where_predicate is not implemented in either execution path
The test_event_with_custom_filter test expected where= in the generated
expression, but we intentionally removed that since where_predicate is
not implemented. Updated test to verify where= is NOT generated.
@gemini-code-assist
Copy link

Summary of Changes

Hello, 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 marks a significant milestone for the spp_cel_event module, transitioning it to a production-ready state. The changes encompass a broad range of improvements, from resolving critical parsing issues and enhancing code quality to bolstering security and refining the user interface. A major focus was on expanding the module's capabilities, particularly in temporal filtering and event aggregation, while ensuring robustness through comprehensive testing. These updates collectively aim to provide a more stable, secure, and user-friendly experience for integrating event data with CEL expressions.

Highlights

  • Module Status Upgrade: The spp_cel_event module has been upgraded from Beta to Production/Stable status, signifying its readiness for public release and broader adoption.
  • Critical Bug Fixes and Code Improvements: Addressed a critical events_exists parsing gap, removed dead code (EventsCollection dataclass), consolidated duplicate period parsing logic, and simplified code through helper extraction and branch flattening, leading to an approximate 8% line reduction in the executor.
  • Enhanced Testing Coverage: Significantly expanded test coverage with the addition of 39 new translator unit tests and the completion of 6 integration test stubs, bringing the total to 147 comprehensive tests.
  • Improved User Experience and Security: Introduced UI/UX enhancements such as expression preview, required field enforcement, onchange cleanup, and better labels. Security was hardened by implementing a field name length cap, narrowing exception handlers, and removing unimplemented where parameters from profiles and expression generation.
  • Dependency Management Refinement: The auto_install mechanism was changed from a generic True to an explicit dependency list, aligning with module-visibility principles.
  • Documentation Updates: Updated SPEC_COMPLIANCE.md and IMPLEMENTATION_NOTES.md to reflect the current state of the module, including expanded period format support and revised compliance percentages.
Changelog
  • spp_cel_event/IMPLEMENTATION_NOTES.md
    • Updated file paths for clarity and removed absolute paths.
    • Added a note clarifying that where_predicate is not yet implemented.
    • Refined logging details, moving op and rhs to DEBUG level.
    • Updated period parsing section to reflect full support for all period formats via cel_event_functions.parse_period().
    • Removed future development tasks related to extended period support and batch optimization, as some have been implemented or are no longer relevant to this document.
  • spp_cel_event/SPEC_COMPLIANCE.md
    • Updated the 'Temporal Filters' table to reflect full implementation of all period formats (YYYY-HN, YYYY-WNN).
    • Removed 'Half-year and ISO week periods' from the list of unimplemented items.
    • Adjusted the 'Overall Compliance' percentage from 93% to 97% to reflect increased implementation completeness.
  • spp_cel_event/manifest.py
    • Updated the development_status from 'Beta' to 'Production/Stable'.
    • Changed auto_install from True to an explicit list of dependent modules (spp_cel_domain, spp_event_data, spp_studio).
  • spp_cel_event/data/cel_event_profiles.yaml
    • Removed the file, as its content was likely consolidated or is no longer needed.
  • spp_cel_event/data/cel_profiles.yaml
    • Removed the where parameter from the signatures and examples of events_count, events_sum, events_avg, events_min, and events_max functions, noting it as a planned but unimplemented feature.
  • spp_cel_event/hooks.py
    • Added encoding="utf-8" to the open() call for reading SQL files, improving file handling robustness.
  • spp_cel_event/models/README.md
    • Removed the file, indicating its content might have been integrated elsewhere or is no longer maintained.
  • spp_cel_event/models/cel_event_executor.py
    • Imported re and cel_event_functions for new functionalities.
    • Refactored logging statements to be more concise and consistent, moving detailed information to DEBUG level.
    • Added a warning for Python fallback paths when processing a large number of candidates, advising simplification for SQL path usage.
    • Simplified the logic within _build_selection_sql for determining event selection modes.
    • Refactored _compute_aggregation to use a dictionary-based dispatch for aggregation functions, improving readability and reducing conditional branches.
    • Refined _compare_value to handle numeric and string comparisons more robustly.
    • Extracted the warning logic for query result limits into a new helper method _warn_if_limit_reached.
    • Implemented a field name length cap (128 characters) and stricter regex validation in _validate_field_name to enhance security against SQL injection.
    • Simplified the _get_default_states logic.
    • Delegated period parsing to event_funcs.parse_period within _parse_period, centralizing period parsing logic.
  • spp_cel_event/models/cel_event_functions.py
    • Updated logging format strings to use %r and %s for better readability and safety.
  • spp_cel_event/models/cel_event_queryplan.py
    • Removed the EventsCollection dataclass, streamlining the query plan structure.
  • spp_cel_event/models/cel_event_translator.py
    • Imported re for use in _eval_literal and _handle_period_function.
    • Refactored _handle_period_function to use a dictionary for mapping no-argument period functions, improving code structure.
    • Removed a redundant re import within _eval_literal.
  • spp_cel_event/models/cel_variable_event_agg.py
    • Added a copyright header to the file.
    • Updated help texts for event_agg_temporal_value and event_agg_field for better clarity.
    • Refined _compute_cel_expression to ensure super() call and specific event aggregation logic.
    • Changed the events_exists aggregation type to generate has_event() CEL expressions, aligning with translator capabilities.
    • Removed the where parameter from generated CEL expressions, as it is not yet implemented in the executor.
    • Added validation constraints (_check_event_aggregation_config) to ensure an event type is selected for event aggregations and that a field is specified for sum/avg/min/max aggregations.
    • Introduced onchange methods (_onchange_aggregate_type_event, _onchange_event_agg_temporal) to clear related fields based on user selections.
    • Refined _check_temporal_value constraint to apply only when event aggregation is active.
  • spp_cel_event/tests/init.py
    • Added test_cel_event_translator to the module's test suite.
  • spp_cel_event/tests/test_cel_event_integration.py
    • Added test_event_value_compare_income_range to verify event value comparison with income ranges.
    • Added test_event_within_months_filter to test temporal filtering by months.
    • Added test_event_select_latest_active_mode to verify selection of the latest active event.
    • Implemented new tests for events_sum, events_avg, events_min, and events_max aggregations, using the visit_number field.
    • Updated comments for test_cel_service_compilation_succeeds and test_cel_service_get_matching_ids to clarify their scope.
  • spp_cel_event/tests/test_cel_event_translator.py
    • Added a new test file containing extensive unit tests for the CEL event translator, covering _is_event_field_access, _handle_event_call, _handle_has_event, _handle_events_aggregate, _handle_aggregate_compare, _extract_event_parameters, _eval_literal, _handle_period_function, and end-to-end _to_plan scenarios.
  • spp_cel_event/views/cel_variable_event_agg_views.xml
    • Updated the UI for event aggregation fields, including required attributes for event_agg_type_id and event_agg_temporal_value.
    • Improved help texts for event_agg_temporal_value, event_agg_field, and event_agg_states.
    • Added a generated expression preview section to display the resulting CEL expression, along with a warning for incomplete configuration.
Activity
  • The pull request was created to upgrade the spp_cel_event module to Production/Stable status.
  • The implementation followed a detailed 13-item plan outlined in docs/plans/SPP_CEL_EVENT_STABLE_RELEASE_PLAN.md.
  • Two rounds of review were conducted by staff engineers and UX experts.
  • A comprehensive test plan was executed, including passing all 147 module-specific tests, linters (ruff, ruff-format, prettier), and verifying clean installation on a fresh database.
  • Specific verifications were performed for event aggregation variable forms and auto_install triggers.
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.

@codecov
Copy link

codecov bot commented Mar 12, 2026

Codecov Report

❌ Patch coverage is 89.53488% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.45%. Comparing base (9c2376e) to head (76d425a).

Files with missing lines Patch % Lines
spp_cel_event/models/cel_event_executor.py 81.39% 8 Missing ⚠️
spp_cel_event/models/cel_event_functions.py 66.66% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #103      +/-   ##
==========================================
+ Coverage   71.17%   71.45%   +0.28%     
==========================================
  Files         704      713       +9     
  Lines       38554    39367     +813     
==========================================
+ Hits        27439    28131     +692     
- Misses      11115    11236     +121     
Flag Coverage Δ
spp_base_common 90.26% <ø> (ø)
spp_cel_event 85.11% <89.53%> (?)
spp_programs 45.51% <ø> (ø)
spp_security 66.66% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_cel_event/__manifest__.py 0.00% <ø> (ø)
spp_cel_event/hooks.py 92.30% <100.00%> (ø)
spp_cel_event/models/cel_event_queryplan.py 100.00% <ø> (ø)
spp_cel_event/models/cel_event_translator.py 87.03% <100.00%> (ø)
spp_cel_event/models/cel_variable_event_agg.py 95.23% <100.00%> (ø)
spp_cel_event/models/cel_event_functions.py 91.84% <66.66%> (ø)
spp_cel_event/models/cel_event_executor.py 74.84% <81.39%> (ø)

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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 is a substantial and high-quality pull request that successfully upgrades the spp_cel_event module to a production-ready state. The changes are comprehensive, addressing critical bugs, removing dead code, adding extensive tests, and improving security, maintainability, and user experience. The refactoring throughout the codebase, such as centralizing period parsing and using dictionary dispatch, greatly improves code quality. The addition of numerous unit and integration tests is particularly valuable. I have one suggestion for a minor refactoring to reduce code duplication.

Comment on lines 149 to 162
# Temporal filter
temporal_part = None
if self.event_agg_temporal == "this_year":
temporal_part = "period=this_year()"
elif self.event_agg_temporal == "this_quarter":
temporal_part = "period=this_quarter()"
elif self.event_agg_temporal == "this_month":
temporal_part = "period=this_month()"
elif self.event_agg_temporal == "within_days" and self.event_agg_temporal_value:
temporal_part = f"within_days={self.event_agg_temporal_value}"
elif self.event_agg_temporal == "within_months" and self.event_agg_temporal_value:
temporal_part = f"within_months={self.event_agg_temporal_value}"

if temporal_part:
parts.append(temporal_part)
named_period_map = {
"this_year": "period=this_year()",
"this_quarter": "period=this_quarter()",
"this_month": "period=this_month()",
}
if self.event_agg_temporal in named_period_map:
parts.append(named_period_map[self.event_agg_temporal])
elif self.event_agg_temporal in ("within_days", "within_months") and self.event_agg_temporal_value:
parts.append(f"{self.event_agg_temporal}={self.event_agg_temporal_value}")

# States filter
if self.event_agg_states == "all":
parts.append("states=['active', 'superseded', 'expired']")

Choose a reason for hiding this comment

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

medium

This block of code for building temporal and state filter arguments is duplicated in the new _build_has_event_cel method (lines 177-190). To improve maintainability and adhere to the DRY (Don't Repeat Yourself) principle, this common logic should be extracted into a helper method.

You can create a helper method like this and place it before _build_event_aggregate_cel:

def _get_event_filter_parts(self):
    """Builds a list of CEL argument parts for temporal and state filters."""
    parts = []
    # Temporal filter
    named_period_map = {
        "this_year": "period=this_year()",
        "this_quarter": "period=this_quarter()",
        "this_month": "period=this_month()",
    }
    if self.event_agg_temporal in named_period_map:
        parts.append(named_period_map[self.event_agg_temporal])
    elif self.event_agg_temporal in ("within_days", "within_months") and self.event_agg_temporal_value:
        parts.append(f"{self.event_agg_temporal}={self.event_agg_temporal_value}")

    # States filter
    if self.event_agg_states == "all":
        parts.append("states=['active', 'superseded', 'expired']")

    return parts

Then, you can replace this block and the duplicated one in _build_has_event_cel with a single call to this new helper.

Suggested change
# Temporal filter
temporal_part = None
if self.event_agg_temporal == "this_year":
temporal_part = "period=this_year()"
elif self.event_agg_temporal == "this_quarter":
temporal_part = "period=this_quarter()"
elif self.event_agg_temporal == "this_month":
temporal_part = "period=this_month()"
elif self.event_agg_temporal == "within_days" and self.event_agg_temporal_value:
temporal_part = f"within_days={self.event_agg_temporal_value}"
elif self.event_agg_temporal == "within_months" and self.event_agg_temporal_value:
temporal_part = f"within_months={self.event_agg_temporal_value}"
if temporal_part:
parts.append(temporal_part)
named_period_map = {
"this_year": "period=this_year()",
"this_quarter": "period=this_quarter()",
"this_month": "period=this_month()",
}
if self.event_agg_temporal in named_period_map:
parts.append(named_period_map[self.event_agg_temporal])
elif self.event_agg_temporal in ("within_days", "within_months") and self.event_agg_temporal_value:
parts.append(f"{self.event_agg_temporal}={self.event_agg_temporal_value}")
# States filter
if self.event_agg_states == "all":
parts.append("states=['active', 'superseded', 'expired']")
parts.extend(self._get_event_filter_parts())

Add USAGE.md with 11 test scenarios covering all event aggregation
workflows, validation errors, onchange behavior, and expression preview.
Regenerate README.rst via oca-gen-addon-readme.
Cover previously untested code paths:
- _build_has_event_cel with temporal filters, named periods, all states
- _check_event_aggregation_config for sum/avg without field
- _onchange_aggregate_type_event and _onchange_event_agg_temporal
- _validate_field_name length cap and edge cases
- _warn_if_limit_reached helper
- _build_selection_sql for active/latest/first modes
- _compare_value string fallback and None handling
- _compute_aggregation dict-based dispatch
- min/max/this_month expression generation
@gonzalesedwin1123 gonzalesedwin1123 marked this pull request as ready for review March 12, 2026 20:25
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