├── ee ├── __init__.py ├── api │ ├── __init__.py │ ├── test │ │ ├── __init__.py │ │ └── __snapshots__ │ │ │ └── test_instance_settings.ambr │ └── debug_ch_queries.py ├── tasks │ ├── __init__.py │ ├── test │ │ ├── __init__.py │ │ └── subscriptions │ │ │ ├── __init__.py │ │ │ └── subscriptions_test_factory.py │ └── org_usage_report.py ├── benchmarks │ └── __init__.py ├── clickhouse │ ├── __init__.py │ ├── models │ │ ├── __init__.py │ │ ├── group.py │ │ └── test │ │ │ ├── __init__.py │ │ │ ├── __snapshots__ │ │ │ └── test_cohort.ambr │ │ │ └── utils │ │ │ └── util.py │ ├── queries │ │ ├── __init__.py │ │ ├── test │ │ │ ├── __init__.py │ │ │ └── test_person_distinct_id_query.py │ │ ├── funnels │ │ │ ├── __init__.py │ │ │ └── test │ │ │ │ └── __init__.py │ │ ├── session_recordings │ │ │ ├── __init__.py │ │ │ └── test │ │ │ │ └── test_clickhouse_session_recording.py │ │ ├── retention │ │ │ ├── __init__.py │ │ │ └── retention.py │ │ ├── stickiness │ │ │ ├── __init__.py │ │ │ ├── stickiness.py │ │ │ ├── stickiness_event_query.py │ │ │ └── stickiness_actors.py │ │ ├── paths │ │ │ ├── __init__.py │ │ │ ├── paths_event_query.py │ │ │ └── paths_actors.py │ │ └── experiments │ │ │ └── __init__.py │ ├── test │ │ ├── __init__.py │ │ └── test_calculate_event_property_usage.py │ ├── views │ │ ├── __init__.py │ │ └── test │ │ │ ├── __init__.py │ │ │ └── funnel │ │ │ └── __init__.py │ ├── materialized_columns │ │ ├── util.py │ │ ├── __init__.py │ │ └── test │ │ │ └── __init__.py │ └── bin │ │ └── clickhouse-flamegraph ├── migrations │ ├── __init__.py │ ├── 0003_license_max_users.py │ ├── 0010_migrate_definitions_tags.py │ └── 0009_deprecated_old_tags.py ├── models │ ├── test │ │ └── __init__.py │ └── __init__.py ├── conftest.py ├── bin │ ├── docker-ch-dev-web │ ├── docker-ch-dev-backend │ └── docker-ch-test ├── apps.py └── pytest.ini ├── staticfiles └── empty.txt ├── posthog ├── api │ ├── test │ │ ├── __init__.py │ │ └── mock_sentry.py │ ├── forbid_destroy_model.py │ └── unsubscribe.py ├── queries │ ├── __init__.py │ ├── test │ │ └── __init__.py │ ├── trends │ │ └── __init__.py │ ├── funnels │ │ ├── test │ │ │ └── __init__.py │ │ ├── sql.py │ │ └── __init__.py │ ├── column_optimizer │ │ ├── __init__.py │ │ └── column_optimizer.py │ ├── session_recordings │ │ └── __init__.py │ ├── retention │ │ ├── types.py │ │ └── __init__.py │ ├── event_query │ │ └── __init__.py │ ├── groups_join_query │ │ └── __init__.py │ ├── cohort_query.py │ ├── paths │ │ └── __init__.py │ └── stickiness │ │ └── __init__.py ├── test │ ├── __init__.py │ ├── activity_logging │ │ └── __init__.py │ ├── mock_urls_cloud.py │ ├── mock_urls_self_hosted.py │ └── db_context_capturing.py ├── clickhouse │ ├── __init__.py │ ├── test │ │ └── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0002_events_materialized.py │ │ ├── 0011_cohortpeople_no_shard.py │ │ ├── 0010_cohortpeople.py │ │ ├── 0007_static_cohorts_table.py │ │ ├── 0025_json_events.py │ │ ├── 0031_event_properties_zstd.py │ │ ├── 0018_group_analytics_schema.py │ │ ├── 0001_initial.py │ │ ├── 0022_person_distinct_id2.py │ │ ├── 0008_plugin_log_entries.py │ │ ├── 0017_events_dead_letter_queue.py │ │ └── 0024_materialize_window_and_session_id.py │ ├── replication │ │ ├── __init__.py │ │ └── utils.py │ └── materialized_columns │ │ └── __init__.py ├── helpers │ ├── tests │ │ └── __init__.py │ └── __init__.py ├── kafka_client │ └── __init__.py ├── migrations │ ├── __init__.py │ ├── 0175_should_update_person_props_function.py │ ├── 0244_drop_should_update_person_prop.py │ ├── 0041_merge_20200407_1805.py │ ├── 0049_delete_funnelstep.py │ ├── 0184_delete_sessionsfilter.py │ ├── 0129_merge_20210223_0757.py │ ├── 0148_merge_20210506_0823.py │ ├── 0239_delete_postgres_pluginlogentry.py │ ├── 0040_remove_event_ip.py │ ├── 0117_merge_20210126_0917.py │ ├── 0176_update_person_props_function.py │ ├── 0005_remove_person_distinct_ids.py │ ├── 0093_remove_user_is_superuser.py │ ├── 0009_auto_20200127_0018.py │ ├── 0173_should_update_person_props_function.py │ ├── 0036_remove_current_url_index.py │ ├── 0191_rename_specialmigration_asyncmigration.py │ ├── 0018_funnel_deleted.py │ ├── 0022_action_deleted.py │ ├── 0062_team_anonymize_ips.py │ ├── 0067_team_updated_at.py │ ├── 0113_cohort_is_static.py │ ├── 0207_cohort_count.py │ ├── 0210_drop_update_person_functions.py │ ├── 0023_team_opt_out_capture.py │ ├── 0057_action_updated_at.py │ ├── 0138_featureflag_name_optional.py │ ├── 0157_plugin_metrics.py │ ├── 0153_plugin_capabilities.py │ ├── 0180_person_version.py │ ├── 0105_cohort_errors_calculating.py │ ├── 0151_plugin_preinstalled.py │ ├── 0209_plugin_logs_disabled.py │ ├── 0230_cohort_filters.py │ ├── 0034_pg_trgm_and_btree_20200318_1447.py │ ├── 0039_user_email_opt_in.py │ ├── 0059_dashboarditem_refreshing.py │ ├── 0081_person_is_identified.py │ ├── 0166_plugin_public_jobs.py │ ├── 0211_async_migrations_errors_length.py │ ├── 0019_team_name.py │ ├── 0202_descriptions_for_action.py │ ├── 0214_migrate_dashboard_insight_tags.py │ ├── 0012_team_app_url.py │ ├── 0097_invite_emails.py │ ├── 0208_alter_plugin_updated_at.py │ ├── 0086_team_session_recording_opt_in.py │ ├── 0015_actionstep_event.py │ ├── 0045_add_timestamp_index.py │ ├── 0119_mandatory_plugin_order.py │ ├── 0134_event_site_url.py │ ├── 0186_insight_refresh_attempt.py │ ├── 0188_person_distinct_id_version.py │ ├── 0016_user_temporary_token.py │ ├── 0058_dashboarditem_last_refresh.py │ ├── 0060_auto_20200616_0746.py │ ├── 0171_cohort_description.py │ ├── 0216_insight_placeholder_name.py │ ├── 0054_dashboard_item_color.py │ ├── 0055_user_anonymize_data.py │ ├── 0181_team_correlation_config.py │ ├── 0197_plugin_is_stateless.py │ ├── 0076_auto_20200819_1214.py │ ├── 0077_cohortpeople_id_to_bigautofield.py │ ├── 0172_person_properties_last_operation.py │ ├── 0240_organizationinvite_message.py │ ├── 0047_auto_20200416_1631.py │ ├── 0178_rename_dashboard_item_to_insight.py │ ├── 0065_auto_20200624_1842.py │ ├── 0084_person_uuid.py │ ├── 0122_organization_setup_section_2_completed.py │ ├── 0162_organization_is_member_join_email_enabled.py │ ├── 0089_auto_20201015_1031.py │ ├── 0104_auto_20201208_1052.py │ ├── 0024_add_event_distinct_id_index.py │ ├── 0075_action_slack_message_format.py │ ├── 0177_path_cleaning_filters.py │ ├── 0035_current_url_index_20200318_1459.py │ ├── 0039_populate_event_ip_property.py │ ├── 0127_add_dashboard_filters.py │ ├── 0064_toolbar_mode.py │ ├── 0125_longer_webhook_url.py │ ├── 0226_longer_action_slack_message_format.py │ ├── 0182_sessionrecordingevent_window_id.py │ ├── 0020_auto_20200210_0212.py │ ├── 0116_session_recording_retention_period.py │ ├── 0123_organizationinvite_first_name.py │ ├── 0169_person_properties_last_updated_at.py │ ├── 0004_auto_20200125_0415.py │ ├── 0053_dashboard_item_layouts.py │ ├── 0121_person_email_index.py │ ├── 0087_fix_annotation_created_at.py │ ├── 0070_team_event_properties_numerical.py │ ├── 0120_organization_personalization.py │ ├── 0152_user_events_column_config.py │ ├── 0066_team_created_at.py │ ├── 0199_update_experiment_model.py │ ├── 0048_auto_20200420_1051.py │ ├── 0136_global_plugin_attachments.py │ ├── 0028_actionstep_url_matching.py │ ├── 0110_sessionrecordingeventbyteamandtimestamp.py │ └── 0237_remove_timezone_from_teams.py ├── models │ ├── test │ │ └── __init__.py │ ├── activity_logging │ │ └── __init__.py │ ├── filters │ │ ├── mixins │ │ │ ├── __init__.py │ │ │ ├── test │ │ │ │ └── __init__.py │ │ │ └── base.py │ │ ├── __init__.py │ │ └── session_recordings_filter.py │ ├── event │ │ └── __init__.py │ ├── group │ │ └── __init__.py │ ├── team │ │ └── __init__.py │ ├── cohort │ │ └── __init__.py │ ├── element │ │ └── __init__.py │ ├── entity │ │ └── __init__.py │ ├── person │ │ └── __init__.py │ ├── property │ │ └── __init__.py │ ├── session_recording_event │ │ └── __init__.py │ ├── action │ │ └── __init__.py │ ├── event_buffer.py │ └── tag.py ├── plugins │ ├── test │ │ └── __init__.py │ ├── __init__.py │ └── reload.py ├── tasks │ ├── exports │ │ └── __init__.py │ ├── test │ │ └── __init__.py │ ├── user_identify.py │ ├── sync_all_organization_available_features.py │ ├── delete_clickhouse_data.py │ └── split_person.py ├── templatetags │ └── __init__.py ├── async_migrations │ ├── __init__.py │ ├── test │ │ └── __init__.py │ ├── examples │ │ └── __init__.py │ └── migrations │ │ ├── __init__.py │ │ └── 0001_events_sample_by.py ├── gitsha.py ├── version.py ├── management │ └── commands │ │ ├── test │ │ └── __init__.py │ │ ├── api_keys.py │ │ └── sync_available_features.py ├── settings │ ├── geoip.py │ ├── ee.py │ ├── statsd.py │ ├── ingestion.py │ ├── feature_flags.py │ ├── cloud.py │ └── shell_plus.py ├── demo │ └── matrix │ │ └── __init__.py ├── __init__.py ├── templates │ ├── email │ │ ├── async_migration_status.html │ │ ├── async_migration_error.html │ │ └── canary_email.html │ └── live_server_inject.html ├── types.py ├── wsgi.py ├── idl │ └── idl.md ├── logging │ ├── timing.py │ └── test │ │ └── test_timing.py └── datetime.py ├── Dockerfile ├── runtime.txt ├── depot.json ├── frontend ├── src │ ├── scenes │ │ ├── billing │ │ │ └── index.ts │ │ ├── organization │ │ │ ├── TeamMembers │ │ │ │ └── index.tsx │ │ │ └── Create │ │ │ │ └── index.tsx │ │ ├── insights │ │ │ ├── EmptyStates │ │ │ │ └── index.ts │ │ │ ├── views │ │ │ │ ├── Histogram │ │ │ │ │ └── index.js │ │ │ │ ├── InsightsTable │ │ │ │ │ └── index.tsx │ │ │ │ ├── WorldMap │ │ │ │ │ ├── index.ts │ │ │ │ │ └── WorldMap.scss │ │ │ │ └── LineGraph │ │ │ │ │ └── LineGraph.scss │ │ │ ├── filters │ │ │ │ ├── InsightDateFilter │ │ │ │ │ └── index.ts │ │ │ │ ├── TestAccountFilter │ │ │ │ │ └── index.ts │ │ │ │ └── BreakdownFilter │ │ │ │ │ └── index.ts │ │ │ ├── __image_snapshots__ │ │ │ │ ├── Insights trends can render bar graphs #0.png │ │ │ │ └── Insights trends can render bar graphs #1.png │ │ │ └── EditorFilters │ │ │ │ └── LifecycleGlobalFilters.tsx │ │ ├── authentication │ │ │ ├── PasswordReset.scss │ │ │ └── PasswordInput.scss │ │ ├── funnels │ │ │ ├── FunnelHistogram.scss │ │ │ └── FunnelCanvasLabel.scss │ │ ├── trends │ │ │ └── viz │ │ │ │ └── index.ts │ │ ├── events │ │ │ └── index.ts │ │ ├── toolbar-launch │ │ │ └── AuthorizedUrls.scss │ │ ├── plugins │ │ │ ├── source │ │ │ │ └── PluginSource.scss │ │ │ ├── plugin │ │ │ │ ├── SourcePluginTag.tsx │ │ │ │ └── styles │ │ │ │ │ └── metrics-drawer.scss │ │ │ └── edit │ │ │ │ └── CapabilitiesInfo.ts │ │ ├── Unsubscribe │ │ │ ├── Unsubscribe.scss │ │ │ └── Unsubscribe.stories.tsx │ │ ├── persons │ │ │ ├── PersonHeader.scss │ │ │ └── Persons.scss │ │ ├── cohorts │ │ │ └── Cohort.scss │ │ ├── instance │ │ │ ├── SystemStatus │ │ │ │ └── index.scss │ │ │ └── Licenses │ │ │ │ └── Licenses.stories.tsx │ │ ├── PreflightCheck │ │ │ └── PreflightCheck.stories.tsx │ │ ├── ingestion │ │ │ ├── Ingestion.stories.tsx │ │ │ ├── types.ts │ │ │ └── frameworks │ │ │ │ ├── index.tsx │ │ │ │ └── CodeSnippet.scss │ │ ├── project │ │ │ └── Create │ │ │ │ └── index.tsx │ │ └── retention │ │ │ └── RetentionTable.scss │ ├── lib │ │ ├── components │ │ │ ├── CompareFilter │ │ │ │ └── index.ts │ │ │ ├── ChartFilter │ │ │ │ └── index.ts │ │ │ ├── IntervalFilter │ │ │ │ ├── index.ts │ │ │ │ └── intervals.ts │ │ │ ├── SmoothingFilter │ │ │ │ └── index.ts │ │ │ ├── InsightCard │ │ │ │ └── index.tsx │ │ │ ├── LemonCheckbox │ │ │ │ └── index.ts │ │ │ ├── PersonalAPIKeys │ │ │ │ └── index.tsx │ │ │ ├── Sharing │ │ │ │ └── SharingModal.scss │ │ │ ├── LemonModal │ │ │ │ └── index.ts │ │ │ ├── LemonRow │ │ │ │ └── index.ts │ │ │ ├── AlertMessage │ │ │ │ └── index.ts │ │ │ ├── Annotations │ │ │ │ ├── index.ts │ │ │ │ └── utils.ts │ │ │ ├── LemonDivider │ │ │ │ └── index.ts │ │ │ ├── ActivityLog │ │ │ │ └── SentenceList.scss │ │ │ ├── PropertyKeyInfo.scss │ │ │ ├── UserActivityIndicator │ │ │ │ └── UserActivityIndicator.scss │ │ │ ├── ProfilePicture │ │ │ │ └── index.ts │ │ │ ├── DatePicker.tsx │ │ │ ├── LemonTable │ │ │ │ └── index.ts │ │ │ ├── LemonButton │ │ │ │ └── index.ts │ │ │ ├── HelpButton │ │ │ │ └── HelpButton.scss │ │ │ ├── PaginationControl │ │ │ │ ├── index.tsx │ │ │ │ └── PaginationControl.scss │ │ │ ├── DurationPicker │ │ │ │ └── DurationPicker.scss │ │ │ ├── AddToDashboard │ │ │ │ └── AddToDashboard.scss │ │ │ ├── PropertyFilters │ │ │ │ ├── components │ │ │ │ │ └── FilterRow.scss │ │ │ │ └── PropertyFilters.scss │ │ │ ├── Fade │ │ │ │ └── Fade.scss │ │ │ ├── EmptyMessage │ │ │ │ └── EmptyMessage.scss │ │ │ ├── Property.tsx │ │ │ ├── Drawer.tsx │ │ │ ├── LinkButton.tsx │ │ │ ├── CloseButton.tsx │ │ │ ├── NotFound │ │ │ │ ├── NotFound.scss │ │ │ │ └── NotFound.stories.tsx │ │ │ ├── SelectDownIcon.tsx │ │ │ ├── LemonSnack │ │ │ │ └── LemonSnack.scss │ │ │ ├── StarryBackground │ │ │ │ ├── index.tsx │ │ │ │ └── index.scss │ │ │ ├── Lettermark │ │ │ │ └── Lettermark.scss │ │ │ ├── DateDisplay │ │ │ │ └── DateDisplay.scss │ │ │ ├── TitleWithIcon.tsx │ │ │ ├── icons.scss │ │ │ ├── DateFilter │ │ │ │ └── DateFilterRange.scss │ │ │ ├── PayCard │ │ │ │ └── PayCard.scss │ │ │ ├── icons │ │ │ │ └── Splotch.stories.tsx │ │ │ ├── HotkeyButton │ │ │ │ └── HotkeyButton.tsx │ │ │ └── PayGateMini │ │ │ │ └── PayGateMini.scss │ │ ├── experimental │ │ │ └── README.md │ │ ├── hooks │ │ │ ├── usePeriodicRerender.ts │ │ │ ├── useSecondRender.js │ │ │ ├── useBreakpoint.ts │ │ │ ├── useEscapeKey.js │ │ │ └── useInterval.js │ │ ├── internalMetrics.ts │ │ └── utils │ │ │ ├── getAppContext.ts │ │ │ ├── logics.ts │ │ │ └── changeType.js │ ├── test │ │ └── mocks │ │ │ └── styleMock.js │ ├── layout │ │ ├── ErrorBoundary │ │ │ └── index.ts │ │ ├── navigation │ │ │ └── DemoWarnings │ │ │ │ └── DemoWarnings.scss │ │ ├── Error404.tsx │ │ └── ErrorNetwork.tsx │ ├── toolbar │ │ ├── elements │ │ │ └── Elements.scss │ │ └── button │ │ │ └── icons │ │ │ └── Close.tsx │ ├── styles │ │ ├── vars.ts │ │ └── index.tsx │ ├── mocks │ │ ├── features.ts │ │ └── jest.ts │ ├── stories │ │ └── Missing components.stories.mdx │ ├── exporter │ │ └── types.ts │ ├── globals.d.ts │ └── custom.d.ts ├── public │ ├── Inter.woff │ ├── squeak.mp3 │ ├── Inter.woff2 │ ├── hedgehog-blue.png │ ├── icons │ │ ├── favicon.ico │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-dev.ico │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ └── android-chrome-512x512.png │ ├── posthog-logo.png │ ├── surprised-hog.png │ ├── email │ │ ├── arrow-up-red.png │ │ ├── arrow-down-red.png │ │ ├── arrow-up-green.png │ │ ├── arrow-down-green.png │ │ └── invite-email-welcome.png │ ├── hedgehog-bridge-page.png │ └── site.webmanifest └── @posthog │ ├── apps-common │ ├── README.md │ ├── tsconfig.json │ └── src │ │ └── index.ts │ └── lemon-ui │ ├── tsconfig.json │ └── README.md ├── cypress ├── fixtures │ ├── api │ │ ├── personal_api_keys.json │ │ ├── cohort │ │ │ └── cohorts.json │ │ ├── event │ │ │ ├── only_time_property_definition.json │ │ │ └── only_browser_version_property_definition.json │ │ ├── person │ │ │ └── properties.json │ │ └── decide.js │ └── example.json ├── data │ └── exports │ │ └── export-pageview-count.png ├── e2e │ ├── commandPalette.js │ ├── persons.js │ ├── organizationSettings.js │ ├── systemStatus.js │ ├── licenses.js │ └── annotations.js └── README.md ├── .environment ├── bin ├── docker-migrate ├── start-backend ├── docker ├── docker-backend ├── docker-frontend ├── docker-dev-web ├── docker-dev ├── docker-worker-beat ├── migrate-check ├── start-frontend ├── migrate ├── start-frontend-https ├── start-https ├── docker-worker ├── check_kafka_clickhouse_up ├── start-worker └── install-macosx_arm64 ├── .husky └── pre-commit ├── jest.setup.ts ├── plugin-server ├── bin │ └── posthog-plugin-server ├── tests │ ├── assets │ │ └── GeoLite2-City-Test.mmdb.br │ └── helpers │ │ ├── promises.ts │ │ └── worker.ts ├── src │ ├── worker │ │ ├── piscina.d.ts │ │ ├── vm │ │ │ ├── transforms │ │ │ │ └── common.ts │ │ │ └── extensions │ │ │ │ ├── geoip.ts │ │ │ │ └── google.ts │ │ └── ingestion │ │ │ └── event-pipeline │ │ │ └── 5-createEventStep.ts │ ├── utils │ │ ├── node-schedule.ts │ │ ├── posthog.ts │ │ ├── kill.ts │ │ └── fetch.ts │ ├── init.ts │ ├── config │ │ ├── constants.ts │ │ └── mmdb-constants.ts │ └── main │ │ └── job-queues │ │ └── buffer.ts ├── .babelrc ├── .editorconfig ├── tsconfig.eslint.json └── jest.config.js ├── codecov.yml ├── docker └── clickhouse │ └── docker-entrypoint-initdb.d │ └── init-db.sh ├── .prettierrc ├── .coveragerc ├── .devcontainer └── container_start.sh ├── .kearc ├── pyproject.toml ├── Procfile ├── pytest.ini ├── .prettierignore ├── postcss.config.js ├── share └── share.md ├── .storybook ├── storybook.scss └── app-context.ts ├── latest_migrations.manifest ├── SECURITY.md ├── .run ├── Frontend.run.xml ├── Storybook.run.xml └── Jest Tests.run.xml ├── CHANGELOG.md ├── .deepsource.toml ├── mypy.ini └── babel.config.js /ee/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/api/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/tasks/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /staticfiles/empty.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/benchmarks/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/models/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/tasks/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/api/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/queries/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | production.Dockerfile -------------------------------------------------------------------------------- /ee/clickhouse/models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/models/group.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/clickhouse/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/helpers/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/kafka_client/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/models/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/plugins/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/queries/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/tasks/exports/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/tasks/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.12 2 | -------------------------------------------------------------------------------- /depot.json: -------------------------------------------------------------------------------- 1 | { "id": "x19jffd9zf" } 2 | -------------------------------------------------------------------------------- /ee/clickhouse/models/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/views/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/scenes/billing/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/async_migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/clickhouse/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/queries/trends/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/materialized_columns/util.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/funnels/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/views/test/funnel/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/tasks/test/subscriptions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/async_migrations/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/clickhouse/replication/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/gitsha.py: -------------------------------------------------------------------------------- 1 | GIT_SHA = "Undefined" 2 | -------------------------------------------------------------------------------- /posthog/models/activity_logging/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/models/filters/mixins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/queries/funnels/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/test/activity_logging/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/version.py: -------------------------------------------------------------------------------- 1 | VERSION = "1.38.0" 2 | -------------------------------------------------------------------------------- /ee/clickhouse/materialized_columns/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/funnels/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/async_migrations/examples/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/async_migrations/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/management/commands/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/models/filters/mixins/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/queries/column_optimizer/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/queries/session_recordings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cypress/fixtures/api/personal_api_keys.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /ee/clickhouse/materialized_columns/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/session_recordings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/lib/components/CompareFilter/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.environment: -------------------------------------------------------------------------------- 1 | export SECRET_KEY=$PLATFORM_PROJECT_ENTROPY -------------------------------------------------------------------------------- /frontend/src/scenes/organization/TeamMembers/index.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posthog/models/event/__init__.py: -------------------------------------------------------------------------------- 1 | from .event import * 2 | -------------------------------------------------------------------------------- /posthog/models/group/__init__.py: -------------------------------------------------------------------------------- 1 | from .group import * 2 | -------------------------------------------------------------------------------- /posthog/models/team/__init__.py: -------------------------------------------------------------------------------- 1 | from .team import * 2 | -------------------------------------------------------------------------------- /frontend/src/test/mocks/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {} 2 | -------------------------------------------------------------------------------- /posthog/models/cohort/__init__.py: -------------------------------------------------------------------------------- 1 | from .cohort import * 2 | -------------------------------------------------------------------------------- /posthog/models/element/__init__.py: -------------------------------------------------------------------------------- 1 | from .element import * 2 | -------------------------------------------------------------------------------- /posthog/models/entity/__init__.py: -------------------------------------------------------------------------------- 1 | from .entity import * 2 | -------------------------------------------------------------------------------- /posthog/models/person/__init__.py: -------------------------------------------------------------------------------- 1 | from .person import * 2 | -------------------------------------------------------------------------------- /posthog/models/property/__init__.py: -------------------------------------------------------------------------------- 1 | from .property import * 2 | -------------------------------------------------------------------------------- /ee/conftest.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from posthog.conftest import * 3 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/retention/__init__.py: -------------------------------------------------------------------------------- 1 | from .retention import * 2 | -------------------------------------------------------------------------------- /bin/docker-migrate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | python manage.py migrate -------------------------------------------------------------------------------- /bin/start-backend: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | python3 manage.py runserver -------------------------------------------------------------------------------- /ee/clickhouse/queries/stickiness/__init__.py: -------------------------------------------------------------------------------- 1 | from .stickiness import * 2 | -------------------------------------------------------------------------------- /frontend/src/lib/components/ChartFilter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ChartFilter' 2 | -------------------------------------------------------------------------------- /frontend/src/scenes/insights/EmptyStates/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EmptyStates' 2 | -------------------------------------------------------------------------------- /frontend/src/lib/components/IntervalFilter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './IntervalFilter' 2 | -------------------------------------------------------------------------------- /frontend/src/scenes/authentication/PasswordReset.scss: -------------------------------------------------------------------------------- 1 | @import './bridgePagesShared'; 2 | -------------------------------------------------------------------------------- /frontend/src/scenes/insights/views/Histogram/index.js: -------------------------------------------------------------------------------- 1 | export * from './Histogram' 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /frontend/src/lib/components/SmoothingFilter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SmoothingFilter' 2 | -------------------------------------------------------------------------------- /posthog/models/session_recording_event/__init__.py: -------------------------------------------------------------------------------- 1 | from .session_recording_event import * 2 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/paths/__init__.py: -------------------------------------------------------------------------------- 1 | from .paths import * 2 | from .paths_actors import * 3 | -------------------------------------------------------------------------------- /frontend/src/layout/ErrorBoundary/index.ts: -------------------------------------------------------------------------------- 1 | export { ErrorBoundary } from './ErrorBoundary' 2 | -------------------------------------------------------------------------------- /frontend/src/lib/components/InsightCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { InsightCard } from './InsightCard' 2 | -------------------------------------------------------------------------------- /posthog/models/action/__init__.py: -------------------------------------------------------------------------------- 1 | from .action import Action 2 | 3 | __all__ = ["Action"] 4 | -------------------------------------------------------------------------------- /frontend/src/lib/components/LemonCheckbox/index.ts: -------------------------------------------------------------------------------- 1 | export { LemonCheckbox } from './LemonCheckbox' 2 | -------------------------------------------------------------------------------- /frontend/src/scenes/insights/filters/InsightDateFilter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './InsightDateFilter' 2 | -------------------------------------------------------------------------------- /frontend/src/scenes/insights/filters/TestAccountFilter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TestAccountFilter' 2 | -------------------------------------------------------------------------------- /cypress/fixtures/api/cohort/cohorts.json: -------------------------------------------------------------------------------- 1 | { "count": 0, "next": null, "previous": null, "results": [] } 2 | -------------------------------------------------------------------------------- /frontend/public/Inter.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/Inter.woff -------------------------------------------------------------------------------- /frontend/public/squeak.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/squeak.mp3 -------------------------------------------------------------------------------- /jest.setup.ts: -------------------------------------------------------------------------------- 1 | import 'whatwg-fetch' 2 | import 'jest-canvas-mock' 3 | 4 | window.scrollTo = jest.fn() 5 | -------------------------------------------------------------------------------- /bin/docker: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ./bin/migrate 5 | ./bin/docker-worker & 6 | ./bin/docker-server 7 | -------------------------------------------------------------------------------- /frontend/public/Inter.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/Inter.woff2 -------------------------------------------------------------------------------- /frontend/src/lib/components/PersonalAPIKeys/index.tsx: -------------------------------------------------------------------------------- 1 | export { PersonalAPIKeys } from './PersonalAPIKeys' 2 | -------------------------------------------------------------------------------- /plugin-server/bin/posthog-plugin-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require(__dirname + '/../dist/index.js') 4 | -------------------------------------------------------------------------------- /ee/bin/docker-ch-dev-web: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ./ee/bin/docker-ch-dev-backend & ./bin/docker-frontend 6 | -------------------------------------------------------------------------------- /frontend/public/hedgehog-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/hedgehog-blue.png -------------------------------------------------------------------------------- /frontend/public/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/icons/favicon.ico -------------------------------------------------------------------------------- /frontend/public/posthog-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/posthog-logo.png -------------------------------------------------------------------------------- /frontend/public/surprised-hog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/surprised-hog.png -------------------------------------------------------------------------------- /frontend/src/scenes/insights/filters/BreakdownFilter/index.ts: -------------------------------------------------------------------------------- 1 | export { BreakdownFilter } from './TaxonomicBreakdownFilter' 2 | -------------------------------------------------------------------------------- /bin/docker-backend: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | python manage.py migrate 6 | 7 | python manage.py runserver 0.0.0.0:8000 8 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # Disable PR comments for now to not spam PRs. We should still have annotations 2 | # on files 3 | comment: false 4 | -------------------------------------------------------------------------------- /ee/bin/docker-ch-dev-backend: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ./bin/migrate 6 | 7 | python manage.py runserver 0.0.0.0:8000 8 | -------------------------------------------------------------------------------- /ee/clickhouse/bin/clickhouse-flamegraph: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/ee/clickhouse/bin/clickhouse-flamegraph -------------------------------------------------------------------------------- /frontend/public/email/arrow-up-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/email/arrow-up-red.png -------------------------------------------------------------------------------- /frontend/public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /frontend/public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /frontend/public/icons/favicon-dev.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/icons/favicon-dev.ico -------------------------------------------------------------------------------- /frontend/public/email/arrow-down-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/email/arrow-down-red.png -------------------------------------------------------------------------------- /frontend/public/email/arrow-up-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/email/arrow-up-green.png -------------------------------------------------------------------------------- /frontend/public/hedgehog-bridge-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/hedgehog-bridge-page.png -------------------------------------------------------------------------------- /bin/docker-frontend: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | export WEBPACK_HOT_RELOAD_HOST="0.0.0.0" 5 | 6 | yarn install 7 | yarn start-docker 8 | -------------------------------------------------------------------------------- /docker/clickhouse/docker-entrypoint-initdb.d/init-db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cp -r /idl/* /var/lib/clickhouse/format_schemas/ 5 | -------------------------------------------------------------------------------- /frontend/public/email/arrow-down-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/email/arrow-down-green.png -------------------------------------------------------------------------------- /frontend/public/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /frontend/src/scenes/insights/views/InsightsTable/index.tsx: -------------------------------------------------------------------------------- 1 | import { InsightsTable } from './InsightsTable' 2 | export { InsightsTable } 3 | -------------------------------------------------------------------------------- /frontend/src/toolbar/elements/Elements.scss: -------------------------------------------------------------------------------- 1 | #posthog-toolbar-elements, 2 | #posthog-infowindow-container { 3 | color: #333333; 4 | } 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "printWidth": 120, 7 | } 8 | -------------------------------------------------------------------------------- /cypress/data/exports/export-pageview-count.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/cypress/data/exports/export-pageview-count.png -------------------------------------------------------------------------------- /frontend/public/email/invite-email-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/email/invite-email-welcome.png -------------------------------------------------------------------------------- /frontend/src/lib/components/Sharing/SharingModal.scss: -------------------------------------------------------------------------------- 1 | .SharingPreview { 2 | background-color: var(--bg-mid); 3 | padding: 0.5rem; 4 | } 5 | -------------------------------------------------------------------------------- /posthog/settings/geoip.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.conf import settings 4 | 5 | GEOIP_PATH = os.path.join(settings.BASE_DIR, "share") 6 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = 3 | posthog/ 4 | ee/ 5 | 6 | branch = true 7 | 8 | omit = 9 | */migrations/* 10 | manage.py 11 | -------------------------------------------------------------------------------- /.devcontainer/container_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/env zsh 2 | 3 | echo "printf 'Hello 🦔! To start PostHog run this:\n "./ee/bin/docker-ch-dev-web"\n'" > ~/.zshrc -------------------------------------------------------------------------------- /bin/docker-dev-web: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | python manage.py migrate 5 | 6 | python manage.py runserver 0.0.0.0:8000 & ./bin/docker-frontend 7 | -------------------------------------------------------------------------------- /ee/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class EnterpriseConfig(AppConfig): 5 | name = "ee" 6 | verbose_name = "Enterprise" 7 | -------------------------------------------------------------------------------- /frontend/public/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /frontend/public/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/public/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /frontend/src/lib/components/LemonModal/index.ts: -------------------------------------------------------------------------------- 1 | export { LemonModal } from './LemonModal' 2 | export type { LemonModalProps } from './LemonModal' 3 | -------------------------------------------------------------------------------- /posthog/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | from .dashboard_templates import create_dashboard_from_template 2 | 3 | __all__ = ["create_dashboard_from_template"] 4 | -------------------------------------------------------------------------------- /frontend/src/lib/components/LemonRow/index.ts: -------------------------------------------------------------------------------- 1 | export { LemonRow } from './LemonRow' 2 | export type { LemonRowProps, LemonRowPropsBase } from './LemonRow' 3 | -------------------------------------------------------------------------------- /frontend/src/scenes/funnels/FunnelHistogram.scss: -------------------------------------------------------------------------------- 1 | .funnel-histogram-outer-container { 2 | &.scrollable { 3 | overflow-x: auto; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/scenes/trends/viz/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ActionsPie' 2 | export * from './ActionsLineGraph' 3 | export * from './ActionsHorizontalBar' 4 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/0002_events_materialized.py: -------------------------------------------------------------------------------- 1 | from infi.clickhouse_orm import migrations 2 | 3 | operations = [migrations.RunSQL("SELECT 1")] 4 | -------------------------------------------------------------------------------- /.kearc: -------------------------------------------------------------------------------- 1 | { 2 | "tsConfigPath": "./tsconfig.json", 3 | "rootPath": "./frontend/src", 4 | "typesPath": "./frontend/src", 5 | "writePaths": true 6 | } 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/lib/components/AlertMessage/index.ts: -------------------------------------------------------------------------------- 1 | export { AlertMessage } from './AlertMessage' 2 | export type { AlertMessageProps } from './AlertMessage' 3 | -------------------------------------------------------------------------------- /frontend/src/lib/components/Annotations/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AnnotationMarker' 2 | export * from './Annotations' 3 | export * from './annotationsLogic' 4 | -------------------------------------------------------------------------------- /frontend/src/lib/components/LemonDivider/index.ts: -------------------------------------------------------------------------------- 1 | export { LemonDivider } from './LemonDivider' 2 | export type { LemonDividerProps } from './LemonDivider' 3 | -------------------------------------------------------------------------------- /plugin-server/tests/assets/GeoLite2-City-Test.mmdb.br: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/plugin-server/tests/assets/GeoLite2-City-Test.mmdb.br -------------------------------------------------------------------------------- /frontend/src/scenes/insights/views/WorldMap/index.ts: -------------------------------------------------------------------------------- 1 | export { WorldMap } from './WorldMap' 2 | export { countryCodeToFlag, countryCodeToName } from './countryCodes' 3 | -------------------------------------------------------------------------------- /bin/docker-dev: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | python manage.py migrate 5 | 6 | python manage.py runserver 0.0.0.0:8000 & ./bin/start-worker & ./bin/docker-frontend 7 | -------------------------------------------------------------------------------- /bin/docker-worker-beat: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | rm celerybeat.pid || echo "celerybeat.pid not found, proceeding" 5 | celery -A posthog beat -S redbeat.RedBeatScheduler -------------------------------------------------------------------------------- /posthog/demo/matrix/__init__.py: -------------------------------------------------------------------------------- 1 | # There is no spoon. 2 | from .manager import MatrixManager 3 | from .matrix import Matrix 4 | 5 | __all__ = ["Matrix", "MatrixManager"] 6 | -------------------------------------------------------------------------------- /frontend/src/lib/components/ActivityLog/SentenceList.scss: -------------------------------------------------------------------------------- 1 | .sentence-list { 2 | display: inline; 3 | 4 | .sentence-part { 5 | display: inline; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /frontend/@posthog/apps-common/README.md: -------------------------------------------------------------------------------- 1 | # PostHog App Common Packages 2 | 3 | This is an experimental package that contains tools frontend apps can use. Please don't use yet. 4 | -------------------------------------------------------------------------------- /frontend/src/scenes/events/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EventDetails' 2 | export * from './EventElements' 3 | export * from './EventsTable' 4 | export * from './eventsTableLogic' 5 | -------------------------------------------------------------------------------- /frontend/src/scenes/toolbar-launch/AuthorizedUrls.scss: -------------------------------------------------------------------------------- 1 | .AuthorizedUrlRow { 2 | &.highlight-on-hover:hover { 3 | background: var(--primary-bg-hover); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /bin/migrate-check: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | python manage.py migrate --check 5 | python manage.py migrate_clickhouse --check 6 | python manage.py run_async_migrations --check 7 | -------------------------------------------------------------------------------- /bin/start-frontend: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # pass first argument to WEBPACK_HOT_RELOAD_HOST 5 | [ $# -ge 1 ] && export WEBPACK_HOT_RELOAD_HOST=$1 6 | 7 | yarn install 8 | yarn start -------------------------------------------------------------------------------- /frontend/src/lib/experimental/README.md: -------------------------------------------------------------------------------- 1 | This folder contains components that are being actively tested. As such, any code/components here is subject to change or be removed at any time. 2 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/paths/paths_event_query.py: -------------------------------------------------------------------------------- 1 | from posthog.queries.paths.paths_event_query import PathEventQuery 2 | 3 | 4 | class ClickhousePathEventQuery(PathEventQuery): 5 | pass 6 | -------------------------------------------------------------------------------- /frontend/src/scenes/plugins/source/PluginSource.scss: -------------------------------------------------------------------------------- 1 | .PluginSource { 2 | .ant-form-item-explain-error { 3 | white-space: pre-wrap; 4 | font-family: monospace; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /posthog/settings/ee.py: -------------------------------------------------------------------------------- 1 | EE_AVAILABLE = False 2 | 3 | try: 4 | from ee.apps import EnterpriseConfig # noqa: F401 5 | except ImportError: 6 | pass 7 | else: 8 | EE_AVAILABLE = True 9 | -------------------------------------------------------------------------------- /bin/migrate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | python manage.py migrate 5 | python manage.py migrate_clickhouse 6 | python manage.py run_async_migrations --check 7 | python manage.py sync_replicated_schema -------------------------------------------------------------------------------- /frontend/src/styles/vars.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Values duplicated from global.scss 3 | 4 | If you edit this file, check there too 5 | */ 6 | export const styles = { zDrawer: 950, zGraphAnnotationPrompt: 99 } 7 | -------------------------------------------------------------------------------- /bin/start-frontend-https: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # pass first argument to WEBPACK_HOT_RELOAD_HOST 5 | [ $# -ge 1 ] && export WEBPACK_HOT_RELOAD_HOST=$1 6 | 7 | yarn install 8 | yarn start-https 9 | -------------------------------------------------------------------------------- /frontend/@posthog/apps-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": true, 5 | "noEmit": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /frontend/@posthog/lemon-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": true, 5 | "noEmit": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /plugin-server/src/worker/piscina.d.ts: -------------------------------------------------------------------------------- 1 | import Piscina from '@posthog/piscina' 2 | 3 | import { PluginsServerConfig } from '../types' 4 | export const makePiscina: (config: PluginsServerConfig) => Piscina 5 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /frontend/@posthog/apps-common/src/index.ts: -------------------------------------------------------------------------------- 1 | import '~/styles' 2 | import '~/initKea' 3 | 4 | export * from 'lib/components/AdHocInsight/AdHocInsight' 5 | 6 | import api_ from 'lib/api' 7 | export const api = api_ 8 | -------------------------------------------------------------------------------- /frontend/src/lib/components/PropertyKeyInfo.scss: -------------------------------------------------------------------------------- 1 | .property-key-info-tooltip, 2 | /* must join .ant-popover to override default width */ 3 | .ant-popover.property-key-info-tooltip { 4 | max-width: 300px; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/styles/index.tsx: -------------------------------------------------------------------------------- 1 | import './global.scss' /* Contains PostHog's main styling configurations */ 2 | import './antd.less' /* Imports Ant Design's components */ 3 | import './style.scss' /* DEPRECATED */ 4 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 120 3 | 4 | [tool.isort] 5 | multi_line_output = 3 6 | include_trailing_comma = true 7 | force_grid_wrap = 8 8 | ensure_newline_before_comments = true 9 | line_length = 120 -------------------------------------------------------------------------------- /posthog/__init__.py: -------------------------------------------------------------------------------- 1 | # This will make sure the app is always imported when 2 | # Django starts so that shared_task will use this app. 3 | from posthog.celery import app as celery_app 4 | 5 | __all__ = ("celery_app",) 6 | -------------------------------------------------------------------------------- /posthog/clickhouse/materialized_columns/__init__.py: -------------------------------------------------------------------------------- 1 | from posthog.settings import EE_AVAILABLE 2 | 3 | if EE_AVAILABLE: 4 | from ee.clickhouse.materialized_columns.columns import * 5 | else: 6 | from .column import * 7 | -------------------------------------------------------------------------------- /ee/bin/docker-ch-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ./bin/migrate 5 | 6 | mkdir -p frontend/dist 7 | touch frontend/dist/index.html 8 | touch frontend/dist/layout.html 9 | touch frontend/dist/exporter.html 10 | pytest ee 11 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/0011_cohortpeople_no_shard.py: -------------------------------------------------------------------------------- 1 | # This migration has been removed - it was only ever relevant on posthog-cloud and caused issues with 2 | # replicated schema migration. 3 | operations = [] # type: ignore 4 | -------------------------------------------------------------------------------- /posthog/queries/retention/types.py: -------------------------------------------------------------------------------- 1 | from typing import NamedTuple, Tuple, Union 2 | 3 | BreakdownValues = Tuple[Union[str, int], ...] 4 | CohortKey = NamedTuple("CohortKey", (("breakdown_values", BreakdownValues), ("period", int))) 5 | -------------------------------------------------------------------------------- /ee/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | env = 3 | DEBUG=1 4 | TEST=1 5 | DJANGO_SETTINGS_MODULE = posthog.settings 6 | addopts = -p no:warnings --reuse-db 7 | 8 | markers = 9 | ee 10 | clickhouse_only 11 | skip_on_multitenancy 12 | -------------------------------------------------------------------------------- /frontend/src/lib/components/UserActivityIndicator/UserActivityIndicator.scss: -------------------------------------------------------------------------------- 1 | .UserActivityIndicator { 2 | display: flex; 3 | align-items: center; 4 | font-size: 0.8125rem; 5 | white-space: nowrap; 6 | overflow-x: auto; 7 | } 8 | -------------------------------------------------------------------------------- /frontend/src/scenes/Unsubscribe/Unsubscribe.scss: -------------------------------------------------------------------------------- 1 | .Unsubscribe { 2 | min-height: 80vh; 3 | align-items: center; 4 | justify-content: center; 5 | display: flex; 6 | flex-direction: column; 7 | padding: 40px 16px; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/scenes/insights/__image_snapshots__/Insights trends can render bar graphs #0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/src/scenes/insights/__image_snapshots__/Insights trends can render bar graphs #0.png -------------------------------------------------------------------------------- /frontend/src/scenes/insights/__image_snapshots__/Insights trends can render bar graphs #1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kefranabg/posthog/master/frontend/src/scenes/insights/__image_snapshots__/Insights trends can render bar graphs #1.png -------------------------------------------------------------------------------- /posthog/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from .access import can_configure_plugins, can_install_plugins 3 | from .reload import reload_plugins_on_workers 4 | from .utils import download_plugin_archive, get_file_from_archive, parse_url 5 | -------------------------------------------------------------------------------- /plugin-server/src/utils/node-schedule.ts: -------------------------------------------------------------------------------- 1 | import * as schedule from 'node-schedule' 2 | 3 | export function cancelAllScheduledJobs(): void { 4 | Object.values(schedule.scheduledJobs).forEach((job) => { 5 | job.cancel() 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/0010_cohortpeople.py: -------------------------------------------------------------------------------- 1 | from infi.clickhouse_orm import migrations 2 | 3 | from posthog.models.cohort.sql import CREATE_COHORTPEOPLE_TABLE_SQL 4 | 5 | operations = [migrations.RunSQL(CREATE_COHORTPEOPLE_TABLE_SQL())] 6 | -------------------------------------------------------------------------------- /posthog/models/filters/__init__.py: -------------------------------------------------------------------------------- 1 | from .filter import Filter 2 | from .path_filter import PathFilter 3 | from .retention_filter import RetentionFilter 4 | 5 | __all__ = [ 6 | "Filter", 7 | "PathFilter", 8 | "RetentionFilter", 9 | ] 10 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | release: REDIS_URL='redis://' python manage.py migrate 2 | web: gunicorn posthog.wsgi --log-file - 3 | worker: ./bin/docker-worker 4 | celeryworker: ./bin/docker-worker-celery --with-scheduler # optional 5 | pluginworker: ./bin/plugin-server # optional 6 | -------------------------------------------------------------------------------- /posthog/templates/email/async_migration_status.html: -------------------------------------------------------------------------------- 1 | {% extends "email/base.html" %} {% load posthog_assets %} {% load posthog_filters %} {% block section %} 2 | 3 |

4 | {{ migration_status_update }} 5 |

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | env = 3 | DEBUG=1 4 | TEST=1 5 | DJANGO_SETTINGS_MODULE = posthog.settings 6 | addopts = -p no:warnings --reuse-db 7 | 8 | markers = 9 | ee 10 | clickhouse_only 11 | skip_on_multitenancy 12 | async_migrations -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | venv 2 | env 3 | .venv 4 | __pycache__/ 5 | staticfiles 6 | .env 7 | *.code-workspace 8 | frontend/.cache/ 9 | .mypy_cache 10 | frontend/dist/ 11 | *Type.ts 12 | .idea 13 | .yalc 14 | .python-version 15 | storybook-static 16 | dist/ 17 | node_modules/ -------------------------------------------------------------------------------- /ee/clickhouse/queries/paths/paths_actors.py: -------------------------------------------------------------------------------- 1 | from ee.clickhouse.queries.paths.paths import ClickhousePaths 2 | from posthog.queries.paths.paths_actors import PathsActors 3 | 4 | 5 | class ClickhousePathsActors(PathsActors, ClickhousePaths): # type: ignore 6 | pass 7 | -------------------------------------------------------------------------------- /plugin-server/src/utils/posthog.ts: -------------------------------------------------------------------------------- 1 | import PostHog from 'posthog-node' 2 | 3 | export const posthog = new PostHog('sTMFPsFhdP1Ssg', { 4 | host: 'https://app.posthog.com', 5 | }) 6 | 7 | if (process.env.NODE_ENV === 'test') { 8 | posthog.disable() 9 | } 10 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | const plugins = [require('autoprefixer')] // postCSS modules here 4 | if (process.env.NODE_ENV === 'production') { 5 | plugins.push(require('cssnano')) 6 | } 7 | 8 | module.exports = { 9 | plugins, 10 | } 11 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/0007_static_cohorts_table.py: -------------------------------------------------------------------------------- 1 | from infi.clickhouse_orm import migrations 2 | 3 | from posthog.models.person.sql import PERSON_STATIC_COHORT_TABLE_SQL 4 | 5 | operations = [ 6 | migrations.RunSQL(PERSON_STATIC_COHORT_TABLE_SQL()), 7 | ] 8 | -------------------------------------------------------------------------------- /frontend/src/lib/components/ProfilePicture/index.ts: -------------------------------------------------------------------------------- 1 | export { ProfilePicture } from './ProfilePicture' 2 | export type { ProfilePictureProps } from './ProfilePicture' 3 | export { ProfileBubbles } from './ProfileBubbles' 4 | export type { ProfileBubblesProps } from './ProfileBubbles' 5 | -------------------------------------------------------------------------------- /frontend/src/scenes/authentication/PasswordInput.scss: -------------------------------------------------------------------------------- 1 | .password-input { 2 | .password-input-strength-indicator { 3 | margin: 0; 4 | flex-grow: 1; 5 | .ant-form-item-control-input { 6 | min-height: unset; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/scenes/insights/views/LineGraph/LineGraph.scss: -------------------------------------------------------------------------------- 1 | .graph-container { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | 6 | .annotations-root { 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /plugin-server/src/worker/vm/transforms/common.ts: -------------------------------------------------------------------------------- 1 | import { PluginObj } from '@babel/core' 2 | import * as types from '@babel/types' 3 | 4 | import { Hub } from '../../../types' 5 | 6 | export type PluginGen = (server: Hub, ...args: any[]) => (param: { types: typeof types }) => PluginObj 7 | -------------------------------------------------------------------------------- /bin/start-https: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT 5 | 6 | export IS_BEHIND_PROXY=1 7 | export DISABLE_SECURE_SSL_REDIRECT=1 8 | ./bin/start-worker & 9 | ./bin/start-backend & 10 | ./bin/start-frontend-https & 11 | 12 | wait 13 | -------------------------------------------------------------------------------- /posthog/queries/event_query/__init__.py: -------------------------------------------------------------------------------- 1 | from posthog.settings import EE_AVAILABLE 2 | 3 | if EE_AVAILABLE: 4 | from ee.clickhouse.queries.event_query import EnterpriseEventQuery as EventQuery 5 | else: 6 | from posthog.queries.event_query.event_query import EventQuery # type: ignore 7 | -------------------------------------------------------------------------------- /frontend/src/scenes/persons/PersonHeader.scss: -------------------------------------------------------------------------------- 1 | .person-header { 2 | .profile-picture { 3 | transition: opacity 200ms ease; 4 | margin-right: 0.5rem; 5 | } 6 | a:hover { 7 | .profile-picture { 8 | opacity: 0.75; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /plugin-server/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "loose": true, 7 | "targets": { "node": 16 } 8 | } 9 | ], 10 | "@babel/preset-typescript" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /posthog/models/filters/session_recordings_filter.py: -------------------------------------------------------------------------------- 1 | from posthog.models import Filter 2 | from posthog.models.filters.mixins.session_recordings import PersonUUIDMixin, SessionRecordingsMixin 3 | 4 | 5 | class SessionRecordingsFilter(SessionRecordingsMixin, PersonUUIDMixin, Filter): 6 | pass 7 | -------------------------------------------------------------------------------- /frontend/src/lib/components/DatePicker.tsx: -------------------------------------------------------------------------------- 1 | import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs' 2 | import generatePicker from 'antd/lib/date-picker/generatePicker' 3 | 4 | import { dayjs } from 'lib/dayjs' 5 | 6 | export const DatePicker = generatePicker(dayjsGenerateConfig) 7 | -------------------------------------------------------------------------------- /frontend/src/lib/components/LemonTable/index.ts: -------------------------------------------------------------------------------- 1 | export { LemonTable } from './LemonTable' 2 | export type { LemonTableProps } from './LemonTable' 3 | export type { Sorting } from './sorting' 4 | export type { ExpandableConfig, LemonTableColumn, LemonTableColumnGroup, LemonTableColumns } from './types' 5 | -------------------------------------------------------------------------------- /posthog/queries/groups_join_query/__init__.py: -------------------------------------------------------------------------------- 1 | from posthog.settings import EE_AVAILABLE 2 | 3 | if EE_AVAILABLE: 4 | from ee.clickhouse.queries.groups_join_query import GroupsJoinQuery 5 | else: 6 | from posthog.queries.groups_join_query.groups_join_query import GroupsJoinQuery # type: ignore 7 | -------------------------------------------------------------------------------- /posthog/settings/statsd.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # Metrics - StatsD 4 | STATSD_HOST = os.getenv("STATSD_HOST") 5 | STATSD_PORT = os.getenv("STATSD_PORT", 8125) 6 | STATSD_PREFIX = os.getenv("STATSD_PREFIX", "") 7 | STATSD_TELEGRAF = True 8 | STATSD_CLIENT = "statshog" 9 | STATSD_SEPARATOR = "_" 10 | -------------------------------------------------------------------------------- /posthog/test/mock_urls_cloud.py: -------------------------------------------------------------------------------- 1 | from posthog.urls import opt_slash_path 2 | from posthog.views import robots_txt 3 | 4 | settings = {"MULTI_TENANCY": True} 5 | 6 | urlpatterns = [] 7 | 8 | if not settings["MULTI_TENANCY"]: 9 | urlpatterns.append(opt_slash_path("robots.txt", robots_txt)) 10 | -------------------------------------------------------------------------------- /frontend/src/lib/components/LemonButton/index.ts: -------------------------------------------------------------------------------- 1 | export { LemonButton, LemonButtonWithSideAction, LemonButtonWithPopup } from './LemonButton' 2 | export type { 3 | LemonButtonProps, 4 | LemonButtonWithSideActionProps, 5 | LemonButtonWithPopupProps, 6 | SideAction, 7 | } from './LemonButton' 8 | -------------------------------------------------------------------------------- /posthog/models/event_buffer.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class EventBuffer(models.Model): 5 | event: models.JSONField = models.JSONField(null=True, blank=True) 6 | process_at: models.DateTimeField = models.DateTimeField() 7 | locked: models.BooleanField = models.BooleanField() 8 | -------------------------------------------------------------------------------- /posthog/queries/cohort_query.py: -------------------------------------------------------------------------------- 1 | from posthog.settings import EE_AVAILABLE 2 | 3 | if EE_AVAILABLE: 4 | from ee.clickhouse.queries.enterprise_cohort_query import EnterpriseCohortQuery as CohortQuery 5 | else: 6 | from posthog.queries.foss_cohort_query import FOSSCohortQuery as CohortQuery # type: ignore 7 | -------------------------------------------------------------------------------- /posthog/test/mock_urls_self_hosted.py: -------------------------------------------------------------------------------- 1 | from posthog.urls import opt_slash_path 2 | from posthog.views import robots_txt 3 | 4 | settings = {"MULTI_TENANCY": False} 5 | 6 | urlpatterns = [] 7 | 8 | if not settings["MULTI_TENANCY"]: 9 | urlpatterns.append(opt_slash_path("robots.txt", robots_txt)) 10 | -------------------------------------------------------------------------------- /frontend/src/lib/components/HelpButton/HelpButton.scss: -------------------------------------------------------------------------------- 1 | .help-button { 2 | align-items: center; 3 | height: 2.5rem; 4 | cursor: pointer; 5 | display: flex; 6 | font-size: 1.5rem; 7 | color: var(--primary-alt); 8 | 9 | &.inline { 10 | display: inline-flex; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/lib/components/PaginationControl/index.tsx: -------------------------------------------------------------------------------- 1 | export { PaginationControl } from './PaginationControl' 2 | export { usePagination } from './usePagination' 3 | export type { PaginationControlProps } from './PaginationControl' 4 | export type { PaginationAuto, PaginationManual, PaginationState } from './types' 5 | -------------------------------------------------------------------------------- /frontend/src/lib/components/DurationPicker/DurationPicker.scss: -------------------------------------------------------------------------------- 1 | .DurationPicker { 2 | display: flex; 3 | flex-direction: row; 4 | .DurationPicker__time-input { 5 | flex: 1; 6 | } 7 | .DurationPicker__unit-picker { 8 | flex: 1; 9 | margin-left: 0.5rem; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /plugin-server/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | max_line_length = 120 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /plugin-server/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "rootDir": "./", 6 | "noEmit": true 7 | }, 8 | "include": [".", "src", "tests", ".eslintrc.js"], 9 | "exclude": ["node_modules", "dist", "bin"] 10 | } 11 | -------------------------------------------------------------------------------- /bin/docker-worker: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ./bin/migrate-check 5 | 6 | # Stop any background jobs on exit 7 | trap 'kill $(jobs -p)' EXIT 8 | 9 | ./bin/plugin-server & 10 | ./bin/docker-worker-celery --with-scheduler & 11 | 12 | # Exit if any processes exit, and exit with it's exit code 13 | wait -n 14 | exit $? 15 | -------------------------------------------------------------------------------- /frontend/src/mocks/features.ts: -------------------------------------------------------------------------------- 1 | import { AvailableFeature } from '~/types' 2 | 3 | let features: AvailableFeature[] = [] 4 | export const useAvailableFeatures = (f: AvailableFeature[]): void => { 5 | features = f 6 | } 7 | export const getAvailableFeatures = (): AvailableFeature[] => { 8 | return features 9 | } 10 | -------------------------------------------------------------------------------- /share/share.md: -------------------------------------------------------------------------------- 1 | # Share folder 2 | 3 | Put here any resources that should be shared across all projects (events, web, worker, plugins, etc.). Most likely this will be things like small static databases or other resources. 4 | 5 | Examples: 6 | - GeoLite2-City.mmdb 7 | - Some small lookup Sqlite db 8 | - random data? 9 | 10 | -------------------------------------------------------------------------------- /frontend/src/lib/components/AddToDashboard/AddToDashboard.scss: -------------------------------------------------------------------------------- 1 | .add-to-dashboard-modal { 2 | .list-wrapper { 3 | min-height: 420px; 4 | } 5 | 6 | .existing-links-info { 7 | color: var(--muted-alt); 8 | 9 | strong { 10 | color: var(--default); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/lib/components/PropertyFilters/components/FilterRow.scss: -------------------------------------------------------------------------------- 1 | .filter-row-popup { 2 | // so the datepicker is visible 3 | overflow: visible; 4 | } 5 | 6 | .property-filter-row { 7 | overflow: hidden; 8 | flex-basis: 100%; 9 | 10 | &.wrap-filters { 11 | flex-basis: auto; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/0025_json_events.py: -------------------------------------------------------------------------------- 1 | from infi.clickhouse_orm import migrations 2 | 3 | from posthog.models.event.sql import EVENTS_TABLE_JSON_MV_SQL, KAFKA_EVENTS_TABLE_JSON_SQL 4 | 5 | operations = [ 6 | migrations.RunSQL(KAFKA_EVENTS_TABLE_JSON_SQL()), 7 | migrations.RunSQL(EVENTS_TABLE_JSON_MV_SQL()), 8 | ] 9 | -------------------------------------------------------------------------------- /frontend/src/lib/components/Fade/Fade.scss: -------------------------------------------------------------------------------- 1 | @keyframes fadeComponentFadeIn { 2 | 0% { 3 | opacity: 0; 4 | } 5 | 100% { 6 | opacity: 1; 7 | } 8 | } 9 | 10 | @keyframes fadeComponentFadeOut { 11 | 0% { 12 | opacity: 1; 13 | } 14 | 100% { 15 | opacity: 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/scenes/cohorts/Cohort.scss: -------------------------------------------------------------------------------- 1 | .Cohort__Name { 2 | &.ant-form-item-has-error { 3 | .ant-form-item-explain-error { 4 | font-weight: 500; 5 | margin: 0.25rem 0; 6 | } 7 | 8 | .LemonInput { 9 | border: 1px solid var(--danger); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ee/api/test/__snapshots__/test_instance_settings.ambr: -------------------------------------------------------------------------------- 1 | # name: TestInstanceSettings.test_update_recordings_ttl_setting 2 | ' 3 | /* request:api_instance_settings_(?P[^_.]+)_?$ (InstanceSettingsViewset) */ 4 | ALTER TABLE sharded_session_recording_events ON CLUSTER 'posthog' MODIFY TTL toDate(created_at) + toIntervalWeek(5) 5 | ' 6 | --- 7 | -------------------------------------------------------------------------------- /frontend/src/scenes/plugins/plugin/SourcePluginTag.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Tag } from 'antd' 3 | 4 | export function SourcePluginTag({ 5 | title = 'Source Code', 6 | style, 7 | }: { 8 | title?: string 9 | style?: React.CSSProperties 10 | }): JSX.Element { 11 | return {title} 12 | } 13 | -------------------------------------------------------------------------------- /posthog/migrations/0175_should_update_person_props_function.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.12 on 2021-10-05 13:06 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0174_organization_slug"), 10 | ] 11 | 12 | operations = [] # type: ignore 13 | -------------------------------------------------------------------------------- /posthog/models/filters/mixins/base.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Literal 2 | 3 | BreakdownType = Literal["event", "person", "cohort", "group", "session"] 4 | IntervalType = Literal["hour", "day", "week", "month"] 5 | FunnelWindowIntervalType = Literal["minute", "hour", "day", "week", "month"] 6 | 7 | 8 | class BaseParamMixin: 9 | _data: Dict 10 | -------------------------------------------------------------------------------- /frontend/src/lib/components/EmptyMessage/EmptyMessage.scss: -------------------------------------------------------------------------------- 1 | .empty-message { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | justify-content: center; 6 | align-items: center; 7 | 8 | .title { 9 | text-align: center; 10 | } 11 | .description { 12 | text-align: center; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/lib/components/Property.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export function Property({ value }: { value: any }): JSX.Element { 4 | return ( 5 | 6 | {typeof value === 'object' ? JSON.stringify(value) : value && value.toString().replace(/(^\w+:|^)\/\//, '')} 7 | 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/scenes/plugins/plugin/styles/metrics-drawer.scss: -------------------------------------------------------------------------------- 1 | .metrics-drawer { 2 | z-index: var(--z-drawer); 3 | .metrics-chart-wrapper { 4 | canvas { 5 | max-width: 90%; 6 | max-height: 80%; 7 | } 8 | canvas:hover { 9 | cursor: default !important; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/0031_event_properties_zstd.py: -------------------------------------------------------------------------------- 1 | from infi.clickhouse_orm import migrations 2 | 3 | from posthog.settings import CLICKHOUSE_CLUSTER 4 | 5 | operations = [ 6 | migrations.RunSQL( 7 | f"ALTER TABLE sharded_events ON CLUSTER '{CLICKHOUSE_CLUSTER}' MODIFY COLUMN properties VARCHAR CODEC(ZSTD(3))" 8 | ), 9 | ] 10 | -------------------------------------------------------------------------------- /posthog/tasks/user_identify.py: -------------------------------------------------------------------------------- 1 | import posthoganalytics 2 | 3 | from posthog.celery import app 4 | from posthog.models import User 5 | 6 | 7 | @app.task(ignore_result=True) 8 | def identify_task(user_id: int) -> None: 9 | 10 | user = User.objects.get(id=user_id) 11 | posthoganalytics.identify(user.distinct_id, user.get_analytics_metadata()) 12 | -------------------------------------------------------------------------------- /posthog/plugins/reload.py: -------------------------------------------------------------------------------- 1 | import structlog 2 | from django.conf import settings 3 | 4 | from posthog.redis import get_client 5 | 6 | logger = structlog.get_logger(__name__) 7 | 8 | 9 | def reload_plugins_on_workers(): 10 | logger.info("Reloading plugins on workers") 11 | get_client().publish(settings.PLUGINS_RELOAD_PUBSUB_CHANNEL, "reload!") 12 | -------------------------------------------------------------------------------- /posthog/settings/ingestion.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from posthog.settings.utils import get_from_env, get_list 4 | 5 | INGESTION_LAG_METRIC_TEAM_IDS = get_list(os.getenv("INGESTION_LAG_METRIC_TEAM_IDS", "")) 6 | 7 | # KEEP IN SYNC WITH plugin-server/src/config/config.ts 8 | BUFFER_CONVERSION_SECONDS = get_from_env("BUFFER_CONVERSION_SECONDS", default=60, type_cast=int) 9 | -------------------------------------------------------------------------------- /posthog/templates/email/async_migration_error.html: -------------------------------------------------------------------------------- 1 | {% extends "email/base.html" %} {% load posthog_assets %} {% load posthog_filters %} {% block section %} 2 | 3 |

4 | Async migration {{ migration_key }} errored at {{ time }} with error: 5 | 6 | 7 | {{ error }} 8 | 9 |

10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /bin/check_kafka_clickhouse_up: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Check Kafka 6 | while true; do 7 | nc -z localhost 9092 && break || echo 'Checking Kafka status...' && sleep 1 8 | done 9 | 10 | # Check ClickHouse 11 | while true; do 12 | curl -s -o /dev/null -I 'http://localhost:8123/' && break || echo 'Checking ClickHouse status...' && sleep 1 13 | done 14 | -------------------------------------------------------------------------------- /frontend/src/layout/navigation/DemoWarnings/DemoWarnings.scss: -------------------------------------------------------------------------------- 1 | .demo-warning { 2 | display: flex; 3 | align-items: center; 4 | 5 | .ant-alert-message { 6 | font-weight: bold; 7 | } 8 | 9 | .ant-alert-close-icon .anticon-close svg { 10 | height: 14px; 11 | width: 14px; 12 | margin-left: 1rem; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/scenes/persons/Persons.scss: -------------------------------------------------------------------------------- 1 | .extra-ids { 2 | display: inline-flex; 3 | align-items: center; 4 | height: 1.25rem; 5 | line-height: 1.125rem; 6 | margin: 0 0 0 0.25rem; 7 | padding: 0 0.25rem 0 0.375rem; 8 | color: var(--primary); 9 | cursor: pointer; 10 | svg { 11 | margin-left: 0.25rem; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /plugin-server/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.(t|j)s$': ['@swc/jest'], 4 | }, 5 | testEnvironment: 'node', 6 | clearMocks: true, 7 | coverageProvider: 'v8', 8 | setupFilesAfterEnv: ['./jest.setup.fetch-mock.js'], 9 | testMatch: ['/tests/**/*.test.ts'], 10 | testTimeout: 60000, 11 | } 12 | -------------------------------------------------------------------------------- /cypress/e2e/commandPalette.js: -------------------------------------------------------------------------------- 1 | describe('Command Palette', () => { 2 | it('Shows on Ctrl + K press', () => { 3 | cy.get('body').type('{ctrl}k') 4 | cy.get('[data-attr=command-palette-input]').should('exist') 5 | 6 | cy.get('body').type('{cmd}k') 7 | cy.get('[data-attr=command-palette-input]').should('not.exist') 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /posthog/migrations/0244_drop_should_update_person_prop.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ("posthog", "0243_unpack_plugin_source_files"), 8 | ] 9 | 10 | operations = [ 11 | migrations.RunSQL("DROP FUNCTION IF EXISTS should_update_person_prop"), 12 | ] 13 | -------------------------------------------------------------------------------- /.storybook/storybook.scss: -------------------------------------------------------------------------------- 1 | // Counteract toolbar styles 2 | 3 | .sb-show-main { 4 | .ant-modal-mask, 5 | .ant-modal-wrap { 6 | z-index: 1050 !important; 7 | } 8 | .ant-select-dropdown, 9 | .ant-picker-dropdown { 10 | z-index: 1060 !important; 11 | } 12 | .ant-tooltip { 13 | z-index: 1060 !important; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/lib/components/Drawer.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import { Drawer as AntDrawer } from 'antd' 3 | import { DrawerProps } from 'antd/lib/drawer' 4 | import { styles } from '~/styles/vars' 5 | 6 | export function Drawer(props: PropsWithChildren): JSX.Element { 7 | return 8 | } 9 | -------------------------------------------------------------------------------- /plugin-server/src/init.ts: -------------------------------------------------------------------------------- 1 | import { initSentry } from './sentry' 2 | import { PluginsServerConfig } from './types' 3 | import { setLogLevel } from './utils/utils' 4 | 5 | // Code that runs on app start, in both the main and worker threads 6 | export function initApp(config: PluginsServerConfig): void { 7 | setLogLevel(config.LOG_LEVEL) 8 | initSentry(config) 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/scenes/instance/SystemStatus/index.scss: -------------------------------------------------------------------------------- 1 | .system-status-scene { 2 | margin-bottom: 64px; 3 | .metric-column { 4 | @media (min-width: 750px) { 5 | width: 33%; 6 | } 7 | } 8 | } 9 | 10 | .embedded-svg-wrapper { 11 | cursor: text; 12 | 13 | svg { 14 | width: 100%; 15 | height: 100%; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /posthog/migrations/0041_merge_20200407_1805.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-04-07 18:05 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0040_remove_event_ip"), 10 | ("posthog", "0039_user_email_opt_in"), 11 | ] 12 | 13 | operations = [] # type: ignore 14 | -------------------------------------------------------------------------------- /posthog/migrations/0049_delete_funnelstep.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-04-21 10:08 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0048_auto_20200420_1051"), 10 | ] 11 | 12 | operations = [ 13 | migrations.DeleteModel(name="FunnelStep",), 14 | ] 15 | -------------------------------------------------------------------------------- /posthog/migrations/0184_delete_sessionsfilter.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-11-29 14:38 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0183_groups_pg"), 10 | ] 11 | 12 | operations = [ 13 | migrations.DeleteModel(name="SessionsFilter",), 14 | ] 15 | -------------------------------------------------------------------------------- /posthog/types.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from posthog.models.filters.filter import Filter 4 | from posthog.models.filters.path_filter import PathFilter 5 | from posthog.models.filters.retention_filter import RetentionFilter 6 | from posthog.models.filters.stickiness_filter import StickinessFilter 7 | 8 | FilterType = Union[Filter, PathFilter, RetentionFilter, StickinessFilter] 9 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/0018_group_analytics_schema.py: -------------------------------------------------------------------------------- 1 | from infi.clickhouse_orm import migrations 2 | 3 | from posthog.models.group.sql import GROUPS_TABLE_MV_SQL, GROUPS_TABLE_SQL, KAFKA_GROUPS_TABLE_SQL 4 | 5 | operations = [ 6 | migrations.RunSQL(GROUPS_TABLE_SQL()), 7 | migrations.RunSQL(KAFKA_GROUPS_TABLE_SQL()), 8 | migrations.RunSQL(GROUPS_TABLE_MV_SQL), 9 | ] 10 | -------------------------------------------------------------------------------- /posthog/migrations/0129_merge_20210223_0757.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2021-02-23 07:57 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0128_stricter_team_schema"), 10 | ("posthog", "0127_add_dashboard_filters"), 11 | ] 12 | 13 | operations = [] # type: ignore 14 | -------------------------------------------------------------------------------- /posthog/migrations/0148_merge_20210506_0823.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.8 on 2021-05-06 08:23 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0147_plugin_logs"), 10 | ("posthog", "0147_fix_stickiness_dashboard_items"), 11 | ] 12 | 13 | operations = [] # type: ignore 14 | -------------------------------------------------------------------------------- /frontend/src/layout/Error404.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PageHeader } from 'lib/components/PageHeader' 3 | 4 | export function Error404(): JSX.Element { 5 | return ( 6 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/lib/components/LinkButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from 'antd' 3 | import { Link, LinkProps } from 'lib/components/Link' 4 | 5 | export function LinkButton(props: LinkProps & { icon?: React.ReactNode }): JSX.Element { 6 | const { icon, ...linkProps } = props 7 | return } /> 8 | } 9 | -------------------------------------------------------------------------------- /posthog/migrations/0239_delete_postgres_pluginlogentry.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-05-24 12:34 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0238_exportedasset"), 10 | ] 11 | 12 | operations = [ 13 | migrations.DeleteModel(name="PluginLogEntry",), 14 | ] 15 | -------------------------------------------------------------------------------- /frontend/src/lib/hooks/usePeriodicRerender.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | export function usePeriodicRerender(milliseconds: number): void { 4 | const [, setTick] = useState(0) 5 | 6 | useEffect(() => { 7 | const intervalId = setInterval(() => setTick((state) => state + 1), milliseconds) 8 | return () => clearInterval(intervalId) 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /latest_migrations.manifest: -------------------------------------------------------------------------------- 1 | admin: 0003_logentry_add_action_flag_choices 2 | auth: 0012_alter_user_first_name_max_length 3 | axes: 0006_remove_accesslog_trusted 4 | contenttypes: 0002_remove_content_type_name 5 | ee: 0013_silence_deprecated_tags_warnings 6 | posthog: 0254_prompt_sequence_state 7 | rest_hooks: 0002_swappable_hook_model 8 | sessions: 0001_initial 9 | social_django: 0010_uid_db_index 10 | -------------------------------------------------------------------------------- /posthog/migrations/0040_remove_event_ip.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-04-04 11:45 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0039_populate_event_ip_property"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField(model_name="event", name="ip",), 14 | ] 15 | -------------------------------------------------------------------------------- /posthog/migrations/0117_merge_20210126_0917.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2021-01-26 09:17 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0116_plugin_latest_tag"), 10 | ("posthog", "0116_session_recording_retention_period"), 11 | ] 12 | 13 | operations = [] # type: ignore 14 | -------------------------------------------------------------------------------- /ee/clickhouse/test/test_calculate_event_property_usage.py: -------------------------------------------------------------------------------- 1 | from posthog.tasks.test.test_calculate_event_property_usage import calculate_event_property_usage_test_factory 2 | from posthog.test.base import ClickhouseTestMixin, _create_event 3 | 4 | 5 | class CalculateEventPropertyUsage( 6 | ClickhouseTestMixin, calculate_event_property_usage_test_factory(_create_event), # type: ignore 7 | ): 8 | pass 9 | -------------------------------------------------------------------------------- /posthog/migrations/0176_update_person_props_function.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.12 on 2021-10-05 13:06 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0175_should_update_person_props_function"), 10 | ] 11 | 12 | # The type and function were never used 13 | operations = [] # type: ignore 14 | -------------------------------------------------------------------------------- /posthog/tasks/sync_all_organization_available_features.py: -------------------------------------------------------------------------------- 1 | from typing import Sequence, cast 2 | 3 | from posthog.models.organization import Organization 4 | 5 | 6 | def sync_all_organization_available_features() -> None: 7 | for organization in cast(Sequence[Organization], Organization.objects.all().only("id")): 8 | organization.update_available_features() 9 | organization.save() 10 | -------------------------------------------------------------------------------- /ee/tasks/org_usage_report.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from posthog.tasks.org_usage_report import OrgReport, send_all_reports 4 | 5 | 6 | def send_all_org_usage_reports(*, dry_run: bool = False) -> List[OrgReport]: 7 | """ 8 | Creates and sends usage reports for all teams. 9 | Returns a list of all the successfully sent reports. 10 | """ 11 | return send_all_reports(dry_run=dry_run) 12 | -------------------------------------------------------------------------------- /frontend/src/lib/components/CloseButton.tsx: -------------------------------------------------------------------------------- 1 | import { CloseOutlined } from '@ant-design/icons' 2 | import React from 'react' 3 | 4 | export function CloseButton(props: Record): JSX.Element { 5 | return ( 6 | 7 | 8 | 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/lib/components/PaginationControl/PaginationControl.scss: -------------------------------------------------------------------------------- 1 | .PaginationControl { 2 | align-self: flex-end; 3 | display: flex; 4 | align-items: center; 5 | justify-content: flex-end; 6 | border: 1px solid var(--border); 7 | border-radius: var(--radius); 8 | padding: 0.5rem 1rem; 9 | margin-top: 1rem; 10 | > span { 11 | margin-right: 0.5rem; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/lib/hooks/useSecondRender.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | export function useSecondRender(callback) { 4 | const [secondRender, setSecondRender] = useState(false) 5 | 6 | useEffect(() => { 7 | requestAnimationFrame(() => { 8 | setSecondRender(true) 9 | callback() 10 | }) 11 | }, []) 12 | 13 | return secondRender 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/stories/Missing components.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/addon-docs'; 2 | 3 | 4 | 5 | # Missing components 6 | 7 | A lot of components are missing. TODO for now: 8 | 9 | 1. Make an overview of what is there in the `lib/components` folder, and `lib/` in general. 10 | 2. Make a list of components that need stories 11 | 3. Create those stories 12 | -------------------------------------------------------------------------------- /posthog/migrations/0005_remove_person_distinct_ids.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-01-25 19:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0004_auto_20200125_0415"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField(model_name="person", name="distinct_ids",), 14 | ] 15 | -------------------------------------------------------------------------------- /posthog/migrations/0093_remove_user_is_superuser.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.7 on 2020-10-26 11:07 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0092_rename_projects_to_default"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField(model_name="user", name="is_superuser",), 14 | ] 15 | -------------------------------------------------------------------------------- /posthog/queries/column_optimizer/column_optimizer.py: -------------------------------------------------------------------------------- 1 | # isort: skip_file 2 | from posthog.settings import EE_AVAILABLE 3 | 4 | if EE_AVAILABLE: 5 | from ee.clickhouse.queries.column_optimizer import EnterpriseColumnOptimizer as ColumnOptimizer 6 | else: 7 | from posthog.queries.column_optimizer.foss_column_optimizer import ( # type: ignore 8 | FOSSColumnOptimizer as ColumnOptimizer, 9 | ) 10 | -------------------------------------------------------------------------------- /posthog/queries/paths/__init__.py: -------------------------------------------------------------------------------- 1 | from posthog.settings import EE_AVAILABLE 2 | 3 | if EE_AVAILABLE: 4 | from ee.clickhouse.queries.paths import ClickhousePaths as Paths 5 | from ee.clickhouse.queries.paths import ClickhousePathsActors as PathsActors 6 | else: 7 | from posthog.queries.paths.paths import Paths # type: ignore 8 | from posthog.queries.paths.paths_actors import PathsActors # type: ignore 9 | -------------------------------------------------------------------------------- /plugin-server/src/config/constants.ts: -------------------------------------------------------------------------------- 1 | import { logLevel } from 'kafkajs' 2 | 3 | export const ONE_MINUTE = 60 * 1000 4 | export const ONE_HOUR = 60 * 60 * 1000 5 | export const CELERY_DEFAULT_QUEUE = 'celery' 6 | export const KAFKAJS_LOG_LEVEL_MAPPING = { 7 | NOTHING: logLevel.NOTHING, 8 | DEBUG: logLevel.INFO, 9 | INFO: logLevel.INFO, 10 | WARN: logLevel.WARN, 11 | ERROR: logLevel.ERROR, 12 | } 13 | -------------------------------------------------------------------------------- /posthog/api/forbid_destroy_model.py: -------------------------------------------------------------------------------- 1 | from rest_framework import status 2 | from rest_framework.response import Response 3 | 4 | 5 | class ForbidDestroyModel: 6 | """ 7 | Override the default in ModelViewSet that allows callers to destroy a model instance. 8 | """ 9 | 10 | def destroy(self, request, *args, **kwargs) -> Response: 11 | return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED) 12 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | from infi.clickhouse_orm import migrations 2 | 3 | from posthog.models.event.sql import EVENTS_TABLE_SQL 4 | from posthog.settings import CLICKHOUSE_CLUSTER, CLICKHOUSE_DATABASE 5 | 6 | operations = [ 7 | migrations.RunSQL(f"CREATE DATABASE IF NOT EXISTS {CLICKHOUSE_DATABASE} ON CLUSTER '{CLICKHOUSE_CLUSTER}'"), 8 | migrations.RunSQL(EVENTS_TABLE_SQL()), 9 | ] 10 | -------------------------------------------------------------------------------- /posthog/migrations/0009_auto_20200127_0018.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-01-27 00:18 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0008_action_actionstep"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField(model_name="element", old_name="el_text", new_name="text",), 14 | ] 15 | -------------------------------------------------------------------------------- /posthog/migrations/0173_should_update_person_props_function.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.12 on 2021-10-05 13:06 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0172_person_properties_last_operation"), 10 | ] 11 | 12 | # Superseded by 0175_should_update_person_props 13 | operations = [] # type: ignore 14 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/experiments/__init__.py: -------------------------------------------------------------------------------- 1 | # The FF variant name for control 2 | CONTROL_VARIANT_KEY = "control" 3 | 4 | # controls minimum number of people to be exposed to a variant 5 | # before the results are deemed significant 6 | FF_DISTRIBUTION_THRESHOLD = 100 7 | 8 | # If probability of a variant is below this threshold, it will be considered 9 | # insignificant 10 | MIN_PROBABILITY_FOR_SIGNIFICANCE = 0.9 11 | -------------------------------------------------------------------------------- /posthog/test/db_context_capturing.py: -------------------------------------------------------------------------------- 1 | from contextlib import contextmanager 2 | 3 | from django.db import DEFAULT_DB_ALIAS, connections 4 | from django.test.utils import CaptureQueriesContext 5 | 6 | 7 | @contextmanager 8 | def capture_db_queries(): 9 | db_connection = connections[DEFAULT_DB_ALIAS] 10 | with CaptureQueriesContext(db_connection) as capture_query_context: 11 | yield capture_query_context 12 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | We always recommend using the latest version of PostHog to ensure you get all security updates. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | Please report security vulnerabilities to hey@posthog.com. 10 | 11 | We currently do not operate a bug bounty program, but we will generously reward you with merch for any actionable security vulnerabilities found. 12 | -------------------------------------------------------------------------------- /posthog/migrations/0036_remove_current_url_index.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-03-19 11:11 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0035_current_url_index_20200318_1459"), 10 | ] 11 | 12 | operations = [migrations.RunSQL("DROP INDEX IF EXISTS posthog_event_properties_current_url_gin;", "SELECT 1;",)] 13 | -------------------------------------------------------------------------------- /posthog/migrations/0191_rename_specialmigration_asyncmigration.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-12-13 19:41 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0190_experiment"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameModel(old_name="SpecialMigration", new_name="AsyncMigration",), 14 | ] 15 | -------------------------------------------------------------------------------- /posthog/tasks/delete_clickhouse_data.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from posthog.celery import app 4 | from posthog.models.team.util import delete_teams_clickhouse_data, is_clickhouse_data_cron_enabled 5 | 6 | 7 | @app.task(ignore_result=True, max_retries=1) 8 | def delete_clickhouse_data(team_ids: List[int]) -> None: 9 | if not is_clickhouse_data_cron_enabled(): 10 | delete_teams_clickhouse_data(team_ids) 11 | -------------------------------------------------------------------------------- /posthog/settings/feature_flags.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from posthog.settings.utils import get_list 4 | 5 | # These flags will be force-enabled on the frontend 6 | # The features here are released, but the flags are just not yet removed from the code 7 | PERSISTED_FEATURE_FLAGS = get_list(os.getenv("PERSISTED_FEATURE_FLAGS", "")) + [ 8 | "invite-teammates-prompt", 9 | "insight-legends", 10 | "simplify-actions", 11 | ] 12 | -------------------------------------------------------------------------------- /.run/Frontend.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | {% endif %} 14 | -------------------------------------------------------------------------------- /ee/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .dashboard_privilege import DashboardPrivilege 2 | from .event_definition import EnterpriseEventDefinition 3 | from .explicit_team_membership import ExplicitTeamMembership 4 | from .hook import Hook 5 | from .license import License 6 | from .property_definition import EnterprisePropertyDefinition 7 | 8 | __all__ = [ 9 | "EnterpriseEventDefinition", 10 | "ExplicitTeamMembership", 11 | "DashboardPrivilege", 12 | "Hook", 13 | "License", 14 | "EnterprisePropertyDefinition", 15 | ] 16 | -------------------------------------------------------------------------------- /posthog/api/test/mock_sentry.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import Mock 2 | 3 | 4 | def mock_sentry_context_for_tagging(patched_scope): 5 | mock_scope = Mock() 6 | mock_set_tag = Mock() 7 | mock_scope.set_context = Mock() 8 | mock_scope.set_tag = mock_set_tag 9 | mock_context_manager = Mock() 10 | mock_context_manager.__enter__ = Mock(return_value=mock_scope) 11 | mock_context_manager.__exit__ = Mock(return_value=None) 12 | patched_scope.return_value = mock_context_manager 13 | return mock_set_tag 14 | -------------------------------------------------------------------------------- /frontend/src/lib/components/NotFound/NotFound.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ComponentStory, ComponentMeta } from '@storybook/react' 3 | 4 | import { NotFound } from './index' 5 | 6 | export default { 7 | title: 'Components/Not Found', 8 | component: NotFound, 9 | } as ComponentMeta 10 | 11 | const Template: ComponentStory = (args) => 12 | 13 | export const NotFound_ = Template.bind({}) 14 | NotFound_.args = { 15 | object: 'Person', 16 | } 17 | -------------------------------------------------------------------------------- /posthog/api/unsubscribe.py: -------------------------------------------------------------------------------- 1 | import jwt 2 | from django.http import HttpRequest, JsonResponse 3 | 4 | from posthog.models.subscription import unsubscribe_using_token 5 | 6 | 7 | def unsubscribe(request: HttpRequest): 8 | token = request.GET.get("token") 9 | if not token: 10 | return JsonResponse({"success": False}) 11 | 12 | try: 13 | unsubscribe_using_token(token) 14 | except jwt.DecodeError: 15 | return JsonResponse({"success": False}) 16 | 17 | return JsonResponse({"success": True}) 18 | -------------------------------------------------------------------------------- /cypress/fixtures/api/decide.js: -------------------------------------------------------------------------------- 1 | export function decideResponse(featureFlags) { 2 | return { 3 | config: { 4 | enable_collect_everything: true, 5 | }, 6 | editorParams: { 7 | toolbarVersion: 'toolbar', 8 | jsURL: 'http://localhost:8234/', 9 | }, 10 | isAuthenticated: true, 11 | supportedCompression: ['gzip', 'gzip-js', 'lz64'], 12 | featureFlags, 13 | sessionRecording: { 14 | endpoint: '/s/', 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/exporter/types.ts: -------------------------------------------------------------------------------- 1 | import { DashboardType, InsightModel, TeamType } from '~/types' 2 | 3 | export enum ExportType { 4 | Image = 'image', 5 | Embed = 'embed', 6 | Scene = 'scene', 7 | } 8 | 9 | export interface ExportOptions { 10 | whitelabel?: boolean 11 | noHeader?: boolean 12 | legend?: boolean 13 | } 14 | 15 | export interface ExportedData extends ExportOptions { 16 | type: ExportType 17 | dashboard?: DashboardType 18 | insight?: InsightModel 19 | team?: Partial 20 | } 21 | -------------------------------------------------------------------------------- /plugin-server/tests/helpers/worker.ts: -------------------------------------------------------------------------------- 1 | import Piscina from '@posthog/piscina' 2 | 3 | import { defaultConfig } from '../../src/config/config' 4 | import { LogLevel } from '../../src/types' 5 | import { makePiscina } from '../../src/worker/piscina' 6 | 7 | export function setupPiscina(workers: number, tasksPerWorker: number): Piscina { 8 | return makePiscina({ 9 | ...defaultConfig, 10 | WORKER_CONCURRENCY: workers, 11 | TASKS_PER_WORKER: tasksPerWorker, 12 | LOG_LEVEL: LogLevel.Log, 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /.storybook/app-context.ts: -------------------------------------------------------------------------------- 1 | import { AppContext } from '~/types' 2 | import { MOCK_DEFAULT_TEAM } from 'lib/api.mock' 3 | 4 | export const getStorybookAppContext = (): AppContext => ({ 5 | anonymous: false, 6 | current_team: MOCK_DEFAULT_TEAM, 7 | current_user: undefined as any, // undefined triggers a fetch and lets us mock the data 8 | default_event_name: '$pageview', 9 | persisted_feature_flags: [], 10 | preflight: null as any, // null triggers a fetch and lets us mock the data 11 | switched_team: null, 12 | }) 13 | -------------------------------------------------------------------------------- /posthog/migrations/0089_auto_20201015_1031.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.7 on 2020-10-15 10:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0088_toolbar_disabled"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="action", 15 | name="slack_message_format", 16 | field=models.CharField(blank=True, default="", max_length=200), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /posthog/migrations/0104_auto_20201208_1052.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2020-12-08 10:52 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0103_retention_remove_date"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="dashboarditem", name="funnel", field=models.IntegerField(blank=True, null=True), 15 | ), 16 | migrations.DeleteModel(name="Funnel",), 17 | ] 18 | -------------------------------------------------------------------------------- /bin/install-macosx_arm64: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Install scripts for M1 Macs 4 | # See https://github.com/PostHog/posthog/issues/2916 5 | # NB: use cryptography==3.4.7 6 | 7 | # Set ld flags to use OpenSSL installed with brew 8 | export LDFLAGS="-L$(brew --prefix openssl)/lib" 9 | export CPPFLAGS="-I$(brew --prefix openssl)/include" 10 | 11 | # Use system OpenSSL instead of BoringSSL for GRPC 12 | export GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=1 13 | export GRPC_PYTHON_BUILD_SYSTEM_ZLIB=1 14 | 15 | pip cache purge 16 | pip install -r requirements.txt 17 | -------------------------------------------------------------------------------- /posthog/migrations/0024_add_event_distinct_id_index.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-02-25 04:07 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | atomic = False 8 | dependencies = [ 9 | ("posthog", "0023_team_opt_out_capture"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RunSQL( 14 | "CREATE INDEX CONCURRENTLY idx_distinct_id ON posthog_event(distinct_id);", 15 | reverse_sql='DROP INDEX "idx_distinct_id";', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /posthog/migrations/0075_action_slack_message_format.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.7 on 2020-08-04 14:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0074_toolbar_default_on"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="action", 15 | name="slack_message_format", 16 | field=models.CharField(blank=True, max_length=200, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /posthog/migrations/0177_path_cleaning_filters.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.8 on 2021-10-15 17:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0176_update_person_props_function"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="team", 15 | name="path_cleaning_filters", 16 | field=models.JSONField(blank=True, default=list, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | module.exports = { 3 | plugins: [ 4 | '@babel/plugin-transform-runtime', 5 | '@babel/plugin-transform-react-jsx', 6 | '@babel/plugin-proposal-class-properties', 7 | '@babel/plugin-proposal-private-property-in-object', 8 | ], 9 | presets: [ 10 | [ 11 | '@babel/preset-env', 12 | { 13 | useBuiltIns: 'usage', 14 | corejs: 3, 15 | }, 16 | ], 17 | '@babel/typescript', 18 | ], 19 | } 20 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/test/test_person_distinct_id_query.py: -------------------------------------------------------------------------------- 1 | from posthog.queries import person_distinct_id_query 2 | 3 | 4 | def test_person_distinct_id_query(db, snapshot): 5 | person_distinct_id_query.using_new_table = True 6 | assert person_distinct_id_query.get_team_distinct_ids_query(2) == snapshot 7 | 8 | person_distinct_id_query.using_new_table = False 9 | assert person_distinct_id_query.get_team_distinct_ids_query(2) == snapshot 10 | 11 | # Reset for other tests 12 | person_distinct_id_query.using_new_table = True 13 | -------------------------------------------------------------------------------- /frontend/src/lib/components/TitleWithIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export interface TitleWithIconProps { 4 | icon: JSX.Element 5 | children: string | JSX.Element 6 | 'data-attr'?: string 7 | } 8 | 9 | export function TitleWithIcon({ icon, children, 'data-attr': dataAttr }: TitleWithIconProps): JSX.Element { 10 | return ( 11 |
12 |
{children}
13 |
{icon}
14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/lib/components/icons.scss: -------------------------------------------------------------------------------- 1 | // Antd arrows rotated at convenient angles 2 | .anticon-wraper-arrow { 3 | &.topLeft { 4 | svg { 5 | transform: rotate(-45deg); 6 | } 7 | } 8 | &.topRight { 9 | svg { 10 | transform: rotate(45deg); 11 | } 12 | } 13 | &.bottomRight { 14 | svg { 15 | transform: rotate(135deg); 16 | } 17 | } 18 | &.bottomLeft { 19 | svg { 20 | transform: rotate(-135deg); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /posthog/migrations/0035_current_url_index_20200318_1459.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-03-18 14:59 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("posthog", "0034_pg_trgm_and_btree_20200318_1447"), 9 | ] 10 | 11 | operations = [ 12 | # We used to create an index here, but realised that made things slower rather than faster 13 | migrations.RunSQL("SELECT 1;", "DROP INDEX IF EXISTS posthog_event_properties_current_url_gin") 14 | ] 15 | -------------------------------------------------------------------------------- /posthog/migrations/0039_populate_event_ip_property.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ("posthog", "0038_migrate_actions_to_precalculate_events"), 8 | ] 9 | 10 | operations = [ 11 | migrations.RunSQL( 12 | """ 13 | UPDATE "posthog_event" 14 | SET properties = properties || jsonb_build_object('$ip', ip) 15 | WHERE ip IS NOT NULL; 16 | """, 17 | "", 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /posthog/migrations/0127_add_dashboard_filters.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2021-02-16 13:38 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("posthog", "0126_fix_funnels_insights_links"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="dashboard", name="filters", field=django.contrib.postgres.fields.jsonb.JSONField(default=dict), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /frontend/src/lib/components/DateFilter/DateFilterRange.scss: -------------------------------------------------------------------------------- 1 | .DateFilterRange__calendartoday { 2 | display: inline-block; 3 | position: relative; 4 | } 5 | 6 | .DateFilterRange__calendartoday::before { 7 | content: '•' !important; 8 | top: 0.5rem !important; 9 | border: 0 !important; 10 | font-size: 0.625em; 11 | } 12 | 13 | .ant-picker-range-arrow { 14 | border-top: 1px solid var(--primary); 15 | border-right: 1px solid var(--primary); 16 | } 17 | .ant-picker-range-wrapper { 18 | border: 1px solid var(--primary); 19 | } 20 | -------------------------------------------------------------------------------- /posthog/clickhouse/replication/utils.py: -------------------------------------------------------------------------------- 1 | from posthog.models.async_migration import is_async_migration_complete 2 | from posthog.settings import TEST 3 | 4 | _is_replicated = False 5 | 6 | 7 | def clickhouse_is_replicated() -> bool: 8 | # This is cached in a way where subsequent lookups don't result in queries if the migration is complete! 9 | global _is_replicated 10 | 11 | if _is_replicated: 12 | return True 13 | 14 | _is_replicated = is_async_migration_complete("0004_replicated_schema") 15 | return _is_replicated or TEST 16 | -------------------------------------------------------------------------------- /posthog/migrations/0064_toolbar_mode.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.5 on 2020-06-08 09:41 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0063_team_completed_snippet_onboarding"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="user", 15 | name="toolbar_mode", 16 | field=models.CharField(blank=True, default="default", max_length=200, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /posthog/migrations/0125_longer_webhook_url.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2021-02-11 11:32 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0124_unset_is_calculating_static_cohorts"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="team", 15 | name="slack_incoming_webhook", 16 | field=models.CharField(blank=True, max_length=500, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /posthog/migrations/0226_longer_action_slack_message_format.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-04-20 12:08 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0225_insight_viewed"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="action", 15 | name="slack_message_format", 16 | field=models.CharField(blank=True, default="", max_length=600), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /frontend/src/scenes/ingestion/frameworks/CodeSnippet.scss: -------------------------------------------------------------------------------- 1 | .CodeSnippet { 2 | position: relative; 3 | .CodeSnippet__actions { 4 | position: absolute; 5 | display: flex; 6 | top: 0.75rem; 7 | right: 0.75rem; 8 | gap: 0.5rem; 9 | font-size: 1.5rem; 10 | .CodeSnippet__copy-button { 11 | background: rgb(39, 40, 34) !important; 12 | box-shadow: 0 0 10px rgb(39 40 34) !important; 13 | } 14 | } 15 | button, 16 | svg { 17 | color: #fff; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /posthog/migrations/0182_sessionrecordingevent_window_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-11-03 21:03 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0181_team_correlation_config"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="sessionrecordingevent", 15 | name="window_id", 16 | field=models.CharField(blank=True, max_length=200, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /ee/migrations/0009_deprecated_old_tags.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2022-02-17 18:11 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("ee", "0008_null_definition_descriptions"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField(model_name="enterpriseeventdefinition", old_name="tags", new_name="deprecated_tags",), 14 | migrations.RenameField(model_name="enterprisepropertydefinition", old_name="tags", new_name="deprecated_tags",), 15 | ] 16 | -------------------------------------------------------------------------------- /frontend/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PostHog", 3 | "short_name": "PostHog", 4 | "icons": [ 5 | { 6 | "src": "/static/icons/android-chrome-192x192.png?v=2021-04-28", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/static/icons/android-chrome-512x512.png?v=2021-04-28", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff" 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/globals.d.ts: -------------------------------------------------------------------------------- 1 | import posthog from 'posthog-js' 2 | import { ExportedData } from '~/exporter/types' 3 | 4 | declare global { 5 | interface Window { 6 | JS_POSTHOG_API_KEY?: str 7 | JS_POSTHOG_HOST?: str 8 | JS_POSTHOG_SELF_CAPTURE?: boolean 9 | JS_CAPTURE_INTERNAL_METRICS?: boolean 10 | posthog?: posthog 11 | ESBUILD_LOAD_SCRIPT: (name) => void 12 | ESBUILD_LOAD_CHUNKS: (name) => void 13 | ESBUILD_LOADED_CHUNKS: Set 14 | POSTHOG_EXPORTED_DATA: ExportedData 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /plugin-server/src/config/mmdb-constants.ts: -------------------------------------------------------------------------------- 1 | export const MMDB_ENDPOINT = 'https://mmdbcdn.posthog.net/' 2 | export const MMDB_ATTACHMENT_KEY = '@posthog/mmdb' 3 | export const MMDB_STALE_AGE_DAYS = 45 4 | export const MMDB_STATUS_REDIS_KEY = '@posthog-plugin-server/mmdb-status' 5 | export const MMDB_INTERNAL_SERVER_TIMEOUT_SECONDS = 10 6 | 7 | export enum MMDBRequestStatus { 8 | TimedOut = 'Internal MMDB server connection timed out!', 9 | ServiceUnavailable = 'IP location capabilities are not available in this PostHog instance!', 10 | OK = 'OK', 11 | } 12 | -------------------------------------------------------------------------------- /posthog/management/commands/sync_available_features.py: -------------------------------------------------------------------------------- 1 | import structlog 2 | from django.core.management.base import BaseCommand 3 | 4 | from posthog.tasks.sync_all_organization_available_features import sync_all_organization_available_features 5 | 6 | logger = structlog.get_logger(__name__) 7 | 8 | 9 | class Command(BaseCommand): 10 | help = "Sync available features for all organizations" 11 | 12 | def handle(self, *args, **options): 13 | sync_all_organization_available_features() 14 | logger.info("Features synced for all organizations") 15 | -------------------------------------------------------------------------------- /posthog/migrations/0020_auto_20200210_0212.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-02-10 02:12 2 | 3 | import django.utils.timezone 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("posthog", "0019_team_name"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="event", 16 | name="timestamp", 17 | field=models.DateTimeField(blank=True, default=django.utils.timezone.now), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /posthog/migrations/0116_session_recording_retention_period.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2021-01-25 11:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0115_session_recording_viewed"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="team", 15 | name="session_recording_retention_period_days", 16 | field=models.IntegerField(default=None, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/retention/retention.py: -------------------------------------------------------------------------------- 1 | from ee.clickhouse.queries.retention.retention_actors import ( 2 | ClickhouseRetentionActors, 3 | ClickhouseRetentionActorsByPeriod, 4 | ) 5 | from ee.clickhouse.queries.retention.retention_event_query import ClickhouseRetentionEventsQuery 6 | from posthog.queries.retention.retention import Retention 7 | 8 | 9 | class ClickhouseRetention(Retention): 10 | event_query = ClickhouseRetentionEventsQuery 11 | actors_query = ClickhouseRetentionActors 12 | actors_by_period_query = ClickhouseRetentionActorsByPeriod 13 | -------------------------------------------------------------------------------- /posthog/logging/test/test_timing.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import Mock, call, patch 2 | 3 | from posthog.logging.timing import timed 4 | 5 | 6 | @patch("posthog.logging.timing.statsd.timer") 7 | def test_wrap_with_timing_calls_statsd(mock_timer) -> None: 8 | timer_instance = Mock() 9 | mock_timer.return_value = timer_instance 10 | 11 | @timed(name="test") 12 | def test_func(): 13 | pass 14 | 15 | test_func() 16 | 17 | mock_timer.assert_called_with("test") 18 | timer_instance.assert_has_calls(calls=[call.start(), call.start().stop()]) 19 | -------------------------------------------------------------------------------- /posthog/migrations/0123_organizationinvite_first_name.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2021-02-03 09:26 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0122_organization_setup_section_2_completed"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="organizationinvite", 15 | name="first_name", 16 | field=models.CharField(blank=True, default="", max_length=30), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /posthog/migrations/0169_person_properties_last_updated_at.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.12 on 2021-09-21 16:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0168_action_step_empty_string_reset"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="person", 15 | name="properties_last_updated_at", 16 | field=models.JSONField(blank=True, default=dict, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /frontend/src/scenes/insights/EditorFilters/LifecycleGlobalFilters.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { TestAccountFilter } from 'scenes/insights/filters/TestAccountFilter' 3 | import { useActions } from 'kea' 4 | import { trendsLogic } from 'scenes/trends/trendsLogic' 5 | import { EditorFilterProps } from '~/types' 6 | 7 | export function LifecycleGlobalFilters({ filters, insightProps }: EditorFilterProps): JSX.Element { 8 | const { setFilters } = useActions(trendsLogic(insightProps)) 9 | return 10 | } 11 | -------------------------------------------------------------------------------- /posthog/queries/retention/__init__.py: -------------------------------------------------------------------------------- 1 | from posthog.settings import EE_AVAILABLE 2 | 3 | if EE_AVAILABLE: 4 | from ee.clickhouse.queries.retention.retention import ClickhouseRetention as Retention 5 | from ee.clickhouse.queries.retention.retention_actors import ClickhouseRetentionActors as RetentionActors 6 | else: 7 | from posthog.queries.retention.actors_query import RetentionActors # type: ignore 8 | from posthog.queries.retention.retention import Retention # type: ignore 9 | 10 | from .retention import build_returning_event_query, build_target_event_query 11 | -------------------------------------------------------------------------------- /ee/clickhouse/models/test/__snapshots__/test_cohort.ambr: -------------------------------------------------------------------------------- 1 | # name: TestCohort.test_static_cohort_precalculated 2 | ' 3 | 4 | SELECT distinct_id 5 | FROM 6 | (SELECT distinct_id, 7 | argMax(person_id, version) as person_id 8 | FROM person_distinct_id2 9 | WHERE team_id = 2 10 | GROUP BY distinct_id 11 | HAVING argMax(is_deleted, version) = 0) 12 | WHERE person_id IN 13 | (SELECT person_id 14 | FROM person_static_cohort 15 | WHERE cohort_id = %(_cohort_id_0)s 16 | AND team_id = %(team_id)s) 17 | ' 18 | --- 19 | -------------------------------------------------------------------------------- /frontend/src/lib/utils/logics.ts: -------------------------------------------------------------------------------- 1 | import { teamLogic } from '../../scenes/teamLogic' 2 | import { TeamType } from '../../types' 3 | import { getAppContext } from './getAppContext' 4 | 5 | export function getCurrentTeamId(providedMaybeTeamId?: TeamType['id'] | null): TeamType['id'] { 6 | const maybeTeamId = providedMaybeTeamId !== undefined ? providedMaybeTeamId : teamLogic.values.currentTeamId 7 | if (!maybeTeamId) { 8 | throw new Error(`Project ID is not known.${getAppContext()?.anonymous ? ' User is anonymous.' : ''}`) 9 | } 10 | return maybeTeamId 11 | } 12 | -------------------------------------------------------------------------------- /posthog/migrations/0004_auto_20200125_0415.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-01-25 04:15 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("posthog", "0003_person_is_user"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="event", 16 | name="elements", 17 | field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /posthog/migrations/0053_dashboard_item_layouts.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.5 on 2020-05-12 10:13 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("posthog", "0052_data_precalculate_cohorts"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="dashboarditem", 16 | name="layouts", 17 | field=django.contrib.postgres.fields.jsonb.JSONField(default=dict), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /posthog/migrations/0121_person_email_index.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2021-01-05 12:55 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | atomic = False 8 | 9 | dependencies = [ 10 | ("posthog", "0120_organization_personalization"), 11 | ] 12 | 13 | operations = [ 14 | migrations.RunSQL( 15 | "CREATE INDEX CONCURRENTLY IF NOT EXISTS posthog_person_email ON posthog_person((properties->>'email'));", 16 | reverse_sql='DROP INDEX "posthog_person_email";', 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /posthog/settings/shell_plus.py: -------------------------------------------------------------------------------- 1 | from posthog.settings.utils import get_from_env, str_to_bool 2 | 3 | # shell_plus settings 4 | # https://django-extensions.readthedocs.io/en/latest/shell_plus.html 5 | 6 | SHELL_PLUS_PRINT_SQL = get_from_env("PRINT_SQL", False, type_cast=str_to_bool) 7 | SHELL_PLUS_POST_IMPORTS = [ 8 | ("posthog.models.filters", ("Filter",)), 9 | ("posthog.models.property", ("Property",)), 10 | ] 11 | 12 | SHELL_PLUS_POST_IMPORTS.append(("posthog.client", ("sync_execute",))) 13 | SHELL_PLUS_POST_IMPORTS.append(("infi.clickhouse_orm.utils", ("import_submodules",))) 14 | -------------------------------------------------------------------------------- /cypress/e2e/licenses.js: -------------------------------------------------------------------------------- 1 | describe('Licenses', () => { 2 | it('Licenses loaded', () => { 3 | cy.get('[data-attr=top-menu-toggle]').click() 4 | cy.get('[data-attr=top-menu-item-licenses]').click() 5 | cy.get('[data-attr=breadcrumb-0]').should('contain', Cypress.config().baseUrl.replace('http://', '')) // Breadcrumbs work 6 | cy.get('[data-attr=breadcrumb-1]').should('have.text', 'Licenses') // Breadcrumbs work 7 | cy.get('h1').should('contain', 'Licenses') 8 | cy.title().should('equal', 'Licenses • PostHog') // Page title works 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /frontend/src/scenes/funnels/FunnelCanvasLabel.scss: -------------------------------------------------------------------------------- 1 | .insights-graph-header.funnels { 2 | border-bottom: 1px solid var(--border); 3 | 4 | .funnel-canvas-label { 5 | .ant-btn-link { 6 | padding: 0; 7 | &:disabled { 8 | cursor: default; 9 | color: var(--default); 10 | } 11 | } 12 | 13 | .funnel-options-label { 14 | padding: 4px; 15 | } 16 | 17 | .funnel-options-inputs { 18 | align-items: center; 19 | padding: 4px; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /posthog/migrations/0087_fix_annotation_created_at.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.7 on 2020-10-14 07:46 2 | 3 | import django.utils.timezone 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("posthog", "0086_team_session_recording_opt_in"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="annotation", 16 | name="created_at", 17 | field=models.DateTimeField(default=django.utils.timezone.now, null=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /cypress/README.md: -------------------------------------------------------------------------------- 1 | ## Testing Feature Flags 2 | 3 | The Cypress tests run with a PostHog instance that has no feature flags set up. 4 | 5 | To test feature flags you can intercept the call to the `decide` endpoint 6 | 7 | ```javascript 8 | // sometimes the system under test calls `/decide` 9 | // and sometimes it calls https://app.posthog.com/decide 10 | cy.intercept('https://app.posthog.com/decide/*', (req) => 11 | req.reply( 12 | decideResponse({ 13 | // add feature flags here, for e.g. 14 | // 'feature-flag-key': true, 15 | }) 16 | ) 17 | ) 18 | ``` 19 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/stickiness/stickiness_event_query.py: -------------------------------------------------------------------------------- 1 | from posthog.models.group.util import get_aggregation_target_field 2 | from posthog.queries.stickiness.stickiness_event_query import StickinessEventsQuery 3 | 4 | 5 | class ClickhouseStickinessEventsQuery(StickinessEventsQuery): 6 | def aggregation_target(self): 7 | return get_aggregation_target_field( 8 | self._entity.math_group_type_index, 9 | self.EVENT_TABLE_ALIAS, 10 | f"{self.DISTINCT_ID_TABLE_ALIAS if not self._using_person_on_events else self.EVENT_TABLE_ALIAS}.person_id", 11 | ) 12 | -------------------------------------------------------------------------------- /frontend/src/lib/components/PayCard/PayCard.scss: -------------------------------------------------------------------------------- 1 | .pay-card { 2 | box-shadow: var(--shadow-elevation); 3 | padding: 1rem; 4 | border-radius: var(--radius); 5 | border: 1px solid var(--warning); 6 | margin-top: 1rem; 7 | cursor: pointer; 8 | position: relative; 9 | background: var(--bg-light); 10 | 11 | h3 { 12 | font-weight: var(--font-medium); 13 | } 14 | 15 | .close-button { 16 | position: absolute; 17 | top: 16px; 18 | right: 16px; 19 | color: var(--muted); 20 | z-index: var(--z-raised); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/lib/components/icons/Splotch.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ComponentMeta, ComponentStory } from '@storybook/react' 3 | import { Splotch, SplotchColor, SplotchProps } from './Splotch' 4 | 5 | export default { 6 | title: 'Lemon UI/Splotch', 7 | component: Splotch, 8 | argTypes: { 9 | color: { 10 | defaultValue: SplotchColor.Purple, 11 | }, 12 | }, 13 | } as ComponentMeta 14 | 15 | export const _Splotch: ComponentStory = (props: SplotchProps) => { 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/mocks/jest.ts: -------------------------------------------------------------------------------- 1 | import { setupServer } from 'msw/node' 2 | import { handlers } from '~/mocks/handlers' 3 | import { Mocks, mocksToHandlers } from '~/mocks/utils' 4 | import { useAvailableFeatures } from '~/mocks/features' 5 | 6 | export const mswServer = setupServer(...handlers) 7 | export const useMocks = (mocks: Mocks): void => mswServer.use(...mocksToHandlers(mocks)) 8 | 9 | window.confirm = jest.fn() 10 | 11 | beforeAll(() => { 12 | useAvailableFeatures([]) 13 | mswServer.listen() 14 | }) 15 | afterEach(() => mswServer.resetHandlers()) 16 | afterAll(() => mswServer.close()) 17 | -------------------------------------------------------------------------------- /posthog/migrations/0070_team_event_properties_numerical.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-07-21 13:25 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("posthog", "0069_auto_20200714_1642"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="team", 16 | name="event_properties_numerical", 17 | field=django.contrib.postgres.fields.jsonb.JSONField(default=list), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /posthog/migrations/0120_organization_personalization.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2021-01-29 09:24 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("posthog", "0119_mandatory_plugin_order"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="organization", 16 | name="personalization", 17 | field=django.contrib.postgres.fields.jsonb.JSONField(default=dict), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /posthog/migrations/0152_user_events_column_config.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.8 on 2021-04-30 20:16 2 | 3 | from django.db import migrations, models 4 | 5 | import posthog.models.user 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ("posthog", "0151_plugin_preinstalled"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="user", 17 | name="events_column_config", 18 | field=models.JSONField(default=posthog.models.user.events_column_config_default), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /posthog/migrations/0066_team_created_at.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.7 on 2020-06-23 17:13 2 | 3 | from django.db import migrations, models 4 | from django.utils.timezone import now 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("posthog", "0065_auto_20200624_1842"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="team", 16 | name="created_at", 17 | field=models.DateTimeField(auto_now_add=True, default=now), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /frontend/src/lib/components/StarryBackground/index.scss: -------------------------------------------------------------------------------- 1 | .starry-background { 2 | height: 100%; 3 | background: linear-gradient(180deg, #200c39 0%, #373088 75%, #4a3587 100%); 4 | 5 | .stars { 6 | z-index: var(--z-city-background-image); 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | right: 0; 11 | bottom: 0; 12 | background: url('./stars.svg') repeat center center; 13 | } 14 | 15 | .children { 16 | height: 100%; 17 | position: relative; 18 | z-index: var(--z-city-background-content); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/scenes/plugins/edit/CapabilitiesInfo.ts: -------------------------------------------------------------------------------- 1 | export const capabilitiesInfo: Record = { 2 | processEvent: 'Read + modify access to events before ingestion', 3 | processEventBatch: 'Read + modify access to events before ingestion, in batches', 4 | onEvent: 'Read-only access to events', 5 | onSnapshot: 'Read-only access to session recording events', 6 | exportEvents: 'Read-only access to events, optimized for export', 7 | runEveryMinute: 'Runs a task every minute', 8 | runEveryHour: 'Runs a task every hour', 9 | runEveryDay: 'Runs a task every day', 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/layout/ErrorNetwork.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button } from 'antd' 3 | import { ReloadOutlined } from '@ant-design/icons' 4 | 5 | export function ErrorNetwork(): JSX.Element { 6 | return ( 7 |
8 |

Network Error

9 |

There was an issue loading the requested resource.

10 |

11 | 14 |

15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/lib/hooks/useEscapeKey.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect } from 'react' 2 | 3 | export function useEscapeKey(callback, deps = []) { 4 | const escFunction = useCallback( 5 | (event) => { 6 | if (event.keyCode === 27) { 7 | callback() 8 | } 9 | }, 10 | 11 | deps 12 | ) 13 | 14 | useEffect( 15 | () => { 16 | document.addEventListener('keydown', escFunction, false) 17 | return () => document.removeEventListener('keydown', escFunction, false) 18 | }, 19 | 20 | deps 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/scenes/instance/Licenses/Licenses.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { Meta } from '@storybook/react' 3 | import { App } from 'scenes/App' 4 | import { router } from 'kea-router' 5 | import { urls } from 'scenes/urls' 6 | 7 | export default { 8 | title: 'Scenes-App/Licenses', 9 | parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' }, 10 | } as Meta 11 | 12 | export const Licenses = (): JSX.Element => { 13 | useEffect(() => { 14 | router.actions.push(urls.instanceLicenses()) 15 | }, []) 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /posthog/migrations/0199_update_experiment_model.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2022-01-24 12:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0198_async_migration_error"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField(model_name="experiment", name="archived", field=models.BooleanField(default=False),), 14 | migrations.AddField( 15 | model_name="experiment", name="secondary_metrics", field=models.JSONField(default=list, null=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /cypress/e2e/annotations.js: -------------------------------------------------------------------------------- 1 | describe('Annotations', () => { 2 | beforeEach(() => { 3 | cy.clickNavMenu('annotations') 4 | }) 5 | 6 | it('Annotations loaded', () => { 7 | cy.get('h1').should('contain', 'Annotations') 8 | }) 9 | 10 | it('Create annotation', () => { 11 | cy.get('[data-attr=create-annotation]').click() 12 | cy.get('[data-attr=create-annotation-input]').type('Test Annotation') 13 | cy.get('[data-attr=create-annotation-submit]').click() 14 | cy.get('[data-attr=annotations-table]').contains('Test Annotation').should('exist') 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /ee/clickhouse/models/test/utils/util.py: -------------------------------------------------------------------------------- 1 | from time import sleep, time 2 | 3 | from posthog.client import sync_execute 4 | 5 | 6 | # this normally is unnecessary as CH is fast to consume from Kafka when testing 7 | # but it helps prevent potential flakiness 8 | def delay_until_clickhouse_consumes_from_kafka(table_name: str, target_row_count: int, timeout_seconds=10) -> None: 9 | ts_start = time() 10 | while time() < ts_start + timeout_seconds: 11 | result = sync_execute(f"SELECT COUNT(1) FROM {table_name}") 12 | if result[0][0] == target_row_count: 13 | return 14 | sleep(0.5) 15 | -------------------------------------------------------------------------------- /ee/tasks/test/subscriptions/subscriptions_test_factory.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Any 3 | 4 | import pytz 5 | 6 | from posthog.models.subscription import Subscription 7 | 8 | 9 | def create_subscription(**kwargs: Any) -> Subscription: 10 | payload = dict( 11 | target_type="email", 12 | target_value="test1@posthog.com,test2@posthog.com", 13 | frequency="daily", 14 | interval=1, 15 | start_date=datetime(2022, 1, 1, 9, 0).replace(tzinfo=pytz.UTC), 16 | ) 17 | 18 | payload.update(kwargs) 19 | return Subscription.objects.create(**payload) 20 | -------------------------------------------------------------------------------- /frontend/src/lib/utils/changeType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Change Type Of Passed Value 3 | * @param {Type} type The type to be casted to 4 | * @param {Number/String} value the value to be changed 5 | **/ 6 | export function changeType(type, value) { 7 | switch (type) { 8 | case 'text': 9 | return String(value) 10 | case 'number': 11 | return Number(value) 12 | case 'boolean': 13 | return value == 'true' ? true : false 14 | case 'string': 15 | return String(value) 16 | default: 17 | throw 'Unsupported type' 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /posthog/queries/funnels/sql.py: -------------------------------------------------------------------------------- 1 | FUNNEL_PERSONS_BY_STEP_SQL = """ 2 | SELECT aggregation_target AS actor_id{matching_events_select_statement} {extra_fields} 3 | FROM ( 4 | {steps_per_person_query} 5 | ) 6 | WHERE {persons_steps} 7 | ORDER BY aggregation_target 8 | {limit} 9 | {offset} 10 | SETTINGS allow_experimental_window_functions = 1 11 | """ 12 | 13 | FUNNEL_INNER_EVENT_STEPS_QUERY = """ 14 | SELECT 15 | aggregation_target, 16 | timestamp, 17 | {steps} 18 | {select_prop} 19 | {extra_fields} 20 | FROM ( 21 | {event_query} 22 | ) events 23 | {extra_join} 24 | WHERE ( 25 | {steps_condition} 26 | ) 27 | """ 28 | -------------------------------------------------------------------------------- /frontend/src/scenes/retention/RetentionTable.scss: -------------------------------------------------------------------------------- 1 | .retention-table { 2 | .ant-table-tbody .ant-table-cell { 3 | padding: 0px !important; 4 | } 5 | 6 | .percentage-cell { 7 | padding: 8px; 8 | } 9 | 10 | .period-in-progress { 11 | background: repeating-linear-gradient(-45deg, #e4edf9, #e4edf9 10px, #f8fbfe 10px, #f8fbfe 20px); 12 | color: #555555; 13 | } 14 | } 15 | 16 | .retention-success { 17 | border-radius: var(--radius); 18 | background-color: var(--depr-lighten-primary-20); 19 | } 20 | .retention-dropped { 21 | background-color: var(--bg-mid); 22 | } 23 | -------------------------------------------------------------------------------- /posthog/clickhouse/migrations/0024_materialize_window_and_session_id.py: -------------------------------------------------------------------------------- 1 | # This migration has been replaced by 0026_fix_materialized_window_and_session_ids 2 | # The original migration led to potentially inconsistent names for materialized columns 3 | # If the columns were created with this migration, they would be `$session_id` and `$window_id`, 4 | # but if the columns were created with the normal column materialization job, they would be `mat_$session_id` 5 | # and `mat_$window_id`. This led to potential inconsistencies between the tables state and the 6 | # schema defined by `EVENTS_TABLE_SQL` 7 | 8 | operations = [] # type: ignore 9 | -------------------------------------------------------------------------------- /posthog/migrations/0048_auto_20200420_1051.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-04-20 10:51 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0047_auto_20200416_1631"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveIndex(model_name="event", name="posthog_eve_timesta_b00cec_idx",), 14 | migrations.AddIndex( 15 | model_name="event", 16 | index=models.Index(fields=["timestamp", "team_id", "event"], name="posthog_eve_timesta_1f6a8c_idx",), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /ee/clickhouse/queries/stickiness/stickiness_actors.py: -------------------------------------------------------------------------------- 1 | from ee.clickhouse.queries.stickiness.stickiness_event_query import ClickhouseStickinessEventsQuery 2 | from posthog.models.filters.mixins.utils import cached_property 3 | from posthog.queries.stickiness.stickiness_actors import StickinessActors 4 | 5 | 6 | class ClickhouseStickinessActors(StickinessActors): 7 | event_query_class = ClickhouseStickinessEventsQuery 8 | 9 | @cached_property 10 | def aggregation_group_type_index(self): 11 | if self.entity.math == "unique_group": 12 | return self.entity.math_group_type_index 13 | return None 14 | -------------------------------------------------------------------------------- /frontend/src/lib/components/HotkeyButton/HotkeyButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button, ButtonProps } from 'antd' 2 | import React from 'react' 3 | import { HotKeys } from '~/types' 4 | import './HotkeyButton.scss' 5 | 6 | interface HotkeyButtonProps extends ButtonProps { 7 | hotkey: HotKeys 8 | } 9 | 10 | export function HotkeyButton({ hotkey, children, ...props }: HotkeyButtonProps): JSX.Element { 11 | return ( 12 | 13 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /posthog/migrations/0136_global_plugin_attachments.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2021-03-23 17:11 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("posthog", "0135_plugins_on_cloud"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="pluginattachment", 16 | name="plugin_config", 17 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to="posthog.PluginConfig"), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /posthog/queries/funnels/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from .base import ClickhouseFunnelBase 3 | from .funnel import ClickhouseFunnel 4 | from .funnel_persons import ClickhouseFunnelActors 5 | from .funnel_strict import ClickhouseFunnelStrict 6 | from .funnel_strict_persons import ClickhouseFunnelStrictActors 7 | from .funnel_time_to_convert import ClickhouseFunnelTimeToConvert 8 | from .funnel_trends import ClickhouseFunnelTrends 9 | from .funnel_trends_persons import ClickhouseFunnelTrendsActors 10 | from .funnel_unordered import ClickhouseFunnelUnordered 11 | from .funnel_unordered_persons import ClickhouseFunnelUnorderedActors 12 | -------------------------------------------------------------------------------- /frontend/src/lib/hooks/useInterval.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react' 2 | 3 | export function useInterval(callback, delay) { 4 | const savedCallback = useRef() 5 | 6 | // Remember the latest callback. 7 | useEffect(() => { 8 | savedCallback.current = callback 9 | }, [callback]) 10 | 11 | // Set up the interval. 12 | useEffect(() => { 13 | function tick() { 14 | savedCallback.current() 15 | } 16 | if (delay !== null) { 17 | let id = setInterval(tick, delay) 18 | return () => clearInterval(id) 19 | } 20 | }, [delay]) 21 | } 22 | -------------------------------------------------------------------------------- /posthog/migrations/0028_actionstep_url_matching.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-03-04 01:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("posthog", "0027_move_elements_to_group"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="actionstep", 15 | name="url_matching", 16 | field=models.CharField( 17 | choices=[("exact", "exact"), ("contains", "contains")], default="contains", max_length=400, 18 | ), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /posthog/migrations/0110_sessionrecordingeventbyteamandtimestamp.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.11 on 2021-01-05 12:55 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | atomic = False 8 | 9 | dependencies = [ 10 | ("posthog", "0109_fix_retention_filters"), 11 | ] 12 | 13 | operations = [ 14 | migrations.RunSQL( 15 | "CREATE INDEX CONCURRENTLY IF NOT EXISTS posthog_ses_team_id_0409c4_idx ON posthog_sessionrecordingevent(team_id, timestamp);", 16 | reverse_sql='DROP INDEX "posthog_ses_team_id_0409c4_idx";', 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /frontend/src/custom.d.ts: -------------------------------------------------------------------------------- 1 | // This fixes TS errors when importing a .svg file 2 | declare module '*.svg' { 3 | const content: any 4 | export default content 5 | } 6 | 7 | // This fixes TS errors when importing a .png file 8 | declare module '*.png' { 9 | const content: any 10 | export default content 11 | } 12 | 13 | // This fixes TS errors when importing an .mp3 file 14 | declare module '*.mp3' { 15 | const content: any 16 | export default content 17 | } 18 | 19 | // This fixes TS errors when importing an .lottie file 20 | declare module '*.lottie' { 21 | const content: any 22 | export default content 23 | } 24 | -------------------------------------------------------------------------------- /plugin-server/src/utils/fetch.ts: -------------------------------------------------------------------------------- 1 | // This module wraps node-fetch with a sentry tracing-aware extension 2 | 3 | import fetch, { Request, Response } from 'node-fetch' 4 | 5 | import { runInSpan } from '../sentry' 6 | 7 | function fetchWrapper(...args: Parameters): Promise { 8 | const request = new Request(...args) 9 | return runInSpan( 10 | { 11 | op: 'fetch', 12 | description: `${request.method} ${request.hostname}`, 13 | }, 14 | () => fetch(...args) 15 | ) 16 | } 17 | 18 | fetchWrapper.isRedirect = fetch.isRedirect 19 | 20 | export default fetchWrapper 21 | -------------------------------------------------------------------------------- /frontend/src/lib/components/PayGateMini/PayGateMini.scss: -------------------------------------------------------------------------------- 1 | .PayGateMini { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | text-align: center; 6 | background: var(--bg-side); 7 | border-radius: var(--radius); 8 | padding: 0.75rem; 9 | font-size: 0.875rem; 10 | } 11 | 12 | .PayGateMini__icon { 13 | display: flex; 14 | color: var(--warning); 15 | font-size: 2rem; 16 | } 17 | 18 | .PayGateMini__description { 19 | margin-top: 0.75rem; 20 | font-weight: 600; 21 | color: var(--primary-alt); 22 | } 23 | 24 | .PayGateMini__cta { 25 | margin-bottom: 0.75rem; 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/toolbar/button/icons/Close.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export function Close(props: React.PropsWithoutRef): JSX.Element { 4 | return ( 5 | 6 | 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /posthog/async_migrations/migrations/0001_events_sample_by.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from posthog.async_migrations.definition import AsyncMigrationDefinition, AsyncMigrationOperation 4 | 5 | """ 6 | Nooping this migration for future compatibility. Superseded by 0002_events_sample_by. 7 | 8 | If users ran the old version of this, they will be ok to run 0002, if not, they will also be ok to run it. 9 | """ 10 | 11 | 12 | class Migration(AsyncMigrationDefinition): 13 | 14 | description = "Test migration" 15 | 16 | operations: List[AsyncMigrationOperation] = [] 17 | 18 | def is_required(self): 19 | return False 20 | -------------------------------------------------------------------------------- /posthog/migrations/0237_remove_timezone_from_teams.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-04-22 09:10 2 | from django.db import migrations 3 | 4 | 5 | def reset_team_timezone_to_UTC(apps, _) -> None: 6 | Team = apps.get_model("posthog", "Team") 7 | Team.objects.exclude(timezone="UTC").update(timezone="UTC") 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ("posthog", "0236_add_instance_setting_model"), 14 | ] 15 | 16 | def reverse(apps, _) -> None: 17 | pass 18 | 19 | operations = [ 20 | migrations.RunPython(reset_team_timezone_to_UTC, reverse), 21 | ] 22 | --------------------------------------------------------------------------------