Skip to content

feat: Support dynamic mobile signal metrics for multiple interfaces#820

Open
Yash-Chauhan22 wants to merge 2 commits into
openwisp:masterfrom
Yash-Chauhan22:feature/dynamic-mobile-metrics
Open

feat: Support dynamic mobile signal metrics for multiple interfaces#820
Yash-Chauhan22 wants to merge 2 commits into
openwisp:masterfrom
Yash-Chauhan22:feature/dynamic-mobile-metrics

Conversation

@Yash-Chauhan22

@Yash-Chauhan22 Yash-Chauhan22 commented Jun 21, 2026

Copy link
Copy Markdown

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #781

(Note: Please replace <issue-number> with your actual issue number)

Description of Changes

This pull request introduces support for tracking mobile signal metrics dynamically across multiple mobile interfaces.

Specifically, these changes:

  • Updated openwisp_monitoring/device/writer.py to generate unique mobile metrics per interface by adding the interface name (ifname) to the extra_tags (e.g. tracking mobile_rx_bytes, mobile_tx_bytes, mobile_signal uniquely for each).
  • Modified openwisp_monitoring/monitoring/configuration.py to support dynamic chart titles utilizing the {ifname} placeholder, ensuring separate and clearly identifiable charts are produced for each mobile interface.
  • Added a comprehensive regression test (test_multiple_mobile_charts) in test_api.py to verify the creation and correct naming of multi-interface charts.
  • Updated docs/user/metrics.rst to document the new extra_tags (like ifname and operator) and explain the multi-interface mobile charting behavior.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request adds per-interface chart support for mobile signal metrics. Chart title templates for signal_strength, signal_quality, and access_tech are updated with an {ifname} placeholder. InfluxDB query templates gain an ifname = '{ifname}' filter in the WHERE clause. The DeviceDataWriter._write_mobile_signal method gains an extra_tags parameter and now scopes metric names to the interface using {ifname} signal strength/quality/access technology formatted strings. Two data migrations (0014 in monitoring, 0006 in sample_monitoring) perform chunked bulk renaming of existing Metric records to the new naming convention. Documentation for the three mobile metric schemas is updated to reflect the signal measurement name, corrected fields, and per-interface chart behavior. A new integration test posts data for two mobile interfaces and asserts six new charts with the correct titles. .dockerignore is also populated with common Python artifact exclusions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • nemesifier
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main feature: support for dynamic mobile signal metrics across multiple interfaces, matching the PR's core objective.
Description check ✅ Passed The description is comprehensive, covering checklist completion, issue reference, detailed changes, and documentation updates across all modified components.
Linked Issues check ✅ Passed The PR fully implements the requested feature: dynamic chart generation for multiple mobile interfaces with interface-specific naming, addressing all requirements from issue #781.
Out of Scope Changes check ✅ Passed All changes are directly aligned with supporting multiple mobile interfaces. The .dockerignore update is a minor, complementary improvement; migrations ensure proper data consistency.
Bug Fixes ✅ Passed This PR implements a feature request (issue #781) to support multiple mobile interfaces, not a bug fix. The custom check applies only to bug fixes in core user-facing functionality; feature additio...

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.dockerignore:
- Around line 1-8: The .dockerignore file is missing several standard Python
cache and artifact directories that should be excluded from Docker builds to
reduce image size and prevent unnecessary artifacts. Add the following entries
to the .dockerignore file: __pycache__/ for Python bytecode cache directories,
.pytest_cache/ for pytest artifacts, .mypy_cache/ for mypy type checking cache,
*.pyc for compiled Python files, and .coverage for code coverage reports.
Additionally, consider adding environment file patterns like .env and .env.local
to prevent accidental inclusion of secrets or local configuration in the Docker
image.

In `@docker-compose.yml`:
- Around line 9-10: Add a comment block above the volumes section in
docker-compose.yml that documents the purpose of the `.:/opt/openwisp` volume
mount, clarifies that it is intended for development and testing only, and
explains the security implications such as bypassing `.dockerignore` rules and
exposing sensitive local files. Include best practices guidance recommending
that developers use `.env` files (excluded from version control) for secrets,
ensure sensitive local directories are not mounted into the container, and be
aware that files excluded by `.dockerignore` during the image build will still
be accessible at runtime through this volume mount.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ba0569d5-39e7-4fc8-8653-dc83262acd6d

📥 Commits

Reviewing files that changed from the base of the PR and between 874aa15 and c1b0499.

📒 Files selected for processing (6)
  • .dockerignore
  • docker-compose.yml
  • docs/user/metrics.rst
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
  • openwisp_monitoring/monitoring/configuration.py
📜 Review details
⏰ Context from checks skipped due to timeout. (12)
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Kilo Code Review
🧰 Additional context used
📓 Path-based instructions (4)
**/*.rst

📄 CodeRabbit inference engine (AGENTS.md)

Update docs when behavior, settings, public APIs, setup steps, metrics, or supported versions change.

Files:

  • docs/user/metrics.rst
{**/*.sh,**/*.{js,ts,tsx,jsx},**/*.py,**/*.rb,**/*.go,**/*.java,**/*.cs,**/*.cpp,**/*.c}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place imports at the top of the file. Only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready).
Avoid unnecessary blank lines inside function and method bodies.
Mark user-facing strings for translation with Django i18n helpers in Django code.
Preserve tenant isolation and object-level permissions for organizations, devices, checks, metrics, charts, alerts, and related data.
Be careful with queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, time series database backends, and dashboard/websocket updates.
Watch for cross-tenant data leaks, permission bypasses, unsafe query construction, excessive metric ingestion, insecure credentials, and secrets.
Preserve validation around metric payloads, device data, alert configuration, time series queries, URLs, and backend credentials.
Write comments and docstrings only when they explain why code is shaped a certain way. Put comments before the relevant code block instead of scattering them inside it.

Files:

  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
**/*test*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*test*.py: Add or update tests for every behavior change.
For bug fixes, write the regression test first, run it against the unfixed code, confirm it fails for the expected reason, then implement the fix.
Prefer in-process tests so coverage tools can measure changed code.
When changing APIs, include tests for permissions, validation, filtering, pagination, and tenant boundaries.

Files:

  • openwisp_monitoring/device/tests/test_api.py
🧠 Learnings (7)
📚 Learning: 2026-02-21T18:44:28.852Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/device/api/views.py:263-281
Timestamp: 2026-02-21T18:44:28.852Z
Learning: In openwisp-monitoring, MonitoringIndoorCoordinatesList inherits organization scoping from the parent IndoorCoordinatesList (from openwisp-controller), which uses FilterByParentManaged mixin and filters by location_id in get_queryset(). The child class only overrides the queryset attribute to add monitoring-specific select_related fields; this pattern is safe as long as get_queryset() from the parent is not bypassed. During reviews, verify that MonitoringIndoorCoordinatesList continues to rely on the parent's get_queryset() and that any added select_related fields in the child do not alter the parent's filtering logic.

Applied to files:

  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In this repository (openwisp/openwisp-monitoring), the project targets Python 3.10–3.13 as defined by CI. Do not flag backports.zoneinfo as a missing dependency; zoneinfo is a built-in module in all supported Python versions. When reviewing Python code, assume zoneinfo is available and avoid suggesting installation of backports.zoneinfo. If a file imports zoneinfo or uses it for time zone handling, treat it as standard library usage compatible with the supported CI matrix.

Applied to files:

  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In the openwisp-monitoring project, targets are Linux-based environments. Do not flag a Windows-specific tzdata package as a missing dependency in code reviews for Python files (e.g., openwisp_monitoring/utils.py). If a platform-specific dependency is truly required, document the exception in review guidelines and ensure CI/packaging checks enforce platform constraints rather than manual review.

Applied to files:

  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
📚 Learning: 2026-02-21T01:03:37.822Z
Learnt from: nemesifier
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:827-859
Timestamp: 2026-02-21T01:03:37.822Z
Learning: In Selenium tests (e.g., in openwisp_monitoring/tests/test_selenium.py and similar test files), when testing JS animations on dashboards or elements driven by JavaScript (such as real-time location updates), insert a short sleep (e.g., sleep(0.3)) before WebDriverWait assertions to allow animations to complete and reduce flakiness. Note: use this as a targeted workaround and prefer explicit waits or animation-end checks where possible to avoid relying on fixed delays.

Applied to files:

  • openwisp_monitoring/device/tests/test_api.py
📚 Learning: 2026-02-25T18:42:08.825Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:309-317
Timestamp: 2026-02-25T18:42:08.825Z
Learning: In Selenium tests that use ChannelsLiveServerTestCase (e.g., openwisp_monitoring/tests/test_selenium.py), override settings to configure CHANNEL_LAYERS with channels_redis.core.RedisChannelLayer instead of InMemoryChannelLayer. This is required because the live server runs in a separate process from the test process, and InMemoryChannelLayer is per-process only and cannot handle cross-process WebSocket broadcasting needed for real-time location update tests.

Applied to files:

  • openwisp_monitoring/device/tests/test_api.py
📚 Learning: 2026-06-17T20:50:49.946Z
Learnt from: nemesifier
Repo: openwisp/openwisp-monitoring PR: 816
File: openwisp_monitoring/device/tests/test_api.py:543-544
Timestamp: 2026-06-17T20:50:49.946Z
Learning: In openwisp-monitoring, when an endpoint is routed using Django’s `path()` with the `<uuid:pk>` path converter, malformed UUID inputs are rejected by Django’s URL dispatcher before your view logic runs (resulting in a 404). During code review, don’t recommend adding regression tests that only validate Django’s framework-level malformed-UUID → 404 behavior; instead, focus tests on application-specific behavior (permissions, view/business logic, database effects, and response content) that runs inside the view.

Applied to files:

  • openwisp_monitoring/device/tests/test_api.py
📚 Learning: 2026-06-10T17:25:49.606Z
Learnt from: pandafy
Repo: openwisp/openwisp-monitoring PR: 815
File: openwisp_monitoring/device/tests/test_admin.py:964-964
Timestamp: 2026-06-10T17:25:49.606Z
Learning: In this repo’s OpenWISP monitoring test suite, `_get_org()` is an intentionally-used helper imported/provided via upstream base test mixins (e.g., from openwisp-users/openwisp-utils). During code review, do not flag `_get_org()` as “undefined” or suggest replacing it with `_create_org()`. Treat them as distinct: `_get_org()` retrieves an existing organization, while `_create_org()` creates a new one.

Applied to files:

  • openwisp_monitoring/device/tests/test_api.py
🔇 Additional comments (4)
openwisp_monitoring/monitoring/configuration.py (1)

575-575: LGTM!

Also applies to: 601-601, 626-626

openwisp_monitoring/device/writer.py (1)

80-86: LGTM!

Also applies to: 201-203, 221-229, 252-276

openwisp_monitoring/device/tests/test_api.py (1)

1011-1075: LGTM!

docs/user/metrics.rst (1)

210-228: LGTM!

Also applies to: 239-257, 268-286

Comment thread .dockerignore
Comment on lines +1 to +8
venv/
.git/
.github/
.tox/
htmlcov/
build/
dist/
*.egg-info/

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add missing common Python artifact exclusions.

The .dockerignore file is missing several standard Python cache and artifact directories that should be excluded from Docker builds:

  • __pycache__/ (essential—generated during any Python execution)
  • .pytest_cache/
  • .mypy_cache/
  • *.pyc
  • .coverage

Additionally, if environment files (.env, .env.local, etc.) may be created or exist in the build context, consider excluding them to prevent accidental inclusion of secrets in the image.

🐳 Proposed additions to .dockerignore
 venv/
 .git/
 .github/
 .tox/
 htmlcov/
 build/
 dist/
 *.egg-info/
+__pycache__/
+.pytest_cache/
+.mypy_cache/
+*.pyc
+.coverage
+.env
+.env.local
📝 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
venv/
.git/
.github/
.tox/
htmlcov/
build/
dist/
*.egg-info/
venv/
.git/
.github/
.tox/
htmlcov/
build/
dist/
*.egg-info/
__pycache__/
.pytest_cache/
.mypy_cache/
*.pyc
.coverage
.env
.env.local
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.dockerignore around lines 1 - 8, The .dockerignore file is missing several
standard Python cache and artifact directories that should be excluded from
Docker builds to reduce image size and prevent unnecessary artifacts. Add the
following entries to the .dockerignore file: __pycache__/ for Python bytecode
cache directories, .pytest_cache/ for pytest artifacts, .mypy_cache/ for mypy
type checking cache, *.pyc for compiled Python files, and .coverage for code
coverage reports. Additionally, consider adding environment file patterns like
.env and .env.local to prevent accidental inclusion of secrets or local
configuration in the Docker image.

Comment thread docker-compose.yml Outdated
self.assertIn("Signal Quality (RSRQ): mobile0", chart_titles)
self.assertIn("Signal Quality (RSRQ): mobile1", chart_titles)
self.assertIn("Access Technology: mobile0", chart_titles)
self.assertIn("Access Technology: mobile1", chart_titles)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CRITICAL: Per-interface mobile charts display aggregated (incorrect) data.

This test verifies chart titles and counts, but not that each interface's chart returns interface-specific data. The signal_strength, signal_quality, and access_tech InfluxDB query templates in openwisp_monitoring/db/backends/influxdb/queries.py filter only by content_type and object_id — they do not filter by ifname, unlike the traffic and wifi_clients query templates (which include AND ifname = '{ifname}').

With multiple mobile interfaces, each per-interface chart runs the same query, so they aggregate data across all interfaces and return identical values. For this test's data, both the mobile0 and mobile1 signal-strength charts would show the mean of -51 and -55 (-53) instead of -51 and -55 respectively, defeating the purpose of separate per-interface charts.

Consider asserting per-interface summary values (e.g. mobile0 RSSI == -51.0, mobile1 RSSI == -55.0) and adding AND ifname = '{ifname}' to the three mobile query templates so the charts are truly scoped per interface.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Jun 21, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

The previously flagged CRITICAL issue (per-interface mobile charts returning aggregated data because the InfluxDB query templates lacked an ifname filter) has been resolved in commit b09e2f6. The signal_strength, signal_quality, and access_tech query templates in openwisp_monitoring/db/backends/influxdb/queries.py now include AND ifname = '{ifname}', matching the existing traffic/wifi_clients pattern. Two accompanying data migrations (0014_update_signal_metric_names.py and 0006_update_signal_metric_names.py) rename legacy signal metrics to the new {ifname} <suffix> naming; their main_tags.get("ifname", "") access is safe since main_tags is a JSONField with default=dict, and the forward/reverse logic is idempotent.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0
Files Reviewed (4 files)
  • openwisp_monitoring/db/backends/influxdb/queries.py - adds AND ifname = '{ifname}' to the three mobile signal query templates; fixes the prior CRITICAL.
  • openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py - data migration renaming signal metrics to include the ifname prefix; idempotent, safe main_tags access.
  • tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py - mirrors 0014 for the sample app.
  • docker-compose.yml - removes the .:/opt/openwisp bind mount; benign dev-scaffolding cleanup.
Previous Review Summaries (3 snapshots, latest commit 0c1af09)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 0c1af09)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
openwisp_monitoring/db/backends/influxdb/queries.py (referenced via openwisp_monitoring/device/tests/test_api.py) 1074 Per-interface mobile charts display aggregated data instead of per-interface data. The signal_strength, signal_quality, and access_tech InfluxDB query templates (queries.py:78-98) filter only by content_type/object_id and do not filter by ifname (unlike traffic/wifi_clients at queries.py:27-28,43-44, which include AND ifname = '{ifname}'). With multiple mobile interfaces, every per-interface chart runs the same query and returns identical aggregated values, defeating the feature's purpose. The new test_multiple_mobile_charts only asserts chart titles/counts, not per-interface data correctness, so it does not catch this. Fix: add AND ifname = '{ifname}' to the mobile query templates and assert per-interface summary values in the test.

WARNING

File Line Issue
.dockerignore, docker-compose.yml Unrelated dev-scaffolding changes mixed into a feature PR. Per AGENTS.md ("Keep changes focused. Avoid unrelated refactors."), these should be split into a separate PR or dropped.
Files Reviewed (7 files)
  • openwisp_monitoring/device/writer.py - mobile metrics now use interface-scoped names and extra_tags; aligns mobile signal metrics with the existing traffic/wifi pattern (good consistency).
  • openwisp_monitoring/monitoring/configuration.py - chart titles use {ifname} placeholder, consistent with traffic/wifi_clients chart titles.
  • openwisp_monitoring/device/tests/test_api.py - new test_multiple_mobile_charts regression test added (titles/counts verified, but not per-interface data values).
  • openwisp_monitoring/monitoring/tests/test_models.py - flaky-test fix using a fresh timezone.now() instead of module-level start_time; benign, no issues.
  • docs/user/metrics.rst - documentation updated to reflect signal measurement, tags, and multi-interface charting.
  • .dockerignore / docker-compose.yml - unrelated dev scaffolding (see WARNING above).

Note: The CRITICAL finding lives in openwisp_monitoring/db/backends/influxdb/queries.py, which is not part of this PR's diff, so it is reported in the summary and via the existing inline comment on test_api.py:1074. Fixing the mobile query templates to include AND ifname = '{ifname}' (and asserting per-interface summary values in the test) is required for this feature to work correctly.

Fix these issues in Kilo Cloud

Previous review (commit f4d870b)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
openwisp_monitoring/db/backends/influxdb/queries.py (referenced via openwisp_monitoring/device/tests/test_api.py) 1074 Per-interface mobile charts display aggregated data instead of per-interface data. The signal_strength, signal_quality, and access_tech InfluxDB query templates filter only by content_type/object_id and do not filter by ifname (unlike traffic/wifi_clients). With multiple mobile interfaces, every per-interface chart runs the same query and returns identical aggregated values, defeating the feature's purpose. The new test_multiple_mobile_charts only asserts chart titles/counts, not per-interface data correctness, so it does not catch this. Fix: add AND ifname = '{ifname}' to the mobile query templates and assert per-interface summary values in the test.

WARNING

File Line Issue
.dockerignore, docker-compose.yml Unrelated dev-scaffolding changes mixed into a feature PR. Per AGENTS.md ("Keep changes focused. Avoid unrelated refactors."), these should be split into a separate PR or dropped.
Files Reviewed (6 files)
  • openwisp_monitoring/device/writer.py - mobile metrics now use interface-scoped names and extra_tags; aligns mobile signal metrics with the existing traffic/wifi pattern (good consistency).
  • openwisp_monitoring/monitoring/configuration.py - chart titles use {ifname} placeholder, consistent with traffic/wifi_clients chart titles.
  • openwisp_monitoring/device/tests/test_api.py - new test_multiple_mobile_charts regression test added (titles/counts verified, but not per-interface data values).
  • docs/user/metrics.rst - documentation updated to reflect signal measurement, tags, and multi-interface charting.
  • .dockerignore / docker-compose.yml - unrelated dev scaffolding (see WARNING above).

Note: The CRITICAL finding lives in openwisp_monitoring/db/backends/influxdb/queries.py, which is not part of this PR's diff, so it is reported in the summary and via the existing inline comment on test_api.py:1074. Fixing the mobile query templates to include AND ifname = '{ifname}' (and asserting per-interface summary values in the test) is required for this feature to work correctly.

Fix these issues in Kilo Cloud

Previous review (commit c1b0499)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 2
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
openwisp_monitoring/db/backends/influxdb/queries.py (referenced via openwisp_monitoring/device/tests/test_api.py) 1074 Per-interface mobile charts show aggregated data instead of per-interface data. The signal_strength, signal_quality, and access_tech InfluxDB query templates filter only by content_type/object_id and do not filter by ifname (unlike traffic/wifi_clients). With multiple mobile interfaces, every per-interface chart runs the same query and returns identical aggregated values, defeating the feature's purpose. The new test_multiple_mobile_charts only asserts chart titles/counts, not per-interface data correctness, so it does not catch this.

WARNING

File Line Issue
PR description The PR references a placeholder issue: Closes #<issue-number>. No actual issue is linked, so the change cannot be validated against a described problem.
docker-compose.yml, .dockerignore These infrastructure/dev-scaffolding changes are unrelated to the dynamic mobile metrics feature. Per AGENTS.md ("Keep changes focused. Avoid unrelated refactors."), they should be split into a separate PR or dropped.
Files Reviewed (6 files)
  • openwisp_monitoring/device/writer.py - mobile metrics now use interface-scoped names and extra_tags; aligns mobile signal metrics with the existing traffic/wifi pattern (good consistency).
  • openwisp_monitoring/monitoring/configuration.py - chart titles use {ifname} placeholder, consistent with traffic/wifi_clients chart titles.
  • openwisp_monitoring/device/tests/test_api.py - new test_multiple_mobile_charts regression test added (titles/counts verified, but not per-interface data values).
  • docs/user/metrics.rst - documentation updated to reflect signal measurement, tags, and multi-interface charting.
  • .dockerignore / docker-compose.yml - unrelated dev scaffolding (see WARNING above).

Note: The CRITICAL finding lives in openwisp_monitoring/db/backends/influxdb/queries.py, which is not part of this PR's diff, so it is reported here in the summary rather than as an inline comment on that file. Fixing the mobile query templates to include AND ifname = '{ifname}' (and asserting per-interface summary values in the test) is required for this feature to work correctly.

Fix these issues in Kilo Cloud


Reviewed by glm-5.2-20260616 · Input: 299.8K · Output: 5.9K · Cached: 156.6K

@openwisp-companion

Copy link
Copy Markdown

ReStructuredText Formatting Failure

Hello @Yash-Chauhan22,
(Analysis for commit c1b0499)

The CI is failing due to a ReStructuredText formatting issue in the documentation.

Failure:

  • ReStructuredText check failed! on file /home/runner/work/openwisp-monitoring/openwisp-monitoring/docs/user/metrics.rst.

Fix:

Please reformat the docs/user/metrics.rst file. You can typically fix this by running a documentation formatting tool or manually adjusting the formatting to comply with ReStructuredText standards.

If you have openwisp-qa-format installed, you can try running it to automatically fix the formatting. If not, please ensure the ReStructuredText syntax in the specified file is correct.

@Yash-Chauhan22 Yash-Chauhan22 force-pushed the feature/dynamic-mobile-metrics branch from c1b0499 to f4d870b Compare June 21, 2026 18:27
@openwisp-companion

Copy link
Copy Markdown

ReStructuredText Formatting Errors

Hello @Yash-Chauhan22,
(Analysis for commit f4d870b)

The CI failed because of ReStructuredText formatting errors in the documentation.

To fix this, please run the following command in your local environment:

openwisp-qa-format --fix-rst

This command will automatically reformat the ReStructuredText files in your project, including the docs/user/metrics.rst file that caused the failure.

@Yash-Chauhan22 Yash-Chauhan22 force-pushed the feature/dynamic-mobile-metrics branch from f4d870b to 0c1af09 Compare June 21, 2026 19:00
@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jun 21, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
.dockerignore (1)

1-8: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add missing common Python artifact exclusions to reduce image size and security surface.

The .dockerignore file is missing several standard Python cache and artifact directories that are generated during Python execution and should not be included in the Docker image. These add unnecessary size and, in the case of environment files, pose a security risk.

📦 Proposed additions to .dockerignore
 venv/
 .git/
 .github/
 .tox/
 htmlcov/
 build/
 dist/
 *.egg-info/
+__pycache__/
+.pytest_cache/
+.mypy_cache/
+*.pyc
+.coverage
+.env
+.env.local

The additions exclude:

  • __pycache__/ and *.pyc: Python bytecode caches (regenerated at runtime)
  • .pytest_cache/ and .mypy_cache/: Test and type-checking artifacts
  • .coverage: Local code coverage reports
  • .env and .env.local: Environment files that may contain secrets or local configuration
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.dockerignore around lines 1 - 8, The .dockerignore file is missing several
standard Python cache and artifact directories that should be excluded to reduce
image size and security risks. Add the following entries to the .dockerignore
file: __pycache__/, *.pyc, .pytest_cache/, .mypy_cache/, .coverage, .env, and
.env.local. These entries will prevent Python bytecode caches, test artifacts,
type-checking caches, coverage reports, and potentially sensitive environment
files from being included in the Docker image.
docker-compose.yml (1)

9-10: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Document the purpose and security implications of the volume mount.

The volume mount .:/opt/openwisp enables live code reloading but bypasses the .dockerignore rules applied during image build. This means files excluded by .dockerignore (e.g., venv/, .git/, .env) are still accessible in the container if they exist locally. Add a comment documenting the purpose, that this is for development/testing, and clarifying best practices for handling secrets in this setup.

📝 Proposed documentation comment
     ports:
       - "8000:8000"
+    # Volume mount for development and testing:
+    # - Enables live code reloading without rebuilding the image.
+    # - Bypasses .dockerignore rules—files excluded during build are still accessible if they exist locally.
+    # - WARNING: Local .env files and other sensitive artifacts will be mounted into the container.
+    # - Use .env.local (excluded from .dockerignore) for local secrets, never commit secrets to version control.
     volumes:
       - .:/opt/openwisp
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` around lines 9 - 10, Add a comment block above the
volumes section in the docker-compose.yml file that documents the purpose and
security implications of the `.:/opt/openwisp` volume mount. The comment should
explain that this volume enables live code reloading for development, clarify
that it bypasses .dockerignore filtering and makes locally excluded files (such
as venv/, .git/, .env) accessible within the container, note that this setup is
intended only for development and testing environments, and include a warning
about best practices for handling sensitive credentials and secrets to avoid
exposing them through this volume mount.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In @.dockerignore:
- Around line 1-8: The .dockerignore file is missing several standard Python
cache and artifact directories that should be excluded to reduce image size and
security risks. Add the following entries to the .dockerignore file:
__pycache__/, *.pyc, .pytest_cache/, .mypy_cache/, .coverage, .env, and
.env.local. These entries will prevent Python bytecode caches, test artifacts,
type-checking caches, coverage reports, and potentially sensitive environment
files from being included in the Docker image.

In `@docker-compose.yml`:
- Around line 9-10: Add a comment block above the volumes section in the
docker-compose.yml file that documents the purpose and security implications of
the `.:/opt/openwisp` volume mount. The comment should explain that this volume
enables live code reloading for development, clarify that it bypasses
.dockerignore filtering and makes locally excluded files (such as venv/, .git/,
.env) accessible within the container, note that this setup is intended only for
development and testing environments, and include a warning about best practices
for handling sensitive credentials and secrets to avoid exposing them through
this volume mount.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 34a2cde0-2f68-4ff1-a172-00278bb76d41

📥 Commits

Reviewing files that changed from the base of the PR and between f4d870b and 0c1af09.

📒 Files selected for processing (7)
  • .dockerignore
  • docker-compose.yml
  • docs/user/metrics.rst
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/monitoring/tests/test_models.py
📜 Review details
⏰ Context from checks skipped due to timeout. (12)
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Kilo Code Review
🧰 Additional context used
📓 Path-based instructions (4)
{**/*.sh,**/*.{js,ts,tsx,jsx},**/*.py,**/*.rb,**/*.go,**/*.java,**/*.cs,**/*.cpp,**/*.c}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place imports at the top of the file. Only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready).
Avoid unnecessary blank lines inside function and method bodies.
Mark user-facing strings for translation with Django i18n helpers in Django code.
Preserve tenant isolation and object-level permissions for organizations, devices, checks, metrics, charts, alerts, and related data.
Be careful with queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, time series database backends, and dashboard/websocket updates.
Watch for cross-tenant data leaks, permission bypasses, unsafe query construction, excessive metric ingestion, insecure credentials, and secrets.
Preserve validation around metric payloads, device data, alert configuration, time series queries, URLs, and backend credentials.
Write comments and docstrings only when they explain why code is shaped a certain way. Put comments before the relevant code block instead of scattering them inside it.

Files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
**/*test*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*test*.py: Add or update tests for every behavior change.
For bug fixes, write the regression test first, run it against the unfixed code, confirm it fails for the expected reason, then implement the fix.
Prefer in-process tests so coverage tools can measure changed code.
When changing APIs, include tests for permissions, validation, filtering, pagination, and tenant boundaries.

Files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/device/tests/test_api.py
**/*.rst

📄 CodeRabbit inference engine (AGENTS.md)

Update docs when behavior, settings, public APIs, setup steps, metrics, or supported versions change.

Files:

  • docs/user/metrics.rst
🧠 Learnings (7)
📚 Learning: 2026-02-21T01:03:37.822Z
Learnt from: nemesifier
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:827-859
Timestamp: 2026-02-21T01:03:37.822Z
Learning: In Selenium tests (e.g., in openwisp_monitoring/tests/test_selenium.py and similar test files), when testing JS animations on dashboards or elements driven by JavaScript (such as real-time location updates), insert a short sleep (e.g., sleep(0.3)) before WebDriverWait assertions to allow animations to complete and reduce flakiness. Note: use this as a targeted workaround and prefer explicit waits or animation-end checks where possible to avoid relying on fixed delays.

Applied to files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/device/tests/test_api.py
📚 Learning: 2026-02-21T18:44:28.852Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/device/api/views.py:263-281
Timestamp: 2026-02-21T18:44:28.852Z
Learning: In openwisp-monitoring, MonitoringIndoorCoordinatesList inherits organization scoping from the parent IndoorCoordinatesList (from openwisp-controller), which uses FilterByParentManaged mixin and filters by location_id in get_queryset(). The child class only overrides the queryset attribute to add monitoring-specific select_related fields; this pattern is safe as long as get_queryset() from the parent is not bypassed. During reviews, verify that MonitoringIndoorCoordinatesList continues to rely on the parent's get_queryset() and that any added select_related fields in the child do not alter the parent's filtering logic.

Applied to files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
📚 Learning: 2026-02-25T18:42:08.825Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:309-317
Timestamp: 2026-02-25T18:42:08.825Z
Learning: In Selenium tests that use ChannelsLiveServerTestCase (e.g., openwisp_monitoring/tests/test_selenium.py), override settings to configure CHANNEL_LAYERS with channels_redis.core.RedisChannelLayer instead of InMemoryChannelLayer. This is required because the live server runs in a separate process from the test process, and InMemoryChannelLayer is per-process only and cannot handle cross-process WebSocket broadcasting needed for real-time location update tests.

Applied to files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/device/tests/test_api.py
📚 Learning: 2026-06-17T20:50:49.946Z
Learnt from: nemesifier
Repo: openwisp/openwisp-monitoring PR: 816
File: openwisp_monitoring/device/tests/test_api.py:543-544
Timestamp: 2026-06-17T20:50:49.946Z
Learning: In openwisp-monitoring, when an endpoint is routed using Django’s `path()` with the `<uuid:pk>` path converter, malformed UUID inputs are rejected by Django’s URL dispatcher before your view logic runs (resulting in a 404). During code review, don’t recommend adding regression tests that only validate Django’s framework-level malformed-UUID → 404 behavior; instead, focus tests on application-specific behavior (permissions, view/business logic, database effects, and response content) that runs inside the view.

Applied to files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/device/tests/test_api.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In this repository (openwisp/openwisp-monitoring), the project targets Python 3.10–3.13 as defined by CI. Do not flag backports.zoneinfo as a missing dependency; zoneinfo is a built-in module in all supported Python versions. When reviewing Python code, assume zoneinfo is available and avoid suggesting installation of backports.zoneinfo. If a file imports zoneinfo or uses it for time zone handling, treat it as standard library usage compatible with the supported CI matrix.

Applied to files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In the openwisp-monitoring project, targets are Linux-based environments. Do not flag a Windows-specific tzdata package as a missing dependency in code reviews for Python files (e.g., openwisp_monitoring/utils.py). If a platform-specific dependency is truly required, document the exception in review guidelines and ensure CI/packaging checks enforce platform constraints rather than manual review.

Applied to files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/monitoring/configuration.py
  • openwisp_monitoring/device/tests/test_api.py
  • openwisp_monitoring/device/writer.py
📚 Learning: 2026-06-10T17:25:49.606Z
Learnt from: pandafy
Repo: openwisp/openwisp-monitoring PR: 815
File: openwisp_monitoring/device/tests/test_admin.py:964-964
Timestamp: 2026-06-10T17:25:49.606Z
Learning: In this repo’s OpenWISP monitoring test suite, `_get_org()` is an intentionally-used helper imported/provided via upstream base test mixins (e.g., from openwisp-users/openwisp-utils). During code review, do not flag `_get_org()` as “undefined” or suggest replacing it with `_create_org()`. Treat them as distinct: `_get_org()` retrieves an existing organization, while `_create_org()` creates a new one.

Applied to files:

  • openwisp_monitoring/monitoring/tests/test_models.py
  • openwisp_monitoring/device/tests/test_api.py
🔇 Additional comments (5)
openwisp_monitoring/monitoring/configuration.py (1)

575-575: LGTM!

Also applies to: 601-601, 626-626

openwisp_monitoring/device/writer.py (1)

80-86: LGTM!

Also applies to: 201-203, 221-229, 252-276

openwisp_monitoring/device/tests/test_api.py (1)

1011-1075: LGTM!

docs/user/metrics.rst (1)

210-228: LGTM!

Also applies to: 239-257, 268-286

openwisp_monitoring/monitoring/tests/test_models.py (1)

340-348: LGTM!

@openwisp-companion

Copy link
Copy Markdown

ReStructuredText Formatting Failure

Hello @Yash-Chauhan22,
(Analysis for commit 0c1af09)

The CI failed because of a ReStructuredText formatting issue in the documentation.

Fix:
Please reformat the file /home/runner/work/openwisp-monitoring/openwisp-monitoring/docs/user/metrics.rst. You can usually fix this by running a documentation formatting tool like make docs or a similar command if one is available in the project's documentation build process. If no specific tool is mentioned, ensure the ReStructuredText syntax is correct and adheres to standard formatting conventions.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py`:
- Around line 27-33: The migration uses Metric.objects directly for queries and
bulk_update operations without specifying the correct database connection. In
multi-database setups, this can cause operations to hit the wrong database. Fix
this by chaining .using(schema_editor.connection.alias) to all Metric.objects
method calls throughout the migration (including filter operations at the lines
you mentioned and the bulk_update operation), ensuring all database operations
use the correct connection alias provided by the schema_editor.

In
`@tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py`:
- Around line 21-27: The migration functions do not route database operations to
the correct database in multi-database setups. At the beginning of both
migration functions (forwards and backwards), extract the database alias by
adding `db_alias = schema_editor.connection.alias`. Then, modify all queryset
operations on the Metric model to route through the correct database by
appending `.using(db_alias)` to every `Metric.objects` query (both the filter
call shown in the diff and any subsequent update or other database operations).
This ensures all read and write operations in the migration respect the
schema_editor connection alias.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b702329d-a5fd-4bca-b5d6-10eb708452b5

📥 Commits

Reviewing files that changed from the base of the PR and between 0c1af09 and b09e2f6.

📒 Files selected for processing (3)
  • openwisp_monitoring/db/backends/influxdb/queries.py
  • openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py
  • tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py
📜 Review details
⏰ Context from checks skipped due to timeout. (12)
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Kilo Code Review
🧰 Additional context used
📓 Path-based instructions (2)
{**/*.sh,**/*.{js,ts,tsx,jsx},**/*.py,**/*.rb,**/*.go,**/*.java,**/*.cs,**/*.cpp,**/*.c}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py
  • openwisp_monitoring/db/backends/influxdb/queries.py
  • tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place imports at the top of the file. Only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready).
Avoid unnecessary blank lines inside function and method bodies.
Mark user-facing strings for translation with Django i18n helpers in Django code.
Preserve tenant isolation and object-level permissions for organizations, devices, checks, metrics, charts, alerts, and related data.
Be careful with queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, time series database backends, and dashboard/websocket updates.
Watch for cross-tenant data leaks, permission bypasses, unsafe query construction, excessive metric ingestion, insecure credentials, and secrets.
Preserve validation around metric payloads, device data, alert configuration, time series queries, URLs, and backend credentials.
Write comments and docstrings only when they explain why code is shaped a certain way. Put comments before the relevant code block instead of scattering them inside it.

Files:

  • openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py
  • openwisp_monitoring/db/backends/influxdb/queries.py
  • tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py
🧠 Learnings (5)
📚 Learning: 2026-02-21T18:44:28.852Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/device/api/views.py:263-281
Timestamp: 2026-02-21T18:44:28.852Z
Learning: In openwisp-monitoring, MonitoringIndoorCoordinatesList inherits organization scoping from the parent IndoorCoordinatesList (from openwisp-controller), which uses FilterByParentManaged mixin and filters by location_id in get_queryset(). The child class only overrides the queryset attribute to add monitoring-specific select_related fields; this pattern is safe as long as get_queryset() from the parent is not bypassed. During reviews, verify that MonitoringIndoorCoordinatesList continues to rely on the parent's get_queryset() and that any added select_related fields in the child do not alter the parent's filtering logic.

Applied to files:

  • openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py
  • openwisp_monitoring/db/backends/influxdb/queries.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In this repository (openwisp/openwisp-monitoring), the project targets Python 3.10–3.13 as defined by CI. Do not flag backports.zoneinfo as a missing dependency; zoneinfo is a built-in module in all supported Python versions. When reviewing Python code, assume zoneinfo is available and avoid suggesting installation of backports.zoneinfo. If a file imports zoneinfo or uses it for time zone handling, treat it as standard library usage compatible with the supported CI matrix.

Applied to files:

  • openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py
  • openwisp_monitoring/db/backends/influxdb/queries.py
  • tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py
📚 Learning: 2026-03-14T18:39:04.626Z
Learnt from: UltraBot05
Repo: openwisp/openwisp-monitoring PR: 766
File: openwisp_monitoring/utils.py:59-68
Timestamp: 2026-03-14T18:39:04.626Z
Learning: In the openwisp-monitoring project, targets are Linux-based environments. Do not flag a Windows-specific tzdata package as a missing dependency in code reviews for Python files (e.g., openwisp_monitoring/utils.py). If a platform-specific dependency is truly required, document the exception in review guidelines and ensure CI/packaging checks enforce platform constraints rather than manual review.

Applied to files:

  • openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py
  • openwisp_monitoring/db/backends/influxdb/queries.py
  • tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py
📚 Learning: 2026-02-25T18:42:08.825Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/tests/test_selenium.py:309-317
Timestamp: 2026-02-25T18:42:08.825Z
Learning: In Selenium tests that use ChannelsLiveServerTestCase (e.g., openwisp_monitoring/tests/test_selenium.py), override settings to configure CHANNEL_LAYERS with channels_redis.core.RedisChannelLayer instead of InMemoryChannelLayer. This is required because the live server runs in a separate process from the test process, and InMemoryChannelLayer is per-process only and cannot handle cross-process WebSocket broadcasting needed for real-time location update tests.

Applied to files:

  • tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py
📚 Learning: 2026-06-17T20:50:49.946Z
Learnt from: nemesifier
Repo: openwisp/openwisp-monitoring PR: 816
File: openwisp_monitoring/device/tests/test_api.py:543-544
Timestamp: 2026-06-17T20:50:49.946Z
Learning: In openwisp-monitoring, when an endpoint is routed using Django’s `path()` with the `<uuid:pk>` path converter, malformed UUID inputs are rejected by Django’s URL dispatcher before your view logic runs (resulting in a 404). During code review, don’t recommend adding regression tests that only validate Django’s framework-level malformed-UUID → 404 behavior; instead, focus tests on application-specific behavior (permissions, view/business logic, database effects, and response content) that runs inside the view.

Applied to files:

  • tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py
🔇 Additional comments (1)
openwisp_monitoring/db/backends/influxdb/queries.py (1)

83-85: LGTM!

Also applies to: 87-93, 94-98

Comment on lines +27 to +33
Metric = apps.get_model("monitoring", "Metric")
updated_metrics = []
for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
metric_qs = Metric.objects.filter(
configuration=configuration,
name=old_suffix,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py

Repository: openwisp/openwisp-monitoring

Length of output: 3942


🏁 Script executed:

# Check if project has multi-database configuration
find . -name "settings*.py" -o -name "settings" -type d | head -5

Repository: openwisp/openwisp-monitoring

Length of output: 356


🏁 Script executed:

# Check main settings for database configuration
grep -A 20 "DATABASES" openwisp_monitoring/settings.py | head -30

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Check Metric model for tenant isolation and data integrity concerns
grep -n "class Metric" openwisp_monitoring/monitoring/models/*.py

Repository: openwisp/openwisp-monitoring

Length of output: 242


🏁 Script executed:

# Find the Metric model
find . -name "*.py" -path "*/monitoring/*" | xargs grep -l "class Metric" | head -5

Repository: openwisp/openwisp-monitoring

Length of output: 251


🏁 Script executed:

# Check for database configuration in test settings
cat openwisp_monitoring/settings.py | head -50

Repository: openwisp/openwisp-monitoring

Length of output: 956


🏁 Script executed:

# Check the Metric model for structure and tenant isolation
ast-grep outline openwisp_monitoring/monitoring/models.py | head -100

Repository: openwisp/openwisp-monitoring

Length of output: 375


🏁 Script executed:

# Check test settings for database configuration
grep -A 20 "DATABASES" tests/openwisp2/settings.py

Repository: openwisp/openwisp-monitoring

Length of output: 1205


🏁 Script executed:

# Check if other migrations in the same app use db_alias pattern
find openwisp_monitoring/monitoring/migrations -name "*.py" | xargs grep -l "schema_editor.connection.alias" | head -5

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Check if other RunPython migrations in the project follow the db_alias pattern
find . -path "*/migrations/*.py" -name "*.py" | xargs grep -B5 -A10 "def.*migration.*apps.*schema_editor" | grep -A10 "schema_editor.connection.alias" | head -40

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Check if project documentation or comments mention multi-database support
rg -i "multi.?db|database.*alias|schema_editor" --type md -A 3 -B 3 | head -50

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Check other files in the monitoring app for any existing migrations patterns
ls -la openwisp_monitoring/monitoring/migrations/ | head -20

Repository: openwisp/openwisp-monitoring

Length of output: 1607


🏁 Script executed:

# Check migration 0013 for comparison - does it use db_alias?
sed -n '1,100p' openwisp_monitoring/monitoring/migrations/0013_replace_jsonfield_with_django_builtin.py

Repository: openwisp/openwisp-monitoring

Length of output: 1974


🏁 Script executed:

# Check migration 0012 for RunPython usage
cat openwisp_monitoring/monitoring/migrations/0012_migrate_signal_metrics.py

Repository: openwisp/openwisp-monitoring

Length of output: 1094


🏁 Script executed:

# Check the influxdb migration helper
cat openwisp_monitoring/monitoring/migrations/influxdb/influxdb_alter_structure_0006.py | head -80

Repository: openwisp/openwisp-monitoring

Length of output: 2606


🏁 Script executed:

# Check Django documentation pattern - look for any comments in the codebase about migration best practices
rg -i "runpython|schema_editor" --type py openwisp_monitoring/monitoring/migrations/ -A 2 -B 2 | head -80

Repository: openwisp/openwisp-monitoring

Length of output: 6814


🏁 Script executed:

# Verify the exact lines mentioned in the review comment one more time
sed -n '22,72p' openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py | cat -n

Repository: openwisp/openwisp-monitoring

Length of output: 2852


Use the migration connection alias for all ORM reads/writes.

Lines 30, 40, 46, 59, 67, and 70 query and update through Metric.objects directly. In RunPython, this can hit the wrong database in multi-DB setups. Use schema_editor.connection.alias consistently for both queryset iteration and bulk_update.

Suggested patch
 def forward_migration(apps, schema_editor):
+    db_alias = schema_editor.connection.alias
     Metric = apps.get_model("monitoring", "Metric")
     updated_metrics = []
     for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
-        metric_qs = Metric.objects.filter(
+        metric_qs = Metric.objects.using(db_alias).filter(
             configuration=configuration,
             name=old_suffix,
         )
         for metric in metric_qs.iterator(chunk_size=CHUNK_SIZE):
@@
                 updated_metrics.append(metric)
                 if len(updated_metrics) >= CHUNK_SIZE:
-                    Metric.objects.bulk_update(updated_metrics, fields=["name"])
+                    Metric.objects.using(db_alias).bulk_update(
+                        updated_metrics, fields=["name"]
+                    )
@@
     if updated_metrics:
-        Metric.objects.bulk_update(updated_metrics, fields=["name"])
+        Metric.objects.using(db_alias).bulk_update(updated_metrics, fields=["name"])
@@
 def reverse_migration(apps, schema_editor):
+    db_alias = schema_editor.connection.alias
     Metric = apps.get_model("monitoring", "Metric")
     updated_metrics = []
     for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
-        metric_qs = Metric.objects.filter(configuration=configuration)
+        metric_qs = Metric.objects.using(db_alias).filter(configuration=configuration)
         for metric in metric_qs.iterator(chunk_size=CHUNK_SIZE):
@@
                 updated_metrics.append(metric)
                 if len(updated_metrics) >= CHUNK_SIZE:
-                    Metric.objects.bulk_update(updated_metrics, fields=["name"])
+                    Metric.objects.using(db_alias).bulk_update(
+                        updated_metrics, fields=["name"]
+                    )
                     updated_metrics = []
     if updated_metrics:
-        Metric.objects.bulk_update(updated_metrics, fields=["name"])
+        Metric.objects.using(db_alias).bulk_update(updated_metrics, fields=["name"])
📝 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
Metric = apps.get_model("monitoring", "Metric")
updated_metrics = []
for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
metric_qs = Metric.objects.filter(
configuration=configuration,
name=old_suffix,
)
Metric = apps.get_model("monitoring", "Metric")
updated_metrics = []
for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
metric_qs = Metric.objects.using(db_alias).filter(
configuration=configuration,
name=old_suffix,
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py`
around lines 27 - 33, The migration uses Metric.objects directly for queries and
bulk_update operations without specifying the correct database connection. In
multi-database setups, this can cause operations to hit the wrong database. Fix
this by chaining .using(schema_editor.connection.alias) to all Metric.objects
method calls throughout the migration (including filter operations at the lines
you mentioned and the bulk_update operation), ensuring all database operations
use the correct connection alias provided by the schema_editor.

Comment on lines +21 to +27
Metric = apps.get_model("sample_monitoring", "Metric")
updated_metrics = []
for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
metric_qs = Metric.objects.filter(
configuration=configuration,
name=old_suffix,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f "0006_update_signal_metric_names.py" --exec wc -l {} \;

Repository: openwisp/openwisp-monitoring

Length of output: 251


🏁 Script executed:

cat -n tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py

Repository: openwisp/openwisp-monitoring

Length of output: 3296


🏁 Script executed:

# Search for other migrations using .using() or db_alias pattern
find tests/openwisp2 -name "*.py" -path "*/migrations/*" | head -20 | xargs rg -l "db_alias|\.using\(" 2>/dev/null

Repository: openwisp/openwisp-monitoring

Length of output: 170


🏁 Script executed:

# Check if other migrations in openwisp use db_alias pattern
rg "schema_editor\.connection\.alias" --type py -A 2 -B 2

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Look for Django migration patterns in the codebase
rg "def forward|RunPython" tests/openwisp2 -l --type py | head -10

Repository: openwisp/openwisp-monitoring

Length of output: 570


🏁 Script executed:

# Check main project settings for multi-database configuration
find . -name "settings*.py" | head -5 | xargs grep -l "DATABASES" 2>/dev/null

Repository: openwisp/openwisp-monitoring

Length of output: 196


🏁 Script executed:

# Check settings for DATABASES configuration
grep -A 20 "DATABASES" tests/openwisp2/settings.py | head -30

Repository: openwisp/openwisp-monitoring

Length of output: 1186


🏁 Script executed:

# Check another migration to see if they use db_alias pattern
cat -n tests/openwisp2/sample_monitoring/migrations/0002_create_general_metric.py

Repository: openwisp/openwisp-monitoring

Length of output: 664


🏁 Script executed:

# Search more broadly for .using() in migrations
rg "\.using\(" tests/openwisp2/sample_monitoring/migrations/ -n

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Find migrations in the main monitoring app
find . -path "*/monitoring/migrations/*.py" -name "*.py" | head -20

Repository: openwisp/openwisp-monitoring

Length of output: 1512


🏁 Script executed:

# Check the create_general_metrics function
rg "def create_general_metrics|def delete_general_metrics" -A 15 --type py

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Search for db_alias usage in the entire codebase
rg "db_alias|schema_editor\.connection\.alias" . --type py | head -20

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Check the main migration that the test migration mirrors
cat -n ./openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py

Repository: openwisp/openwisp-monitoring

Length of output: 3942


🏁 Script executed:

# Search for the create_general_metrics function definition
rg "def create_general_metrics" . -A 20 --type py

Repository: openwisp/openwisp-monitoring

Length of output: 2009


🏁 Script executed:

# Check if there are any migrations in this project that properly use db_alias
rg "connection\.alias" . --type py -B 3 -A 3

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Check Django best practices documentation by looking at what patterns exist
rg "using\(" openwisp_monitoring/ --type py | head -15

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Verify the main migration doesn't have db_alias either
grep -n "\.using\|db_alias" ./openwisp_monitoring/monitoring/migrations/0014_update_signal_metric_names.py

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Check if the project/library is meant to support multi-DB
rg "DATABASES" . --type py | grep -v test | grep -v ".pyc"

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Check README or docs for multi-database mention
find . -maxdepth 2 -name "README*" -o -name "*.md" | xargs grep -l "database\|multi" 2>/dev/null | head -5

Repository: openwisp/openwisp-monitoring

Length of output: 178


🏁 Script executed:

# Check if this is a reusable app/library by looking at setup.py or pyproject.toml
cat setup.py 2>/dev/null || cat pyproject.toml 2>/dev/null | head -50

Repository: openwisp/openwisp-monitoring

Length of output: 1893


🏁 Script executed:

# Check if there's any discussion or related issues about multi-DB in the project
rg "multi.*database|multiple.*database" . --type py --type md | head -10

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Look for any Django router configuration
rg "DATABASE_ROUTERS" . --type py

Repository: openwisp/openwisp-monitoring

Length of output: 166


🏁 Script executed:

# Check the openwisp codebase structure - does it support swappable models (common in reusable apps)?
rg "swapper" openwisp_monitoring/ --type py | head -10

Repository: openwisp/openwisp-monitoring

Length of output: 981


Add database alias routing to ensure migrations run on correct database in multi-database setups.

The migration uses Metric.objects for both reads and writes without routing through schema_editor.connection.alias. In multi-database environments, this can cause operations to target the wrong database. Add db_alias = schema_editor.connection.alias at the start of both migration functions and use .using(db_alias) on all queryset operations.

Suggested patch
 def forward_migration(apps, schema_editor):
+    db_alias = schema_editor.connection.alias
     Metric = apps.get_model("sample_monitoring", "Metric")
     updated_metrics = []
     for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
-        metric_qs = Metric.objects.filter(
+        metric_qs = Metric.objects.using(db_alias).filter(
             configuration=configuration,
             name=old_suffix,
         )
@@
                 updated_metrics.append(metric)
                 if len(updated_metrics) >= CHUNK_SIZE:
-                    Metric.objects.bulk_update(updated_metrics, fields=["name"])
+                    Metric.objects.using(db_alias).bulk_update(
+                        updated_metrics, fields=["name"]
+                    )
                     updated_metrics = []
     if updated_metrics:
-        Metric.objects.bulk_update(updated_metrics, fields=["name"])
+        Metric.objects.using(db_alias).bulk_update(updated_metrics, fields=["name"])
@@
 def reverse_migration(apps, schema_editor):
+    db_alias = schema_editor.connection.alias
     Metric = apps.get_model("sample_monitoring", "Metric")
     updated_metrics = []
     for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
-        metric_qs = Metric.objects.filter(configuration=configuration)
+        metric_qs = Metric.objects.using(db_alias).filter(configuration=configuration)
@@
                 updated_metrics.append(metric)
                 if len(updated_metrics) >= CHUNK_SIZE:
-                    Metric.objects.bulk_update(updated_metrics, fields=["name"])
+                    Metric.objects.using(db_alias).bulk_update(
+                        updated_metrics, fields=["name"]
+                    )
                     updated_metrics = []
     if updated_metrics:
-        Metric.objects.bulk_update(updated_metrics, fields=["name"])
+        Metric.objects.using(db_alias).bulk_update(updated_metrics, fields=["name"])
📝 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
Metric = apps.get_model("sample_monitoring", "Metric")
updated_metrics = []
for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
metric_qs = Metric.objects.filter(
configuration=configuration,
name=old_suffix,
)
def forward_migration(apps, schema_editor):
db_alias = schema_editor.connection.alias
Metric = apps.get_model("sample_monitoring", "Metric")
updated_metrics = []
for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
metric_qs = Metric.objects.using(db_alias).filter(
configuration=configuration,
name=old_suffix,
)
for metric in metric_qs:
metric.name = metric.name.replace(old_suffix, new_suffix)
updated_metrics.append(metric)
if len(updated_metrics) >= CHUNK_SIZE:
Metric.objects.using(db_alias).bulk_update(
updated_metrics, fields=["name"]
)
updated_metrics = []
if updated_metrics:
Metric.objects.using(db_alias).bulk_update(updated_metrics, fields=["name"])
def reverse_migration(apps, schema_editor):
db_alias = schema_editor.connection.alias
Metric = apps.get_model("sample_monitoring", "Metric")
updated_metrics = []
for configuration, old_suffix in SIGNAL_CONFIGURATIONS.items():
metric_qs = Metric.objects.using(db_alias).filter(configuration=configuration)
for metric in metric_qs:
metric.name = metric.name.replace(new_suffix, old_suffix)
updated_metrics.append(metric)
if len(updated_metrics) >= CHUNK_SIZE:
Metric.objects.using(db_alias).bulk_update(
updated_metrics, fields=["name"]
)
updated_metrics = []
if updated_metrics:
Metric.objects.using(db_alias).bulk_update(updated_metrics, fields=["name"])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@tests/openwisp2/sample_monitoring/migrations/0006_update_signal_metric_names.py`
around lines 21 - 27, The migration functions do not route database operations
to the correct database in multi-database setups. At the beginning of both
migration functions (forwards and backwards), extract the database alias by
adding `db_alias = schema_editor.connection.alias`. Then, modify all queryset
operations on the Metric model to route through the correct database by
appending `.using(db_alias)` to every `Metric.objects` query (both the filter
call shown in the diff and any subsequent update or other database operations).
This ensures all read and write operations in the migration respect the
schema_editor connection alias.

@openwisp-companion

Copy link
Copy Markdown

ReStructuredText Formatting Failure

Hello @Yash-Chauhan22,
(Analysis for commit b09e2f6)

The CI failed because of a ReStructuredText formatting issue in the documentation.

Fix:
Please reformat the file /home/runner/work/openwisp-monitoring/openwisp-monitoring/docs/user/metrics.rst. You can usually fix this by running a documentation formatting tool or by manually adjusting the line breaks and spacing according to ReStructuredText conventions.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] Support multiple mobile interface per device

1 participant