trueppm-api 0.4.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trueppm_api/__init__.py +14 -0
- trueppm_api/apps/access/__init__.py +0 -0
- trueppm_api/apps/access/apps.py +19 -0
- trueppm_api/apps/access/groups.py +518 -0
- trueppm_api/apps/access/management/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/create_admin.py +164 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +209 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +167 -0
- trueppm_api/apps/access/migrations/0002_alter_projectmembership_id.py +29 -0
- trueppm_api/apps/access/migrations/0003_projectmembership_soft_delete.py +24 -0
- trueppm_api/apps/access/migrations/0004_alter_projectmembership_role.py +34 -0
- trueppm_api/apps/access/migrations/0005_program_entity_and_membership.py +76 -0
- trueppm_api/apps/access/migrations/0006_role_ordinal_spacing.py +113 -0
- trueppm_api/apps/access/migrations/0007_projectmembership_joined_at_and_more.py +35 -0
- trueppm_api/apps/access/migrations/0008_projectmembership_source_group.py +25 -0
- trueppm_api/apps/access/migrations/0009_projectmembership_pm_proj_serverver_idx.py +20 -0
- trueppm_api/apps/access/migrations/0010_programmembership_joined_at_and_more.py +38 -0
- trueppm_api/apps/access/migrations/0011_alter_programmembership_unique_together_and_more.py +36 -0
- trueppm_api/apps/access/migrations/0012_programmembership_role_title.py +17 -0
- trueppm_api/apps/access/migrations/0013_userdefinedmentiongroup.py +82 -0
- trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
- trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
- trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
- trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +132 -0
- trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
- trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
- trueppm_api/apps/access/migrations/__init__.py +0 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +2837 -0
- trueppm_api/apps/access/serializers.py +946 -0
- trueppm_api/apps/access/services.py +667 -0
- trueppm_api/apps/access/signals.py +103 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +95 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1931 -0
- trueppm_api/apps/agents/__init__.py +6 -0
- trueppm_api/apps/agents/apps.py +11 -0
- trueppm_api/apps/agents/canonical.py +73 -0
- trueppm_api/apps/agents/deferred.py +108 -0
- trueppm_api/apps/agents/management/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
- trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
- trueppm_api/apps/agents/middleware.py +44 -0
- trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
- trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
- trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
- trueppm_api/apps/agents/migrations/__init__.py +0 -0
- trueppm_api/apps/agents/models.py +371 -0
- trueppm_api/apps/agents/refusal.py +103 -0
- trueppm_api/apps/agents/serializers.py +85 -0
- trueppm_api/apps/agents/services.py +397 -0
- trueppm_api/apps/agents/signals.py +30 -0
- trueppm_api/apps/agents/urls.py +15 -0
- trueppm_api/apps/agents/views.py +108 -0
- trueppm_api/apps/csvimport/__init__.py +12 -0
- trueppm_api/apps/csvimport/apps.py +11 -0
- trueppm_api/apps/csvimport/mapping.py +363 -0
- trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
- trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
- trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
- trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
- trueppm_api/apps/csvimport/models.py +111 -0
- trueppm_api/apps/csvimport/parser.py +1825 -0
- trueppm_api/apps/csvimport/services.py +48 -0
- trueppm_api/apps/csvimport/tasks.py +418 -0
- trueppm_api/apps/csvimport/template.py +37 -0
- trueppm_api/apps/csvimport/urls.py +50 -0
- trueppm_api/apps/csvimport/views.py +793 -0
- trueppm_api/apps/history/__init__.py +0 -0
- trueppm_api/apps/history/apps.py +12 -0
- trueppm_api/apps/history/changelog.py +359 -0
- trueppm_api/apps/history/diff_policy.py +177 -0
- trueppm_api/apps/history/serializers.py +104 -0
- trueppm_api/apps/history/signals.py +82 -0
- trueppm_api/apps/history/tasks.py +105 -0
- trueppm_api/apps/history/urls.py +38 -0
- trueppm_api/apps/history/views.py +443 -0
- trueppm_api/apps/idempotency/__init__.py +0 -0
- trueppm_api/apps/idempotency/apps.py +11 -0
- trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
- trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
- trueppm_api/apps/idempotency/mixins.py +226 -0
- trueppm_api/apps/idempotency/models.py +75 -0
- trueppm_api/apps/idempotency/tasks.py +61 -0
- trueppm_api/apps/integrations/__init__.py +13 -0
- trueppm_api/apps/integrations/apps.py +59 -0
- trueppm_api/apps/integrations/connections.py +806 -0
- trueppm_api/apps/integrations/encryption.py +92 -0
- trueppm_api/apps/integrations/external_sources.py +837 -0
- trueppm_api/apps/integrations/git_automation_services.py +231 -0
- trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
- trueppm_api/apps/integrations/http.py +403 -0
- trueppm_api/apps/integrations/me_work.py +218 -0
- trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
- trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
- trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
- trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
- trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
- trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
- trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
- trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
- trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
- trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
- trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
- trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
- trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
- trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +622 -0
- trueppm_api/apps/integrations/notification_channels.py +57 -0
- trueppm_api/apps/integrations/opengraph.py +229 -0
- trueppm_api/apps/integrations/outgoing.py +201 -0
- trueppm_api/apps/integrations/providers.py +519 -0
- trueppm_api/apps/integrations/registry.py +345 -0
- trueppm_api/apps/integrations/serializers.py +453 -0
- trueppm_api/apps/integrations/services.py +171 -0
- trueppm_api/apps/integrations/tasks.py +731 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1106 -0
- trueppm_api/apps/jiraimport/__init__.py +8 -0
- trueppm_api/apps/jiraimport/apps.py +11 -0
- trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
- trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
- trueppm_api/apps/jiraimport/models.py +70 -0
- trueppm_api/apps/jiraimport/parser.py +350 -0
- trueppm_api/apps/jiraimport/services.py +46 -0
- trueppm_api/apps/jiraimport/tasks.py +277 -0
- trueppm_api/apps/jiraimport/urls.py +15 -0
- trueppm_api/apps/jiraimport/views.py +153 -0
- trueppm_api/apps/msproject/__init__.py +0 -0
- trueppm_api/apps/msproject/apps.py +11 -0
- trueppm_api/apps/msproject/dataclasses.py +153 -0
- trueppm_api/apps/msproject/exporter.py +500 -0
- trueppm_api/apps/msproject/extended_attributes.py +77 -0
- trueppm_api/apps/msproject/importer.py +1095 -0
- trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
- trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
- trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
- trueppm_api/apps/msproject/migrations/__init__.py +0 -0
- trueppm_api/apps/msproject/models.py +81 -0
- trueppm_api/apps/msproject/parser.py +1231 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +436 -0
- trueppm_api/apps/msproject/urls.py +39 -0
- trueppm_api/apps/msproject/views.py +531 -0
- trueppm_api/apps/notifications/__init__.py +7 -0
- trueppm_api/apps/notifications/apps.py +15 -0
- trueppm_api/apps/notifications/backfill.py +79 -0
- trueppm_api/apps/notifications/categories.py +112 -0
- trueppm_api/apps/notifications/delivery_limits.py +258 -0
- trueppm_api/apps/notifications/digests.py +447 -0
- trueppm_api/apps/notifications/email_backend.py +330 -0
- trueppm_api/apps/notifications/email_health.py +128 -0
- trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
- trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +294 -0
- trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
- trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
- trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
- trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
- trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
- trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
- trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
- trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
- trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
- trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
- trueppm_api/apps/notifications/migrations/__init__.py +0 -0
- trueppm_api/apps/notifications/models.py +1197 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +961 -0
- trueppm_api/apps/notifications/services.py +1451 -0
- trueppm_api/apps/notifications/tasks.py +711 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +904 -0
- trueppm_api/apps/observability/__init__.py +0 -0
- trueppm_api/apps/observability/apps.py +31 -0
- trueppm_api/apps/observability/logging.py +213 -0
- trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
- trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
- trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
- trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
- trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
- trueppm_api/apps/observability/migrations/__init__.py +0 -0
- trueppm_api/apps/observability/models.py +141 -0
- trueppm_api/apps/observability/otel/__init__.py +45 -0
- trueppm_api/apps/observability/otel/attributes.py +247 -0
- trueppm_api/apps/observability/otel/export_health.py +533 -0
- trueppm_api/apps/observability/otel/instrumentation.py +351 -0
- trueppm_api/apps/observability/otel/metrics.py +551 -0
- trueppm_api/apps/observability/otel/provider.py +511 -0
- trueppm_api/apps/observability/otel/request_attributes.py +204 -0
- trueppm_api/apps/observability/purge_registry.py +124 -0
- trueppm_api/apps/observability/retention.py +139 -0
- trueppm_api/apps/observability/selectors.py +969 -0
- trueppm_api/apps/observability/serializers.py +142 -0
- trueppm_api/apps/observability/services.py +407 -0
- trueppm_api/apps/observability/tasks.py +266 -0
- trueppm_api/apps/observability/urls.py +27 -0
- trueppm_api/apps/observability/views.py +523 -0
- trueppm_api/apps/profiles/__init__.py +0 -0
- trueppm_api/apps/profiles/apps.py +15 -0
- trueppm_api/apps/profiles/constants.py +54 -0
- trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
- trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
- trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
- trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
- trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
- trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
- trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
- trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
- trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
- trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
- trueppm_api/apps/profiles/migrations/__init__.py +0 -0
- trueppm_api/apps/profiles/models.py +276 -0
- trueppm_api/apps/profiles/serializers.py +199 -0
- trueppm_api/apps/profiles/services.py +466 -0
- trueppm_api/apps/profiles/views.py +124 -0
- trueppm_api/apps/projects/__init__.py +0 -0
- trueppm_api/apps/projects/actual_date_rules.py +163 -0
- trueppm_api/apps/projects/amend.py +133 -0
- trueppm_api/apps/projects/apps.py +38 -0
- trueppm_api/apps/projects/asset_feed.py +459 -0
- trueppm_api/apps/projects/asset_views.py +317 -0
- trueppm_api/apps/projects/attachment_policy.py +278 -0
- trueppm_api/apps/projects/authentication.py +449 -0
- trueppm_api/apps/projects/backfill.py +287 -0
- trueppm_api/apps/projects/backlog_services.py +324 -0
- trueppm_api/apps/projects/backlog_views.py +302 -0
- trueppm_api/apps/projects/batch_operation_views.py +264 -0
- trueppm_api/apps/projects/blocker_services.py +510 -0
- trueppm_api/apps/projects/board_activity.py +462 -0
- trueppm_api/apps/projects/board_activity_views.py +186 -0
- trueppm_api/apps/projects/board_lanes.py +134 -0
- trueppm_api/apps/projects/bulk_settings.py +194 -0
- trueppm_api/apps/projects/bundled_templates.py +215 -0
- trueppm_api/apps/projects/calendar_settings.py +180 -0
- trueppm_api/apps/projects/ceremony_views.py +244 -0
- trueppm_api/apps/projects/commit_moment.py +204 -0
- trueppm_api/apps/projects/config_notice.py +1325 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +93 -0
- trueppm_api/apps/projects/decisions_views.py +94 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +353 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4256 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2000 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1825 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1905 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1409 -0
- trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
- trueppm_api/apps/projects/inbound_sync.py +555 -0
- trueppm_api/apps/projects/iteration_label.py +85 -0
- trueppm_api/apps/projects/lifecycle.py +191 -0
- trueppm_api/apps/projects/management/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
- trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
- trueppm_api/apps/projects/management/commands/export_program.py +50 -0
- trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
- trueppm_api/apps/projects/management/commands/load_sample_project.py +247 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +405 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +178 -0
- trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
- trueppm_api/apps/projects/mcp_settings.py +305 -0
- trueppm_api/apps/projects/methodology.py +178 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5595 -0
- trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
- trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
- trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
- trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
- trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
- trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
- trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
- trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
- trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
- trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
- trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
- trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
- trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
- trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
- trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
- trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
- trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
- trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
- trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
- trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
- trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
- trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
- trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
- trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
- trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
- trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
- trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
- trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
- trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
- trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
- trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
- trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
- trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
- trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
- trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
- trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
- trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
- trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
- trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
- trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
- trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
- trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
- trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
- trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
- trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
- trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
- trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
- trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
- trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
- trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
- trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
- trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
- trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
- trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
- trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
- trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
- trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
- trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
- trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
- trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
- trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
- trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
- trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
- trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
- trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
- trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
- trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
- trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
- trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
- trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
- trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
- trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
- trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
- trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
- trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
- trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
- trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
- trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
- trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
- trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
- trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
- trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
- trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
- trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
- trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
- trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
- trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +44 -0
- trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
- trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
- trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
- trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
- trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
- trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
- trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
- trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
- trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +61 -0
- trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
- trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
- trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
- trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +62 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
- trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
- trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
- trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
- trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
- trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
- trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
- trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
- trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
- trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
- trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
- trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
- trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
- trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
- trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
- trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
- trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
- trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
- trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
- trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
- trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
- trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
- trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
- trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
- trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
- trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
- trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
- trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
- trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
- trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
- trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
- trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
- trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
- trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
- trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
- trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
- trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
- trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +8946 -0
- trueppm_api/apps/projects/poker_services.py +154 -0
- trueppm_api/apps/projects/poker_views.py +398 -0
- trueppm_api/apps/projects/product_backlog_services.py +450 -0
- trueppm_api/apps/projects/program_label_services.py +94 -0
- trueppm_api/apps/projects/program_label_views.py +189 -0
- trueppm_api/apps/projects/program_rollup.py +575 -0
- trueppm_api/apps/projects/program_schedule.py +745 -0
- trueppm_api/apps/projects/program_views.py +3220 -0
- trueppm_api/apps/projects/project_templates.py +422 -0
- trueppm_api/apps/projects/receivers.py +234 -0
- trueppm_api/apps/projects/refusal_codes.py +140 -0
- trueppm_api/apps/projects/reorder_services.py +145 -0
- trueppm_api/apps/projects/restructure_hooks.py +54 -0
- trueppm_api/apps/projects/retro_board_services.py +368 -0
- trueppm_api/apps/projects/retro_services.py +285 -0
- trueppm_api/apps/projects/risk_import.py +396 -0
- trueppm_api/apps/projects/row_identity.py +113 -0
- trueppm_api/apps/projects/schema_migrations.py +228 -0
- trueppm_api/apps/projects/schemas/seed_v1.json +493 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +892 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/exporter.py +1644 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2390 -0
- trueppm_api/apps/projects/seed/reanchor.py +551 -0
- trueppm_api/apps/projects/seed/reldates.py +126 -0
- trueppm_api/apps/projects/seed/replace.py +126 -0
- trueppm_api/apps/projects/seed/replay.py +1691 -0
- trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
- trueppm_api/apps/projects/seed/samples.py +427 -0
- trueppm_api/apps/projects/seed/validation.py +1149 -0
- trueppm_api/apps/projects/serializers.py +12310 -0
- trueppm_api/apps/projects/services.py +6966 -0
- trueppm_api/apps/projects/share_serializers.py +132 -0
- trueppm_api/apps/projects/share_services.py +325 -0
- trueppm_api/apps/projects/share_views.py +374 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +909 -0
- trueppm_api/apps/projects/signal_privacy_views.py +476 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +266 -0
- trueppm_api/apps/projects/sprint_cadence.py +453 -0
- trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
- trueppm_api/apps/projects/standup.py +304 -0
- trueppm_api/apps/projects/standup_views.py +99 -0
- trueppm_api/apps/projects/structural_operation_services.py +727 -0
- trueppm_api/apps/projects/structural_operation_views.py +311 -0
- trueppm_api/apps/projects/surface_visibility.py +126 -0
- trueppm_api/apps/projects/task_batch_services.py +379 -0
- trueppm_api/apps/projects/task_bulk.py +1152 -0
- trueppm_api/apps/projects/task_classification.py +561 -0
- trueppm_api/apps/projects/task_classification_defaults.py +95 -0
- trueppm_api/apps/projects/task_duration_settings.py +142 -0
- trueppm_api/apps/projects/task_grouping.py +754 -0
- trueppm_api/apps/projects/tasks.py +1917 -0
- trueppm_api/apps/projects/template_divergence.py +168 -0
- trueppm_api/apps/projects/template_services.py +160 -0
- trueppm_api/apps/projects/template_tasks.py +258 -0
- trueppm_api/apps/projects/template_views.py +799 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1011 -0
- trueppm_api/apps/projects/utilization.py +885 -0
- trueppm_api/apps/projects/views.py +20572 -0
- trueppm_api/apps/projects/wbs_paths.py +119 -0
- trueppm_api/apps/resources/__init__.py +0 -0
- trueppm_api/apps/resources/apps.py +8 -0
- trueppm_api/apps/resources/capacity.py +121 -0
- trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
- trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
- trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
- trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
- trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
- trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
- trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
- trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
- trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
- trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
- trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
- trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
- trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
- trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
- trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
- trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
- trueppm_api/apps/resources/migrations/__init__.py +0 -0
- trueppm_api/apps/resources/models.py +298 -0
- trueppm_api/apps/resources/serializers.py +426 -0
- trueppm_api/apps/resources/services.py +465 -0
- trueppm_api/apps/resources/urls.py +28 -0
- trueppm_api/apps/resources/views.py +1749 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +119 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
- trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
- trueppm_api/apps/scheduling/graph_guard.py +263 -0
- trueppm_api/apps/scheduling/management/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
- trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
- trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
- trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
- trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
- trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
- trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
- trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
- trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
- trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
- trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
- trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
- trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
- trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
- trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
- trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
- trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +516 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +283 -0
- trueppm_api/apps/scheduling/serializers.py +564 -0
- trueppm_api/apps/scheduling/services.py +1182 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +1984 -0
- trueppm_api/apps/scheduling/telemetry.py +80 -0
- trueppm_api/apps/scheduling/units.py +35 -0
- trueppm_api/apps/scheduling/urls.py +57 -0
- trueppm_api/apps/scheduling/views.py +2564 -0
- trueppm_api/apps/sso/__init__.py +0 -0
- trueppm_api/apps/sso/apps.py +19 -0
- trueppm_api/apps/sso/extensions.py +130 -0
- trueppm_api/apps/sso/management/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
- trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
- trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
- trueppm_api/apps/sso/migrations/__init__.py +0 -0
- trueppm_api/apps/sso/models.py +148 -0
- trueppm_api/apps/sso/serializers.py +437 -0
- trueppm_api/apps/sso/services.py +1385 -0
- trueppm_api/apps/sso/urls.py +47 -0
- trueppm_api/apps/sso/views.py +935 -0
- trueppm_api/apps/sync/__init__.py +0 -0
- trueppm_api/apps/sync/apps.py +15 -0
- trueppm_api/apps/sync/backfill.py +208 -0
- trueppm_api/apps/sync/broadcast.py +320 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +471 -0
- trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
- trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
- trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
- trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
- trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
- trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +225 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +371 -0
- trueppm_api/apps/sync/serializers.py +687 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +501 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +930 -0
- trueppm_api/apps/sync/ws_auth.py +193 -0
- trueppm_api/apps/taskruns/__init__.py +0 -0
- trueppm_api/apps/taskruns/apps.py +11 -0
- trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
- trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
- trueppm_api/apps/taskruns/models.py +68 -0
- trueppm_api/apps/taskruns/serializers.py +92 -0
- trueppm_api/apps/taskruns/tasks.py +60 -0
- trueppm_api/apps/taskruns/tracker.py +286 -0
- trueppm_api/apps/taskruns/urls.py +46 -0
- trueppm_api/apps/taskruns/views.py +195 -0
- trueppm_api/apps/teams/__init__.py +8 -0
- trueppm_api/apps/teams/apps.py +16 -0
- trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
- trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +142 -0
- trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
- trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
- trueppm_api/apps/teams/migrations/__init__.py +0 -0
- trueppm_api/apps/teams/models.py +145 -0
- trueppm_api/apps/teams/permissions.py +149 -0
- trueppm_api/apps/teams/serializers.py +95 -0
- trueppm_api/apps/teams/services.py +434 -0
- trueppm_api/apps/teams/signals.py +61 -0
- trueppm_api/apps/teams/urls.py +34 -0
- trueppm_api/apps/teams/views.py +201 -0
- trueppm_api/apps/timetracking/__init__.py +0 -0
- trueppm_api/apps/timetracking/apps.py +9 -0
- trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
- trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
- trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
- trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
- trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
- trueppm_api/apps/timetracking/models.py +178 -0
- trueppm_api/apps/timetracking/serializers.py +175 -0
- trueppm_api/apps/timetracking/services.py +117 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +526 -0
- trueppm_api/apps/webhooks/__init__.py +0 -0
- trueppm_api/apps/webhooks/apps.py +8 -0
- trueppm_api/apps/webhooks/backfill.py +90 -0
- trueppm_api/apps/webhooks/dispatch.py +144 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +243 -0
- trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
- trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
- trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
- trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
- trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
- trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
- trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
- trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +90 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +372 -0
- trueppm_api/apps/webhooks/serializers.py +318 -0
- trueppm_api/apps/webhooks/tasks.py +663 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +383 -0
- trueppm_api/apps/workflow_engine/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/apps.py +23 -0
- trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
- trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
- trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
- trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
- trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/models.py +265 -0
- trueppm_api/apps/workflow_engine/tasks.py +368 -0
- trueppm_api/apps/workspace/__init__.py +0 -0
- trueppm_api/apps/workspace/apps.py +10 -0
- trueppm_api/apps/workspace/backfill.py +111 -0
- trueppm_api/apps/workspace/export.py +242 -0
- trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
- trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
- trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
- trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
- trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
- trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
- trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
- trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
- trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
- trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
- trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
- trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
- trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
- trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
- trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
- trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
- trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
- trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
- trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
- trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
- trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
- trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
- trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
- trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
- trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
- trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
- trueppm_api/apps/workspace/migrations/__init__.py +0 -0
- trueppm_api/apps/workspace/models.py +857 -0
- trueppm_api/apps/workspace/permissions.py +187 -0
- trueppm_api/apps/workspace/serializers.py +590 -0
- trueppm_api/apps/workspace/services.py +919 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +555 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1502 -0
- trueppm_api/asgi.py +42 -0
- trueppm_api/celery.py +31 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +916 -0
- trueppm_api/core/constant_time.py +51 -0
- trueppm_api/core/csp.py +54 -0
- trueppm_api/core/db.py +87 -0
- trueppm_api/core/db_session.py +37 -0
- trueppm_api/core/exception_handlers.py +157 -0
- trueppm_api/core/export_downloads.py +56 -0
- trueppm_api/core/extension_providers.py +56 -0
- trueppm_api/core/extension_signals.py +147 -0
- trueppm_api/core/idempotent.py +260 -0
- trueppm_api/core/metadata.py +42 -0
- trueppm_api/core/middleware.py +51 -0
- trueppm_api/core/openapi.py +949 -0
- trueppm_api/core/password_reset.py +500 -0
- trueppm_api/core/protect_conflict.py +100 -0
- trueppm_api/core/ratelimit.py +148 -0
- trueppm_api/core/redis_throttle.py +82 -0
- trueppm_api/core/refresh_cookie_policy.py +156 -0
- trueppm_api/core/request_body.py +86 -0
- trueppm_api/core/security_checks.py +884 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +344 -0
- trueppm_api/core/valkey.py +214 -0
- trueppm_api/core/valkey_cache.py +66 -0
- trueppm_api/core/valkey_checks.py +104 -0
- trueppm_api/core/valkey_config.py +39 -0
- trueppm_api/core/worker_broker_wait.py +149 -0
- trueppm_api/core/worker_heartbeat.py +134 -0
- trueppm_api/fields.py +36 -0
- trueppm_api/permissions.py +30 -0
- trueppm_api/py.typed +0 -0
- trueppm_api/routing.py +11 -0
- trueppm_api/settings/__init__.py +0 -0
- trueppm_api/settings/base.py +2455 -0
- trueppm_api/settings/dev.py +273 -0
- trueppm_api/settings/prod.py +228 -0
- trueppm_api/urls.py +153 -0
- trueppm_api/workflows/__init__.py +38 -0
- trueppm_api/workflows/backends/__init__.py +6 -0
- trueppm_api/workflows/backends/default.py +297 -0
- trueppm_api/workflows/consumers/__init__.py +11 -0
- trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
- trueppm_api/workflows/interface.py +148 -0
- trueppm_api/workflows/registry.py +106 -0
- trueppm_api/workflows/services.py +90 -0
- trueppm_api/wsgi.py +11 -0
- trueppm_api-0.4.0b2.dist-info/METADATA +70 -0
- trueppm_api-0.4.0b2.dist-info/RECORD +759 -0
- trueppm_api-0.4.0b2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""OSS ``NOTIFICATION_CHANNELS`` registrations (ADR-0049 §1, ADR-0085 §2).
|
|
2
|
+
|
|
3
|
+
Two channels ship in OSS: ``in_app`` (the durable inbox row) and ``email``
|
|
4
|
+
(SMTP, opt-in). The registry's job is to (a) validate the ``channel`` field on
|
|
5
|
+
``NotificationPreference`` rows against a known key set, and (b) be the seam
|
|
6
|
+
Enterprise registers ``slack_dm`` / ``teams_dm`` / ``sms`` against without an
|
|
7
|
+
OSS migration.
|
|
8
|
+
|
|
9
|
+
OSS delivery does NOT go through ``handler.send()`` — fan-out is the bulk
|
|
10
|
+
``notifications/services.py::create_event_notifications`` path (in-app row +
|
|
11
|
+
``email_pending`` flag) drained by ``drain_notification_emails``. The ``send``
|
|
12
|
+
stubs therefore raise ``NotImplementedError``; they exist so the registry has a
|
|
13
|
+
concrete handler per key (mirrors the #637 ``TaskLinkProvider`` stubs). Enterprise
|
|
14
|
+
channels implement a real ``send``.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import Any, ClassVar
|
|
20
|
+
|
|
21
|
+
from .registry import NotificationChannel
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class InAppNotificationChannel(NotificationChannel):
|
|
25
|
+
"""The in-app inbox channel — the durable Notification row."""
|
|
26
|
+
|
|
27
|
+
key: ClassVar[str] = "in_app"
|
|
28
|
+
label: ClassVar[str] = "In-app"
|
|
29
|
+
|
|
30
|
+
def send(self, user: Any, event: Any) -> Any:
|
|
31
|
+
raise NotImplementedError(
|
|
32
|
+
"OSS in-app delivery is the Notification row created by "
|
|
33
|
+
"create_event_notifications; the registry entry is for channel "
|
|
34
|
+
"validation, not a per-event send."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class EmailNotificationChannel(NotificationChannel):
|
|
39
|
+
"""The email channel — SMTP via the existing notification email drain."""
|
|
40
|
+
|
|
41
|
+
key: ClassVar[str] = "email"
|
|
42
|
+
label: ClassVar[str] = "Email"
|
|
43
|
+
|
|
44
|
+
def send(self, user: Any, event: Any) -> Any:
|
|
45
|
+
raise NotImplementedError(
|
|
46
|
+
"OSS email delivery is the email_pending row drained by "
|
|
47
|
+
"drain_notification_emails; the registry entry is for channel "
|
|
48
|
+
"validation, not a per-event send."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Ordered tuple — apps.py iterates these in declaration order so OSS
|
|
53
|
+
# registration order is deterministic for tests + the preferences UI.
|
|
54
|
+
OSS_NOTIFICATION_CHANNELS: tuple[type[NotificationChannel], ...] = (
|
|
55
|
+
InAppNotificationChannel,
|
|
56
|
+
EmailNotificationChannel,
|
|
57
|
+
)
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""OpenGraph / Twitter-Card scraping for cloud-file link previews (#571, ADR-0163).
|
|
2
|
+
|
|
3
|
+
A cloud-file ``TaskLinkProvider`` fetches a pasted file URL through the
|
|
4
|
+
SSRF-guarded :mod:`~trueppm_api.apps.integrations.http` helper and hands the
|
|
5
|
+
(already size-capped, redirect-disabled) response body here. This module turns
|
|
6
|
+
that HTML into a :class:`PreviewData` — the ``{title, description, thumbnail,
|
|
7
|
+
preview_type}`` the preview card renders.
|
|
8
|
+
|
|
9
|
+
Why the standard library and not BeautifulSoup/lxml: the job is "pull a handful
|
|
10
|
+
of ``<meta>`` tags and ``<title>`` out of an already-bounded (256 KB) body."
|
|
11
|
+
``html.parser.HTMLParser`` does that with zero new dependencies (no license/CVE
|
|
12
|
+
audit) and has no entity-expansion ("billion laughs") exposure the way an XML
|
|
13
|
+
parser would. The parser is deliberately lenient — malformed markup yields an
|
|
14
|
+
empty :class:`PreviewData`, never an exception, matching ``fetch_metadata``'s
|
|
15
|
+
"never raise for an unreachable/garbage provider" contract.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from html.parser import HTMLParser
|
|
22
|
+
from urllib.parse import urljoin, urlparse
|
|
23
|
+
|
|
24
|
+
from .registry import (
|
|
25
|
+
PREVIEW_TYPE_DOCUMENT,
|
|
26
|
+
PREVIEW_TYPE_FILE,
|
|
27
|
+
PREVIEW_TYPE_FOLDER,
|
|
28
|
+
PREVIEW_TYPE_IMAGE,
|
|
29
|
+
PREVIEW_TYPE_PDF,
|
|
30
|
+
PREVIEW_TYPE_PRESENTATION,
|
|
31
|
+
PREVIEW_TYPE_SPREADSHEET,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Cap each scraped string so a hostile page can't bloat a synced row. The model
|
|
35
|
+
# columns are 512 (title), 1024 (description), 2048 (thumbnail URL); trimming
|
|
36
|
+
# here keeps the row small and the sync delta cheap even before the DB truncates.
|
|
37
|
+
_MAX_TITLE = 512
|
|
38
|
+
_MAX_DESCRIPTION = 1024
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class PreviewData:
|
|
43
|
+
"""The scraped preview of a file URL.
|
|
44
|
+
|
|
45
|
+
Every field is best-effort: a page with no OpenGraph tags and no ``<title>``
|
|
46
|
+
yields ``PreviewData(None, None, None)`` and the caller still classifies a
|
|
47
|
+
``preview_type`` from the URL shape.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
title: str | None = None
|
|
51
|
+
description: str | None = None
|
|
52
|
+
thumbnail_url: str | None = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class _MetaCollector(HTMLParser):
|
|
56
|
+
"""Collect ``<meta>`` property/name→content pairs and the ``<title>`` text.
|
|
57
|
+
|
|
58
|
+
Only the ``<head>`` carries the tags we want, but we don't hard-stop at
|
|
59
|
+
``</head>`` — some providers emit OpenGraph tags late, and the body is
|
|
60
|
+
already capped at 256 KB upstream, so scanning all of it is bounded work.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self) -> None:
|
|
64
|
+
super().__init__(convert_charrefs=True)
|
|
65
|
+
self.metas: dict[str, str] = {}
|
|
66
|
+
self.title: str | None = None
|
|
67
|
+
self._in_title = False
|
|
68
|
+
|
|
69
|
+
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
|
|
70
|
+
if tag == "title":
|
|
71
|
+
self._in_title = True
|
|
72
|
+
return
|
|
73
|
+
if tag != "meta":
|
|
74
|
+
return
|
|
75
|
+
attr = {k.lower(): (v or "") for k, v in attrs}
|
|
76
|
+
# OpenGraph uses ``property=``; Twitter Cards and the legacy description
|
|
77
|
+
# use ``name=``. Either key is fine — store under whichever is present.
|
|
78
|
+
key = (attr.get("property") or attr.get("name") or "").strip().lower()
|
|
79
|
+
content = attr.get("content")
|
|
80
|
+
if key and content and key not in self.metas:
|
|
81
|
+
# First occurrence wins — a page that repeats og:image keeps the first.
|
|
82
|
+
self.metas[key] = content.strip()
|
|
83
|
+
|
|
84
|
+
def handle_endtag(self, tag: str) -> None:
|
|
85
|
+
if tag == "title":
|
|
86
|
+
self._in_title = False
|
|
87
|
+
|
|
88
|
+
def handle_data(self, data: str) -> None:
|
|
89
|
+
if self._in_title and self.title is None:
|
|
90
|
+
text = data.strip()
|
|
91
|
+
if text:
|
|
92
|
+
self.title = text
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _first(metas: dict[str, str], *keys: str) -> str | None:
|
|
96
|
+
"""Return the first non-empty value among ``keys`` in priority order."""
|
|
97
|
+
for key in keys:
|
|
98
|
+
value = metas.get(key)
|
|
99
|
+
if value:
|
|
100
|
+
return value
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _safe_thumbnail(candidate: str | None, *, base_url: str) -> str | None:
|
|
105
|
+
"""Resolve a candidate image URL against ``base_url`` and keep it only if https.
|
|
106
|
+
|
|
107
|
+
Relative ``og:image`` values are resolved against the page URL. We persist
|
|
108
|
+
**only** ``https://`` thumbnails (ADR-0163): the stored URL is rendered
|
|
109
|
+
client-side as ``<img src>``, so an ``http://`` value would be a mixed-content
|
|
110
|
+
/ downgrade vector and a ``data:``/``javascript:`` value must never reach the
|
|
111
|
+
DOM. Anything that isn't https after resolution is dropped (the card falls
|
|
112
|
+
back to a type glyph).
|
|
113
|
+
"""
|
|
114
|
+
if not candidate:
|
|
115
|
+
return None
|
|
116
|
+
resolved = urljoin(base_url, candidate.strip())
|
|
117
|
+
if urlparse(resolved).scheme != "https":
|
|
118
|
+
return None
|
|
119
|
+
return resolved[:2048]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def parse_preview(body: bytes, *, base_url: str) -> PreviewData:
|
|
123
|
+
"""Scrape ``{title, description, thumbnail_url}`` from an HTML ``body``.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
body: The raw response bytes (already capped at 256 KB by ``http.get``).
|
|
127
|
+
base_url: The fetched URL — used to resolve a relative ``og:image``.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
A :class:`PreviewData`; any field the page doesn't provide is ``None``.
|
|
131
|
+
Never raises — malformed/binary bodies decode with replacement and yield
|
|
132
|
+
an empty result.
|
|
133
|
+
"""
|
|
134
|
+
parser = _MetaCollector()
|
|
135
|
+
try:
|
|
136
|
+
parser.feed(body.decode("utf-8", errors="replace"))
|
|
137
|
+
parser.close()
|
|
138
|
+
except Exception:
|
|
139
|
+
# A broken/hostile page must degrade to an empty preview, never 500 a
|
|
140
|
+
# refresh — fetch_metadata's contract is "never raise for bad input".
|
|
141
|
+
return PreviewData()
|
|
142
|
+
|
|
143
|
+
metas = parser.metas
|
|
144
|
+
title = _first(metas, "og:title", "twitter:title") or parser.title
|
|
145
|
+
description = _first(metas, "og:description", "twitter:description", "description")
|
|
146
|
+
image = _first(metas, "og:image:secure_url", "og:image", "twitter:image")
|
|
147
|
+
|
|
148
|
+
return PreviewData(
|
|
149
|
+
title=title[:_MAX_TITLE].strip() if title else None,
|
|
150
|
+
description=description[:_MAX_DESCRIPTION].strip() if description else None,
|
|
151
|
+
thumbnail_url=_safe_thumbnail(image, base_url=base_url),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# File extensions → preview type. Covers the common office/file shapes that
|
|
156
|
+
# Dropbox/Box/OneDrive expose directly in the URL path.
|
|
157
|
+
_EXT_TYPES: dict[str, str] = {
|
|
158
|
+
".doc": PREVIEW_TYPE_DOCUMENT,
|
|
159
|
+
".docx": PREVIEW_TYPE_DOCUMENT,
|
|
160
|
+
".odt": PREVIEW_TYPE_DOCUMENT,
|
|
161
|
+
".rtf": PREVIEW_TYPE_DOCUMENT,
|
|
162
|
+
".txt": PREVIEW_TYPE_DOCUMENT,
|
|
163
|
+
".md": PREVIEW_TYPE_DOCUMENT,
|
|
164
|
+
".pages": PREVIEW_TYPE_DOCUMENT,
|
|
165
|
+
".xls": PREVIEW_TYPE_SPREADSHEET,
|
|
166
|
+
".xlsx": PREVIEW_TYPE_SPREADSHEET,
|
|
167
|
+
".csv": PREVIEW_TYPE_SPREADSHEET,
|
|
168
|
+
".ods": PREVIEW_TYPE_SPREADSHEET,
|
|
169
|
+
".numbers": PREVIEW_TYPE_SPREADSHEET,
|
|
170
|
+
".ppt": PREVIEW_TYPE_PRESENTATION,
|
|
171
|
+
".pptx": PREVIEW_TYPE_PRESENTATION,
|
|
172
|
+
".odp": PREVIEW_TYPE_PRESENTATION,
|
|
173
|
+
".key": PREVIEW_TYPE_PRESENTATION,
|
|
174
|
+
".pdf": PREVIEW_TYPE_PDF,
|
|
175
|
+
".png": PREVIEW_TYPE_IMAGE,
|
|
176
|
+
".jpg": PREVIEW_TYPE_IMAGE,
|
|
177
|
+
".jpeg": PREVIEW_TYPE_IMAGE,
|
|
178
|
+
".gif": PREVIEW_TYPE_IMAGE,
|
|
179
|
+
".webp": PREVIEW_TYPE_IMAGE,
|
|
180
|
+
".svg": PREVIEW_TYPE_IMAGE,
|
|
181
|
+
".bmp": PREVIEW_TYPE_IMAGE,
|
|
182
|
+
".heic": PREVIEW_TYPE_IMAGE,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
# Google Docs editor path segments → preview type. Drive uses an editor host
|
|
186
|
+
# path (``/spreadsheets/d/…``) rather than a file extension.
|
|
187
|
+
_GOOGLE_PATH_TYPES: tuple[tuple[str, str], ...] = (
|
|
188
|
+
("/spreadsheets/", PREVIEW_TYPE_SPREADSHEET),
|
|
189
|
+
("/presentation/", PREVIEW_TYPE_PRESENTATION),
|
|
190
|
+
("/document/", PREVIEW_TYPE_DOCUMENT),
|
|
191
|
+
("/forms/", PREVIEW_TYPE_DOCUMENT),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def classify_preview_type(url: str, type_hint: str | None = None) -> str:
|
|
196
|
+
"""Classify ``url`` onto one of :data:`PREVIEW_TYPE_VALUES`.
|
|
197
|
+
|
|
198
|
+
Resolution order, most specific first:
|
|
199
|
+
|
|
200
|
+
1. A folder path (``/folders/`` — Drive, ``/sh/`` Dropbox shared folders).
|
|
201
|
+
2. A Google Docs editor path segment (spreadsheets/presentation/document).
|
|
202
|
+
3. A known file extension on the URL path.
|
|
203
|
+
4. A ``type_hint`` of ``image``/``video`` — the caller passes the response
|
|
204
|
+
``Content-Type`` (e.g. ``image/png``) or an OpenGraph ``og:type``; both
|
|
205
|
+
start with the class name, so a direct image URL with no path extension
|
|
206
|
+
still classes as an image.
|
|
207
|
+
5. The generic ``file`` fallback — a real file we just can't class further.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
url: The file URL being classified.
|
|
211
|
+
type_hint: Optional response ``Content-Type`` or OpenGraph ``og:type``.
|
|
212
|
+
"""
|
|
213
|
+
path = urlparse(url).path.lower()
|
|
214
|
+
|
|
215
|
+
if "/folders/" in path or path.endswith("/folder") or "/sh/" in path:
|
|
216
|
+
return PREVIEW_TYPE_FOLDER
|
|
217
|
+
|
|
218
|
+
for segment, ptype in _GOOGLE_PATH_TYPES:
|
|
219
|
+
if segment in path:
|
|
220
|
+
return ptype
|
|
221
|
+
|
|
222
|
+
for ext, ptype in _EXT_TYPES.items():
|
|
223
|
+
if path.endswith(ext):
|
|
224
|
+
return ptype
|
|
225
|
+
|
|
226
|
+
if type_hint and type_hint.lower().startswith("image"):
|
|
227
|
+
return PREVIEW_TYPE_IMAGE
|
|
228
|
+
|
|
229
|
+
return PREVIEW_TYPE_FILE
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""OSS ``OUTGOING_CHANNEL_PROVIDERS`` registrations (ADR-0049 §2, ADR-0083).
|
|
2
|
+
|
|
3
|
+
Two providers ship in OSS:
|
|
4
|
+
|
|
5
|
+
- ``generic`` — returns the event payload unchanged. This is the historical
|
|
6
|
+
behavior (the internal envelope POSTed as the raw body), so every existing
|
|
7
|
+
webhook row — which defaults to ``format="generic"`` — is byte-for-byte
|
|
8
|
+
unaffected by the format extension.
|
|
9
|
+
- ``slack`` — renders a Slack incoming-webhook message (``text`` + a single
|
|
10
|
+
legacy ``attachments`` entry — not Block Kit). Discord and Mattermost incoming
|
|
11
|
+
webhooks accept the Slack attachment shape de-facto, so this one renderer covers
|
|
12
|
+
all three; ``blocks`` would only work on Slack.
|
|
13
|
+
|
|
14
|
+
Enterprise registers richer providers (``slack_app`` OAuth, ``teams``, …)
|
|
15
|
+
against the same registry from its own ``AppConfig.ready()`` — no OSS change.
|
|
16
|
+
The provider only *renders*; ``apps/webhooks/tasks.py::deliver_webhook`` owns
|
|
17
|
+
transport, HMAC signing, retries, and the sequence header (ADR-0083).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import Any, ClassVar
|
|
23
|
+
|
|
24
|
+
from .registry import OutgoingChannelEvent, OutgoingChannelProvider
|
|
25
|
+
|
|
26
|
+
# Slack attachment bar color per event family. Red for destructive/blocking
|
|
27
|
+
# transitions, green for creation/assignment, neutral grey for the rest —
|
|
28
|
+
# matches the design-system semantic palette so the Slack card reads at a glance.
|
|
29
|
+
_SLACK_COLOR = "#1C6B3A" # brand-primary (green)
|
|
30
|
+
_SLACK_COLOR_CRITICAL = "#B91C1C" # semantic-critical (red)
|
|
31
|
+
_SLACK_COLOR_NEUTRAL = "#6B6965" # text-secondary (grey)
|
|
32
|
+
|
|
33
|
+
# Human-readable title + bar color for every OSS event type, plus the reserved
|
|
34
|
+
# ``ping``. Kept as data (not branches) so the slack renderer stays a pure lookup;
|
|
35
|
+
# an unmapped event still renders with a sensible fallback title rather than
|
|
36
|
+
# raising. ``test_slack_meta_covers_every_event`` fails if a new WebhookEventType
|
|
37
|
+
# lands without an entry — the gap this table had for eight real OSS events, which
|
|
38
|
+
# made `risk.opened` post to Slack as `*risk.opened* — <uuid>` with no fields
|
|
39
|
+
# (#2883).
|
|
40
|
+
_SLACK_EVENT_META: dict[str, tuple[str, str]] = {
|
|
41
|
+
"task.created": ("Task created", _SLACK_COLOR),
|
|
42
|
+
"task.updated": ("Task updated", _SLACK_COLOR_NEUTRAL),
|
|
43
|
+
"task.deleted": ("Task deleted", _SLACK_COLOR_CRITICAL),
|
|
44
|
+
"task.assigned": ("Task assigned", _SLACK_COLOR),
|
|
45
|
+
"task.assignee_changed": ("Task reassigned", _SLACK_COLOR_NEUTRAL),
|
|
46
|
+
"task.mentioned": ("You were mentioned", _SLACK_COLOR),
|
|
47
|
+
"task.due_date_changed": ("Task date changed", _SLACK_COLOR_NEUTRAL),
|
|
48
|
+
"dependency.created": ("Dependency added", _SLACK_COLOR_NEUTRAL),
|
|
49
|
+
"dependency.deleted": ("Dependency removed", _SLACK_COLOR_NEUTRAL),
|
|
50
|
+
"schedule.recalculated": ("Schedule recalculated", _SLACK_COLOR_NEUTRAL),
|
|
51
|
+
"project.created": ("Project created", _SLACK_COLOR),
|
|
52
|
+
"sprint.activated": ("Sprint started", _SLACK_COLOR),
|
|
53
|
+
"sprint.closed": ("Sprint closed", _SLACK_COLOR_NEUTRAL),
|
|
54
|
+
"sprint.scope_changed": ("Sprint scope changed", _SLACK_COLOR_CRITICAL),
|
|
55
|
+
"risk.opened": ("Risk opened", _SLACK_COLOR_CRITICAL),
|
|
56
|
+
"risk.escalated": ("Risk escalated", _SLACK_COLOR_CRITICAL),
|
|
57
|
+
"risk.closed": ("Risk closed", _SLACK_COLOR),
|
|
58
|
+
"baseline.captured": ("Baseline captured", _SLACK_COLOR),
|
|
59
|
+
"comment.created": ("New comment", _SLACK_COLOR_NEUTRAL),
|
|
60
|
+
# Reserved, non-domain: the "Send test" ping now renders through this provider
|
|
61
|
+
# like any other delivery (#2884), so it needs a title an admin recognizes in
|
|
62
|
+
# the channel rather than a bare "ping".
|
|
63
|
+
"ping": ("Test ping", _SLACK_COLOR_NEUTRAL),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# The payload key each event family carries its human-readable name under. Task,
|
|
67
|
+
# sprint, and baseline payloads use ``name``; a risk uses ``title``; a sprint
|
|
68
|
+
# scope change is about the injected item, so ``item_name``. ``id`` is the last
|
|
69
|
+
# resort (task.deleted carries only id + project).
|
|
70
|
+
_SLACK_NAME_KEYS: tuple[str, ...] = ("name", "title", "item_name", "id")
|
|
71
|
+
|
|
72
|
+
# Slack attachment field rows per event family — (label, payload key). The
|
|
73
|
+
# extractor read only task keys (name/status/assignee/planned_start) before
|
|
74
|
+
# #2883, which are absent from every sprint, risk, and baseline payload, so those
|
|
75
|
+
# events rendered with an empty ``fields`` array.
|
|
76
|
+
_SLACK_DEFAULT_FIELDS: tuple[tuple[str, str], ...] = (
|
|
77
|
+
("Status", "status"),
|
|
78
|
+
("Assignee", "assignee"),
|
|
79
|
+
("Planned start", "planned_start"),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
_SLACK_FIELDS_BY_FAMILY: dict[str, tuple[tuple[str, str], ...]] = {
|
|
83
|
+
"sprint": (
|
|
84
|
+
("State", "state"),
|
|
85
|
+
("Goal", "goal"),
|
|
86
|
+
("Committed points", "committed_points"),
|
|
87
|
+
("Start", "start_date"),
|
|
88
|
+
("Finish", "finish_date"),
|
|
89
|
+
),
|
|
90
|
+
"risk": (
|
|
91
|
+
("Status", "status"),
|
|
92
|
+
("Severity", "severity"),
|
|
93
|
+
("Probability", "probability"),
|
|
94
|
+
("Impact", "impact"),
|
|
95
|
+
("Category", "category"),
|
|
96
|
+
),
|
|
97
|
+
"baseline": (
|
|
98
|
+
("Tasks captured", "task_count"),
|
|
99
|
+
("Has CPM dates", "has_cpm_dates"),
|
|
100
|
+
),
|
|
101
|
+
"comment": (
|
|
102
|
+
("Author", "author_display"),
|
|
103
|
+
("Status", "status"),
|
|
104
|
+
("Assignee", "assignee"),
|
|
105
|
+
),
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Event-level override, checked before the family table. sprint.scope_changed is
|
|
109
|
+
# in the "sprint" family but its payload is about the injected item, not the
|
|
110
|
+
# sprint's own plan fields.
|
|
111
|
+
_SLACK_FIELDS_BY_EVENT: dict[str, tuple[tuple[str, str], ...]] = {
|
|
112
|
+
"sprint.scope_changed": (
|
|
113
|
+
("Item", "item_name"),
|
|
114
|
+
("Decision", "status"),
|
|
115
|
+
("Goal impact", "goal_impact"),
|
|
116
|
+
),
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _slack_field_specs(event_type: str) -> tuple[tuple[str, str], ...]:
|
|
121
|
+
"""Return the (label, payload key) rows to surface for ``event_type``."""
|
|
122
|
+
if event_type in _SLACK_FIELDS_BY_EVENT:
|
|
123
|
+
return _SLACK_FIELDS_BY_EVENT[event_type]
|
|
124
|
+
family = event_type.split(".", 1)[0]
|
|
125
|
+
return _SLACK_FIELDS_BY_FAMILY.get(family, _SLACK_DEFAULT_FIELDS)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class GenericOutgoingChannelProvider(OutgoingChannelProvider):
|
|
129
|
+
"""Pass-through provider — the raw TruePPM event envelope, unchanged.
|
|
130
|
+
|
|
131
|
+
This preserves the pre-#638 behavior exactly: the payload built by the
|
|
132
|
+
dispatching view is what gets POSTed. Existing webhooks (``format="generic"``
|
|
133
|
+
by default) see no change.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
key: ClassVar[str] = "generic"
|
|
137
|
+
label: ClassVar[str] = "Generic (JSON)"
|
|
138
|
+
|
|
139
|
+
def render(self, event: OutgoingChannelEvent) -> dict[str, Any]:
|
|
140
|
+
return event.payload
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class SlackOutgoingChannelProvider(OutgoingChannelProvider):
|
|
144
|
+
"""Render a TruePPM event as a Slack incoming-webhook message.
|
|
145
|
+
|
|
146
|
+
Produces ``{"text": ..., "attachments": [{...}]}`` — the shape Slack,
|
|
147
|
+
Discord, and Mattermost incoming webhooks all accept. The attachment lists the
|
|
148
|
+
fields present in the payload as Slack ``fields`` so the message is skimmable
|
|
149
|
+
without opening TruePPM. Which fields those are is per event family
|
|
150
|
+
(:func:`_slack_field_specs`), because a sprint, a risk, and a baseline share
|
|
151
|
+
almost no keys with a task.
|
|
152
|
+
|
|
153
|
+
This is the legacy ``attachments`` shape, **not** Block Kit — deliberately, and
|
|
154
|
+
the reason one renderer covers Slack, Discord, and Mattermost (only Slack
|
|
155
|
+
understands ``blocks``). Migrating to Block Kit would drop the other two.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
key: ClassVar[str] = "slack"
|
|
159
|
+
label: ClassVar[str] = "Slack"
|
|
160
|
+
|
|
161
|
+
def render(self, event: OutgoingChannelEvent) -> dict[str, Any]:
|
|
162
|
+
payload = event.payload
|
|
163
|
+
title, color = _SLACK_EVENT_META.get(
|
|
164
|
+
event.event_type, (event.event_type, _SLACK_COLOR_NEUTRAL)
|
|
165
|
+
)
|
|
166
|
+
subject = next(
|
|
167
|
+
(str(payload[key]) for key in _SLACK_NAME_KEYS if payload.get(key)),
|
|
168
|
+
"event",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Only surface fields that are present — task.deleted carries just id +
|
|
172
|
+
# project, so we must not emit empty "Status"/"Assignee" rows for it. The
|
|
173
|
+
# check is against None/"" rather than truthiness so a genuine ``0``
|
|
174
|
+
# (committed_points on an empty sprint) or ``False`` (has_cpm_dates) still
|
|
175
|
+
# renders instead of vanishing.
|
|
176
|
+
fields = [
|
|
177
|
+
{"title": label, "value": str(payload[key]), "short": True}
|
|
178
|
+
for label, key in _slack_field_specs(event.event_type)
|
|
179
|
+
if payload.get(key) not in (None, "")
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
"text": f"*{title}* — {subject}",
|
|
184
|
+
"attachments": [
|
|
185
|
+
{
|
|
186
|
+
"color": color,
|
|
187
|
+
"title": subject,
|
|
188
|
+
"fields": fields,
|
|
189
|
+
"footer": "TruePPM",
|
|
190
|
+
"mrkdwn_in": ["text"],
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# Ordered tuple — apps.py iterates these in declaration order so OSS
|
|
197
|
+
# registration order is deterministic for tests and the format-picker menu.
|
|
198
|
+
OSS_OUTGOING_CHANNEL_PROVIDERS: tuple[type[OutgoingChannelProvider], ...] = (
|
|
199
|
+
GenericOutgoingChannelProvider,
|
|
200
|
+
SlackOutgoingChannelProvider,
|
|
201
|
+
)
|