Skip to content

feat(crm): Create GroupUsageMetric model #36706

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

arthurdedeus
Copy link
Contributor

@arthurdedeus arthurdedeus commented Aug 15, 2025

Problem

Closes #36664

Changes

  • Create GroupUsageMetric model
  • Create GroupUsageMetricViewSet
  • Integrate crmUsageMetricsConfigLogic with backend

How did you test this code?

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

@arthurdedeus arthurdedeus self-assigned this Aug 15, 2025
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Summary

This PR introduces a comprehensive GroupUsageMetric system to support CRM usage metrics configuration as part of issue #36664. The implementation creates a new Django model GroupUsageMetric that stores configurable usage metrics associated with specific group types within teams.

The model includes several key components:

  • Configuration fields: Format options (numeric/currency), time intervals (week/month/quarter), and display types (number/sparkline)
  • Filter system: JSON fields for storing filters, compiled bytecode, and error handling
  • Team scoping: Foreign key relationship to Team model with CASCADE deletion
  • Unique constraints: Prevents duplicate metric names within the same group type

The PR also introduces a new UUID7Model base class that uses the modern RFC9562 UUIDv7 specification for time-sortable primary keys, replacing the older UUIDT pattern for new models.

On the API side, a complete GroupUsageMetricViewSet provides CRUD operations through a nested REST endpoint structure (/api/projects/{id}/groups_types/{index}/metrics). The implementation includes different serializers for list vs detail operations and proper team-based authorization.

The frontend integration updates crmUsageMetricsConfigLogic from mock data to real API calls, changing the data structure to match the backend model (numeric intervals instead of strings, filters object instead of events array) and following standard PostHog patterns for API integration.

Comprehensive test coverage ensures all CRUD operations work correctly with proper authorization and validation. The migration creates the necessary database structure with appropriate indexes and constraints.

Confidence score: 3/5

  • This PR has several implementation concerns that could cause issues in production
  • Score lowered due to potential constraint and scoping issues in the model design
  • Pay close attention to the GroupUsageMetric model unique constraint and team scoping logic

Context used:

Rule - Use Django's .get() method instead of filtering and checking length when you expect exactly one object. This will automatically raise an exception if the object is not found. (link)
Context - Use 'None' as the default value for JSONField in models instead of 'dict()' to avoid creating a new dict instance for each model. (link)

9 files reviewed, 8 comments

Edit Code Review Bot Settings | Greptile

Comment on lines +61 to +67
return await api.create(values.metricsUrl, metric)
},
updateUsageMetric: async ({ metric }) => {
// Mock API delay
await new Promise((resolve) => setTimeout(resolve, 300))
const updatedMetrics = values.usageMetrics.map((oldMetric) =>
oldMetric.id === metric.id ? { ...oldMetric, ...metric } : oldMetric
)
return updatedMetrics
return await api.update(`${values.metricsUrl}/${metric.id}`, metric)
},
removeUsageMetric: async ({ id }) => {
// Mock API delay
await new Promise((resolve) => setTimeout(resolve, 300))
return values.usageMetrics.filter((metric) => metric.id !== id)
return await api.delete(`${values.metricsUrl}/${id}`)
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: The add/update/remove operations return different response types than expected by the loader pattern - they should return the updated array of metrics, not individual responses

self.assertEqual(response.json(), self.permission_denied_response("You don't have access to the project."))

def test_unauthorized_metric_creation(self):
other_url = f"/api/projects/{self.other_team.id}/groups_types/{str(self.other_group_type.pk)}/metrics"
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider using group_type_index instead of pk for consistency with other URLs in the test that use group_type_index.

NUMBER = "number", "number"
SPARKLINE = "sparkline", "sparkline"

team = models.ForeignKey("Team", on_delete=models.CASCADE)
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Missing related_name attribute for the team foreign key. Consider adding related_name='group_usage_metrics' to avoid potential conflicts.

@arthurdedeus arthurdedeus force-pushed the feat/group-usage-metric branch 3 times, most recently from e5ef89e to 2d9a1fd Compare August 15, 2025 14:13
Copy link
Contributor

github-actions bot commented Aug 15, 2025

Size Change: 0 B

Total Size: 2.68 MB

ℹ️ View Unchanged
Filename Size
frontend/dist/toolbar.js 2.68 MB

compressed-size-action

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

4 snapshot changes in total. 0 added, 4 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

3 snapshot changes in total. 0 added, 3 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

2 snapshot changes in total. 0 added, 2 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@arthurdedeus arthurdedeus linked an issue Aug 15, 2025 that may be closed by this pull request
2 tasks
@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

2 snapshot changes in total. 0 added, 2 modified, 0 deleted:

  • chromium: 0 added, 2 modified, 0 deleted (diff for shard 10)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@arthurdedeus arthurdedeus changed the title feat(crm): Crete GroupUsageMetric model feat(crm): Create GroupUsageMetric model Aug 15, 2025
@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

5 snapshot changes in total. 0 added, 5 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

5 snapshot changes in total. 0 added, 5 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

6 snapshot changes in total. 0 added, 6 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

3 snapshot changes in total. 0 added, 3 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

3 snapshot changes in total. 0 added, 3 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 5)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

3 snapshot changes in total. 0 added, 3 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

2 snapshot changes in total. 0 added, 2 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

6 snapshot changes in total. 0 added, 6 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

4 snapshot changes in total. 0 added, 4 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 10)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

2 snapshot changes in total. 0 added, 2 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

5 snapshot changes in total. 0 added, 5 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

3 snapshot changes in total. 0 added, 3 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@arthurdedeus
Copy link
Contributor Author

omg, didn't realize this was on a loop. will rebase asap

@arthurdedeus arthurdedeus force-pushed the feat/usage-metrics-config-ui branch from fb6b9d4 to a3c2269 Compare August 15, 2025 17:18
@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

6 snapshot changes in total. 0 added, 6 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

2 snapshot changes in total. 0 added, 2 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

Base automatically changed from feat/usage-metrics-config-ui to master August 15, 2025 17:29
@arthurdedeus arthurdedeus force-pushed the feat/group-usage-metric branch from 1720021 to 9299688 Compare August 15, 2025 17:41
Copy link
Contributor

Migration SQL Changes

Hey 👋, we've detected some migrations on this PR. Here's the SQL output for each migration, make sure they make sense:

posthog/migrations/0824_groupusagemetric_groupusagemetric_unique_metric_name.py

BEGIN;
--
-- Create model GroupUsageMetric
--
CREATE TABLE "posthog_groupusagemetric" ("id" uuid NOT NULL PRIMARY KEY, "group_type_index" integer NOT NULL, "name" varchar(255) NOT NULL, "format" varchar(64) NOT NULL, "interval" integer NOT NULL, "display" varchar(64) NOT NULL, "filters" jsonb NULL, "bytecode" jsonb NULL, "bytecode_error" text NULL, "team_id" integer NOT NULL);
--
-- Create constraint unique_metric_name on model groupusagemetric
--
ALTER TABLE "posthog_groupusagemetric" ADD CONSTRAINT "unique_metric_name" UNIQUE ("team_id", "group_type_index", "name");
ALTER TABLE "posthog_groupusagemetric" ADD CONSTRAINT "posthog_groupusagemetric_team_id_f72a5b9f_fk_posthog_team_id" FOREIGN KEY ("team_id") REFERENCES "posthog_team" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "posthog_groupusagemetric_team_id_f72a5b9f" ON "posthog_groupusagemetric" ("team_id");
COMMIT;

@arthurdedeus arthurdedeus requested review from aspicer, zlwaterfield, a team and rafaeelaudibert and removed request for a team August 15, 2025 19:12
Copy link
Member

@rafaeelaudibert rafaeelaudibert left a comment

Choose a reason for hiding this comment

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

It all makes sense to me! Left a couple comments, review them before merging

Comment on lines +26 to +28
filters = models.JSONField(null=True, blank=True)
bytecode = models.JSONField(null=True, blank=True)
bytecode_error = models.TextField(null=True, blank=True)
Copy link
Member

Choose a reason for hiding this comment

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

Please confirm with Ben whether these are the right types.

Also, does it make sense to make these nullable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll make these non-nullable, as it is easier to move to nullable later if needed.

About the types, I'm reproducing what's been done for cohorts, this should be pretty similar.

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.

Create usage metrics model
3 participants