Marketplace mixpanel-analytics
git clone https://github.com/aiskillstore/marketplace
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/diversioteam/mixpanel-analytics" ~/.claude/skills/aiskillstore-marketplace-mixpanel-analytics && rm -rf "$T"
skills/diversioteam/mixpanel-analytics/SKILL.mdMixPanel Analytics Skill
When to Use This Skill
Use this Skill in the Django4Lyfe backend when working with MixPanel analytics tracking in the
optimo_analytics module:
– to implement new MixPanel tracking events or update existing ones following established patterns (7-step checklist)./mixpanel-analytics:implement
– to review MixPanel implementations for correctness, PII protection, and adherence to Django4Lyfe standards./mixpanel-analytics:review
Example Prompts
Implement Mode
- "Use
to add a new event for tracking when a user completes their profile setup."/mixpanel-analytics:implement - "Run
to add tracking for survey reminder notifications."/mixpanel-analytics:implement svc.surveys.reminder_sent - "Implement MixPanel tracking for the new HRIS CSV validation feature using
."/mixpanel-analytics:implement
Review Mode
- "Run
to check my staged MixPanel changes for PII violations and pattern compliance."/mixpanel-analytics:review staged - "Use
to audit all analytics changes on this feature branch."/mixpanel-analytics:review branch - "Review the entire optimo_analytics module with
."/mixpanel-analytics:review all
Modes
This Skill behaves differently based on how it is invoked:
mode – invoked viaimplement
:/mixpanel-analytics:implement- Guides implementation of new MixPanel events through 7 steps.
- Creates constants, schemas, registry entries, service methods, and tests.
- Enforces PII protection and code patterns.
mode – invoked viareview
:/mixpanel-analytics:review- Audits existing implementations for compliance.
- Checks PII protection, schema design, service patterns, and test coverage.
- Generates structured review reports with severity tags.
Environment & Context Gathering
When this Skill runs, gather context first:
# Git context git branch --show-current git status --porcelain git diff --cached --name-only | grep -E "optimo_analytics|mixpanel" # Analytics module stats grep -c "^ [A-Z_]* = " optimo_analytics/constants.py 2>/dev/null || echo "0" grep -c "^class Mxp" optimo_analytics/schemas.py 2>/dev/null || echo "0" grep -c "MixPanelEvent\." optimo_analytics/registry.py 2>/dev/null || echo "0" ls -1 optimo_analytics/service/*.py 2>/dev/null | xargs -I{} basename {} .py
Read key reference files:
– module-level rules and PII guidelinesoptimo_analytics/AGENTS.md
– existing schema patternsoptimo_analytics/schemas.py
– service layer patternsoptimo_analytics/service/AGENTS.md
– test patternsoptimo_analytics/tests/AGENTS.md
Implementation Mode
7-Step Implementation Checklist
For each new event, complete these steps in order:
Step 1: Add Event Constant (optimo_analytics/constants.py
)
optimo_analytics/constants.py# Event naming convention: {prefix}.{object}.{action}[.error] # Examples: # - svc.surveys.survey_delivered # - svc.map.action_plan_created # - svc.hris_csv.upload.analysis_completed # # NOTE: Do NOT include "cron" in event names - use is_cron_job property instead class MixPanelEvent: # Add under appropriate section with comment NEW_EVENT_NAME = "svc.domain.action_name"
Step 2: Create Schema (optimo_analytics/schemas.py
)
optimo_analytics/schemas.py# Schema naming: Mxp{Domain}{Action}EventSchema # CRITICAL RULES: # - All UUIDs MUST be strings (str, not UUID) # - NO PII: no names, emails, phone numbers # - organization_name IS allowed (business approved) # - Use STRICT_MODEL_CONFIG (no aliases) or ALIASED_MODEL_CONFIG ($ aliases) class MxpNewEventSchema(MixpanelSuperEventPropertiesSchema): """Properties for svc.domain.action_name event. Tracked when [describe when this event fires]. """ # Required fields (no defaults) employee_id: str = Field(description="Employee UUID as string") organization_id: str = Field(description="Organization UUID as string") organization_name: str = Field(description="Organization name for analytics") role: SystemRole | None = Field(description="User role") impersonation: bool = Field(description="Is impersonated session") # Event-specific fields custom_field: str = Field(description="What this field represents") # Use STRICT_MODEL_CONFIG for internal-only schemas # Use ALIASED_MODEL_CONFIG when field names need $ prefix for MixPanel (e.g., $device_id) model_config = STRICT_MODEL_CONFIG
Step 3: Register in Registry (optimo_analytics/registry.py
)
optimo_analytics/registry.py# Add import at top from optimo_analytics.schemas import MxpNewEventSchema # Add to _EVENT_SCHEMA_REGISTRY dict _EVENT_SCHEMA_REGISTRY: dict[str, type[MixpanelSuperEventPropertiesSchema]] = { # ... existing entries ... MixPanelEvent.NEW_EVENT_NAME: MxpNewEventSchema, }
Step 4: Add Tracking Helper (optimo_analytics/service/{domain}.py
)
optimo_analytics/service/{domain}.pyChoose appropriate service file or create new one:
- Authentication eventsauth.py
- Survey lifecycle eventssurvey.py
- Risk calculation eventsrisk.py
- Manager Action Pipeline eventsmap.py
- Core/HRIS eventscore.py
class OptimoMixpanel{Domain}TrackHelper: """Helper class for {Domain} event tracking.""" @classmethod def track_new_event( cls, *, # CRITICAL: Force keyword-only arguments employee_id: str, # ... other params ... ) -> None: """ Track new event (svc.domain.action_name). Tracked when [describe trigger condition]. Args: employee_id: Employee UUID as string """ try: cls._track_new_event( employee_id=employee_id, # ... pass all args ... ) except Exception: # Fire-and-forget: log but don't propagate logger.exception( "mixpanel_new_event_tracking_failed", employee_id=employee_id, ) @staticmethod def _track_new_event( *, employee_id: str, # ... other params ... ) -> None: """Track new event implementation.""" emp_info = OptimoMixpanelService._fetch_required_emp_info( employee_id=employee_id ) properties = MxpNewEventSchema( employee_id=employee_id, organization_id=str(emp_info.organization.uuid), organization_name=emp_info.organization.name, role=emp_info.role, impersonation=False, # ... event-specific fields ... ) # distinct_id fallback hierarchy: # 1. User's UUID (primary) # 2. org_<organization_uuid> (fallback when no user) # 3. Context-specific: slack_<id>, apikey_<id>, webhook_<id> distinct_id = employee_id # or f"org_{org_uuid}" if no user OptimoMixpanelService.track_event( distinct_id=distinct_id, event_name=MixPanelEvent.NEW_EVENT_NAME, properties=properties, )
Step 5: Export from __init__.py
(optimo_analytics/service/__init__.py
)
__init__.pyoptimo_analytics/service/__init__.py# Add to imports from optimo_analytics.service.{domain} import OptimoMixpanel{Domain}TrackHelper # Add to __all__ __all__ = [ # ... existing ... "OptimoMixpanel{Domain}TrackHelper", ]
Step 6: Add Tests (optimo_analytics/tests/test_{event}_event.py
)
optimo_analytics/tests/test_{event}_event.py"""Tests for {Event} MixPanel tracking.""" from unittest.mock import patch from uuid import uuid4 import pytest from optimo_analytics.constants import MixPanelEvent from optimo_analytics.registry import EVENT_SCHEMA_REGISTRY, is_event_registered from optimo_analytics.schemas import MxpNewEventSchema from optimo_analytics.service import OptimoMixpanel{Domain}TrackHelper pytestmark = [pytest.mark.django_db] @pytest.fixture(autouse=True) def eager_jobs(settings): """Force synchronous job execution.""" settings.OPTIMO_JOBS_EAGER_MODE = True yield settings.OPTIMO_JOBS_EAGER_MODE = False @pytest.fixture def mock_mixpanel(): """Mock MixPanel client.""" with patch("optimo_analytics.service.MixPanelFactory.get_client") as mock: yield mock.return_value class TestNewEventSchema: """Test schema validation.""" def test_schema_creation_with_valid_properties(self): """Schema accepts valid properties.""" schema = MxpNewEventSchema( employee_id=str(uuid4()), organization_id=str(uuid4()), organization_name="Test Org", role=SystemRole.EMPLOYEE, impersonation=False, ) assert schema.employee_id is not None class TestNewEventRegistry: """Test registry registration.""" def test_event_is_registered(self): """Event should be registered in schema registry.""" assert is_event_registered(MixPanelEvent.NEW_EVENT_NAME) assert EVENT_SCHEMA_REGISTRY.get(MixPanelEvent.NEW_EVENT_NAME) is MxpNewEventSchema class TestNewEventTracking: """Test service tracking method.""" def test_tracking_calls_mixpanel(self, mock_mixpanel, optimo_employee): """Tracking should call MixPanel with correct properties.""" OptimoMixpanel{Domain}TrackHelper.track_new_event( employee_id=str(optimo_employee.uuid), ) mock_mixpanel.track.assert_called_once() class TestNewEventNonBlocking: """Test fire-and-forget behavior.""" def test_exception_does_not_propagate(self): """Tracking exceptions should be caught and logged.""" with patch.object( OptimoMixpanel{Domain}TrackHelper, "_track_new_event", side_effect=Exception("boom"), ): # Should NOT raise OptimoMixpanel{Domain}TrackHelper.track_new_event( employee_id=str(uuid4()), )
Step 7: Integrate with Business Logic
from optimo_analytics.service import OptimoMixpanel{Domain}TrackHelper def some_business_method(self, ...): # ... business logic ... # Track after successful operation OptimoMixpanel{Domain}TrackHelper.track_new_event( employee_id=str(employee.uuid), )
Critical Rules (DO NOT VIOLATE)
PII Protection
- NEVER send: names, emails, phone numbers, addresses
- ALLOWED: organization_name (business approved for analytics)
- ALWAYS use UUIDs as strings for identifiers
Code Patterns
- ALWAYS use keyword-only arguments (
in method signature)*, - ALWAYS wrap tracking in try-except (fire-and-forget)
- NEVER let tracking failures break business logic
- ALWAYS use structured logging with IDs only
Event Naming Convention
{prefix}.{object}.{action}[.error]
Examples:
svc.surveys.survey_delivered
(for failures)svc.surveys.survey_delivered.errorsvc.map.action_plan_created
Note: Do NOT include execution context (like "cron") in event names. Use
is_cron_job property instead.
When to Use is_cron_job
is_cron_jobNOT all background jobs need
. Only set it when you need:is_cron_job=True
- API time and tracking time to align - the event
should reflect the original user action, not when the CRON rantime - Ordering events with same timestamp - distinguish CRON-processed events from user-triggered ones
When to set
:is_cron_job=True
properties = MxpYourEventSchema( # ... other fields ... is_cron_job=True, cron_execution_timestamp=datetime_to_timestamp_ms(timezone.now()), )
Validation: If
is_cron_job=True, then cron_execution_timestamp is
required (enforced by validate_cron_properties).
Schema Field Types
- UUIDs:
(neverstr
)UUID - Timestamps: Use
for MixPaneldatetime_to_timestamp_ms() - Enums: Use
, etc.SystemRole | None - Lists:
for UUID listslist[str]
Optional Values for String Fields
NEVER override base schema fields as Optional to handle
None values. Instead:
- For
fields that might have no value, pass empty stringstr"" - Do NOT duplicate
,organization_id
,organization_name
, etc. withemployee_id
types in child schemasOptional[str] - The base
already defines these fields - inherit them, don't redefineMixpanelSuperEventPropertiesSchema
BAD - Don't do this:
class MxpNewEventSchema(MixpanelSuperEventPropertiesSchema): # WRONG: duplicating base fields as Optional organization_id: str | None = Field(default=None, description="...") organization_name: str | None = Field(default=None, description="...")
GOOD - Do this instead:
class MxpNewEventSchema(MixpanelSuperEventPropertiesSchema): # Inherit organization_id, organization_name from base schema # Pass empty string when value is not available pass # In service method: properties = MxpNewEventSchema( organization_id=str(org.uuid) if org else "", organization_name=org.name if org else "", # ... ) ## Post-Implementation Validations ```bash # 1. Ruff lint and format .bin/ruff check optimo_analytics/ --fix .bin/ruff format optimo_analytics/ # 2. Type checking .bin/ty check optimo_analytics/ # 3. Django checks DJANGO_CONFIGURATION=DevApp uv run python manage.py check # 4. Run tests .bin/pytest optimo_analytics/tests/ -v --dc=TestLocalApp
Review Mode
Review Checklist
1. PII Protection (CRITICAL - P0)
MUST CHECK:
- No
,first_name
,last_name
,full_name
in schemasdisplay_name - No
,email
,email_address
fieldsuser_email - No
,phone
,phone_number
fieldsphone_e164 - No
,address
,city
as free-text fieldscountry - All identifiers are UUIDs as strings (not UUID objects)
-
is ONLY sent to MixPanel, never loggedorganization_name
2. Event Registration Completeness (P1)
MUST VERIFY:
- Event constant exists in
underconstants.pyMixPanelEvent - Schema class exists in
schemas.py - Event is registered in
registry.py_EVENT_SCHEMA_REGISTRY - Schema inherits from
MixpanelSuperEventPropertiesSchema
3. Schema Design (P1)
MUST VERIFY:
- All UUID fields are typed as
, notstrUUID - All required fields have
Field(description="...") - Uses
orSTRICT_MODEL_CONFIG
appropriatelyALIASED_MODEL_CONFIG - Enum fields use
patternSystemRole | None - Docstring describes when the event is tracked
- Base schema fields from
are NOT redefined asMixpanelSuperEventPropertiesSchema
- pass empty stringOptional[str]
for missing values""
4. Service Method Patterns (P1)
MUST VERIFY:
- Public method is
@classmethod - Uses keyword-only arguments (
after cls)*, - Has try-except wrapper (fire-and-forget)
- Exception handler logs with structured fields
- Private implementation is
@staticmethod
5. Test Coverage (P2)
MUST HAVE:
- Schema validation tests
- Registry registration test
- Service tracking test with
mock_mixpanel - Non-blocking test (exception doesn't propagate)
- Uses
pytestmark = [pytest.mark.django_db]
6. Naming Conventions (P2)
Event names:
{prefix}.{object}.{action}[.error]
Schema names: Mxp{Domain}{Action}EventSchema
Helper names: OptimoMixpanel{Domain}TrackHelper
7. is_cron_job
Usage (P2)
is_cron_jobNOTE: Not all background jobs need
is_cron_job=True. Only use when:
- API time and tracking time need to align
- Events with same timestamp need ordering
IF
is used, MUST VERIFY:is_cron_job=True
-
is provided as Unix millisecondscron_execution_timestamp - Event name does NOT contain "cron"
8. Timestamp Handling (P2)
MUST VERIFY:
- Uses
for MixPanel timestampsdatetime_to_timestamp_ms() - Never sends ISO 8601 strings to MixPanel
9. distinct_id Selection (P1)
distinct_id MUST strictly follow this fallback hierarchy:
- Primary: User's UUID (the authenticated user performing the action)
- Fallback 1:
(when no user context exists)org_<organization_uuid> - Fallback 2: Context-specific ID based on the entity being tracked:
- Slack workspace:
slack_<slack_workspace_id> - API key:
apikey_<api_key_id> - Webhook:
webhook_<webhook_id>
- Slack workspace:
NEVER pass organization_id directly as distinct_id - always prefix with
org_.
MUST VERIFY:
- distinct_id is user's UUID when user context is available
- distinct_id uses
prefix when falling back to organizationorg_<uuid> - distinct_id uses appropriate prefix for context-specific fallbacks
- distinct_id is NEVER a raw organization_id without prefix
10. Export Completeness (P3)
MUST VERIFY:
- New helper classes exported in
service/__init__.py - Added to
list__all__
Automated Checks
# 1. PII Scan grep -rn "first_name\|last_name\|email\|phone\|address" optimo_analytics/schemas.py # 2. UUID Type Check grep -rn ": UUID" optimo_analytics/schemas.py # 3. Registration Check for event in $(grep "^ [A-Z_]* = " optimo_analytics/constants.py | cut -d'=' -f1 | tr -d ' '); do grep -q "$event" optimo_analytics/registry.py || echo "UNREGISTERED: $event" done # 4. Keyword-only Check grep -rn "def track_" optimo_analytics/service/*.py | while read line; do file=$(echo $line | cut -d: -f1) linenum=$(echo $line | cut -d: -f2) if ! sed -n "$((linenum+1)),$((linenum+5))p" "$file" | grep -q '\*,'; then echo "MISSING *,: $line" fi done
Review Output Format
# MixPanel Implementation Review **Branch**: {branch} **Scope**: {scope} **Date**: {date} ## Summary | Category | Status | Issues | |----------|--------|--------| | PII Protection | PASS/FAIL | {count} | | Event Registration | PASS/FAIL | {count} | | Schema Design | PASS/FAIL | {count} | | Service Patterns | PASS/FAIL | {count} | | Test Coverage | PASS/FAIL | {count} | ## Issues Found ### [P0] CRITICAL - {title} **File**: `path:line` **Issue**: Description **Fix**: How to fix ### [P1] HIGH - {title} ... ## Recommendations 1. ... 2. ...
Severity Tags
CRITICAL – PII violations, security issues; must fix before merge[P0]
HIGH – Missing registrations, pattern violations; strongly recommended[P1]
MEDIUM – Test coverage gaps, naming issues; should fix[P2]
LOW – Minor improvements; nice to have[P3]
Post-Review Actions
After review, if issues found:
- Create todo list of fixes
- Apply fixes using
/mixpanel-analytics:implement - Re-run review to verify
If review passes:
- Run
for general code quality/monty-code-review:code-review - Run
for commit preparation/backend-atomic-commit:pre-commit - Run tests:
.bin/pytest optimo_analytics/tests/ -v --dc=TestLocalApp
Compatibility Notes
This skill is designed to work with both Claude Code and OpenAI Codex.
For Codex users:
- Install via skill-installer with
.--repo DiversioTeam/agent-skills-marketplace --path plugins/mixpanel-analytics/skills/mixpanel-analytics - Use
to invoke.$skill mixpanel-analytics
For Claude Code users:
- Install via
./plugin install mixpanel-analytics@diversiotech - Use
or/mixpanel-analytics:implement
to invoke./mixpanel-analytics:review