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,317 @@
|
|
|
1
|
+
"""Unified Assets endpoints — project, program, and workspace scope (ADR-0215; ADR-0428).
|
|
2
|
+
|
|
3
|
+
Read-only aggregation of every task's files (``TaskAttachment``) and external
|
|
4
|
+
links (``TaskLink``) into one paginated, filterable ``AssetItem`` feed:
|
|
5
|
+
|
|
6
|
+
- ``GET /api/v1/projects/{project_pk}/assets/`` — any project member (Viewer+).
|
|
7
|
+
- ``GET /api/v1/programs/{program_pk}/assets/`` — any program member, narrowed to
|
|
8
|
+
the caller's *readable* member projects (the audited ``task_search`` pattern),
|
|
9
|
+
so a program member with no readable child projects gets an empty list, never a
|
|
10
|
+
403 leak.
|
|
11
|
+
- ``GET /api/v1/assets/`` — the workspace tier (ADR-0428): the caller's *own*
|
|
12
|
+
visible-assets collection across **every** project they can read, narrowed by
|
|
13
|
+
the same ``ProjectMembership`` pattern extended from program- to instance-scope.
|
|
14
|
+
Adds a ``mine`` filter (assets on the caller's assigned tasks) and a ``program``
|
|
15
|
+
filter. Strictly RBAC-identical — it returns nothing the caller cannot already
|
|
16
|
+
read per-project, so it is OSS convenience aggregation, **not** portfolio
|
|
17
|
+
governance (no health/comparison analytics, no actor dimension, no audit trail —
|
|
18
|
+
those remain the Enterprise asset register; see ADR-0428).
|
|
19
|
+
|
|
20
|
+
The two nested views are dedicated ``APIView``\\s (not ``@action``\\s) — matching the
|
|
21
|
+
closest precedent, the unified changelog (``history/views.py``): both are read-only
|
|
22
|
+
cross-table keyset merges wired under a ``{scope}_pk`` nested path. The nested-path
|
|
23
|
+
kwarg lets ``IsProjectMember`` / ``IsProgramMember`` gate membership at
|
|
24
|
+
``has_permission`` (a non-member gets 403 before the query runs), and
|
|
25
|
+
``check_object_permissions`` re-enforces it on the resolved object for defense in
|
|
26
|
+
depth. The workspace view has no path pk, so RBAC lives entirely in the readable-
|
|
27
|
+
projects narrowing rather than an object permission.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import uuid
|
|
33
|
+
from typing import Any
|
|
34
|
+
|
|
35
|
+
from django.shortcuts import get_object_or_404
|
|
36
|
+
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
|
|
37
|
+
from rest_framework.exceptions import ValidationError as DRFValidationError
|
|
38
|
+
from rest_framework.permissions import IsAuthenticated
|
|
39
|
+
from rest_framework.request import Request
|
|
40
|
+
from rest_framework.response import Response
|
|
41
|
+
from rest_framework.views import APIView
|
|
42
|
+
|
|
43
|
+
from trueppm_api.apps.access.models import ProjectMembership
|
|
44
|
+
from trueppm_api.apps.access.permissions import (
|
|
45
|
+
IsProgramMember,
|
|
46
|
+
IsProjectMember,
|
|
47
|
+
McpReadableViewMixin,
|
|
48
|
+
McpScope,
|
|
49
|
+
)
|
|
50
|
+
from trueppm_api.apps.projects import asset_feed
|
|
51
|
+
from trueppm_api.apps.projects.asset_feed import AssetFeedResponseSerializer, build_asset_feed
|
|
52
|
+
from trueppm_api.apps.projects.models import Program, Project
|
|
53
|
+
|
|
54
|
+
_ASSET_PARAMS = [
|
|
55
|
+
OpenApiParameter(
|
|
56
|
+
"kind", str, description="Restrict to a single kind: 'file' or 'link'. Default: both."
|
|
57
|
+
),
|
|
58
|
+
OpenApiParameter(
|
|
59
|
+
"label", str, description="Restrict to links carrying this exact label (link-only)."
|
|
60
|
+
),
|
|
61
|
+
OpenApiParameter(
|
|
62
|
+
"provider", str, description="Restrict to links from this provider (link-only)."
|
|
63
|
+
),
|
|
64
|
+
OpenApiParameter(
|
|
65
|
+
"q",
|
|
66
|
+
str,
|
|
67
|
+
description="Case-insensitive substring matched against title/url on both sources.",
|
|
68
|
+
),
|
|
69
|
+
OpenApiParameter(
|
|
70
|
+
"cursor",
|
|
71
|
+
str,
|
|
72
|
+
description=(
|
|
73
|
+
"Opaque keyset cursor from a prior response's next_cursor. Stable across "
|
|
74
|
+
"concurrent writes; persistable; 400 if malformed."
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
OpenApiParameter("page_size", int, description="Page size (1..100, default 50)."),
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _parse_asset_params(request: Request) -> dict[str, Any]:
|
|
82
|
+
"""Validate + normalize the shared Assets query params into feed kwargs."""
|
|
83
|
+
params = request.query_params
|
|
84
|
+
|
|
85
|
+
kind = params.get("kind")
|
|
86
|
+
if kind is not None and kind not in asset_feed.KINDS:
|
|
87
|
+
raise DRFValidationError({"kind": "Must be 'file' or 'link'."})
|
|
88
|
+
|
|
89
|
+
page_size_raw = params.get("page_size")
|
|
90
|
+
if page_size_raw:
|
|
91
|
+
try:
|
|
92
|
+
page_size = int(page_size_raw)
|
|
93
|
+
except (TypeError, ValueError) as err:
|
|
94
|
+
raise DRFValidationError({"page_size": "Must be an integer."}) from err
|
|
95
|
+
else:
|
|
96
|
+
page_size = asset_feed.DEFAULT_PAGE_SIZE
|
|
97
|
+
|
|
98
|
+
cursor_raw = params.get("cursor")
|
|
99
|
+
cursor = asset_feed.AssetCursor.decode(cursor_raw) if cursor_raw else None
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
"kind": kind,
|
|
103
|
+
"label": params.get("label"),
|
|
104
|
+
"provider": params.get("provider"),
|
|
105
|
+
"q": params.get("q"),
|
|
106
|
+
"cursor": cursor,
|
|
107
|
+
"page_size": page_size,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _asset_response(entries: list[dict[str, Any]], next_cursor: Any) -> Response:
|
|
112
|
+
serializer = AssetFeedResponseSerializer(
|
|
113
|
+
{
|
|
114
|
+
"results": entries,
|
|
115
|
+
"next_cursor": next_cursor.encode() if next_cursor is not None else None,
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
return Response(serializer.data)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class ProjectAssetsView(APIView):
|
|
122
|
+
"""Unified Assets feed for a single project (ADR-0215).
|
|
123
|
+
|
|
124
|
+
``GET /api/v1/projects/{project_pk}/assets/?kind=&label=&provider=&q=&cursor=&page_size=``
|
|
125
|
+
|
|
126
|
+
Any project member (Viewer+) may read; a non-member gets 403 (membership is
|
|
127
|
+
checked at ``has_permission`` via the ``project_pk`` kwarg). Archived projects
|
|
128
|
+
stay readable — this is a read-only browse surface (``IsProjectNotArchived`` is
|
|
129
|
+
deliberately omitted, matching the history/changelog reads).
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
permission_classes = [IsAuthenticated, IsProjectMember] # noqa: RUF012
|
|
133
|
+
|
|
134
|
+
@extend_schema(
|
|
135
|
+
summary="Unified Assets feed for a project (files + external links, cursor-paginated)",
|
|
136
|
+
parameters=_ASSET_PARAMS,
|
|
137
|
+
responses={200: OpenApiResponse(AssetFeedResponseSerializer)},
|
|
138
|
+
)
|
|
139
|
+
def get(self, request: Request, project_pk: str) -> Response:
|
|
140
|
+
project = get_object_or_404(Project, pk=project_pk, is_deleted=False)
|
|
141
|
+
self.check_object_permissions(request, project)
|
|
142
|
+
params = _parse_asset_params(request)
|
|
143
|
+
entries, next_cursor = build_asset_feed([project.pk], **params)
|
|
144
|
+
return _asset_response(entries, next_cursor)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ProgramAssetsView(APIView):
|
|
148
|
+
"""Unified Assets feed across a program's member projects (ADR-0215).
|
|
149
|
+
|
|
150
|
+
``GET /api/v1/programs/{program_pk}/assets/?kind=&label=&provider=&q=&cursor=&page_size=``
|
|
151
|
+
|
|
152
|
+
Any program member reaches the endpoint; results are narrowed to the caller's
|
|
153
|
+
**readable** member projects (the audited ``ProgramViewSet.task_search``
|
|
154
|
+
pattern) so no asset from a project the caller cannot read leaks through. A
|
|
155
|
+
program member with no readable child projects gets an empty list, never a
|
|
156
|
+
403. Cross-program / portfolio rollups are Enterprise and out of scope.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
permission_classes = [IsAuthenticated, IsProgramMember] # noqa: RUF012
|
|
160
|
+
|
|
161
|
+
@extend_schema(
|
|
162
|
+
summary="Unified Assets feed for a program (files + external links across member projects)",
|
|
163
|
+
parameters=_ASSET_PARAMS,
|
|
164
|
+
responses={200: OpenApiResponse(AssetFeedResponseSerializer)},
|
|
165
|
+
)
|
|
166
|
+
def get(self, request: Request, program_pk: str) -> Response:
|
|
167
|
+
program = get_object_or_404(Program, pk=program_pk, is_deleted=False)
|
|
168
|
+
self.check_object_permissions(request, program)
|
|
169
|
+
|
|
170
|
+
# Readable member projects only (ADR-0120 D5 / task_search): a single
|
|
171
|
+
# ProjectMembership query resolves the whole program's readable set; a
|
|
172
|
+
# member project the caller cannot read is simply not aggregated.
|
|
173
|
+
#
|
|
174
|
+
# Draft projects are deliberately KEPT (#3144; reasoning recorded in
|
|
175
|
+
# projects.lifecycle). These rows are the member's own browse list, which
|
|
176
|
+
# the exclusion list exempts: the feed emits no count, health band or
|
|
177
|
+
# ranking, and nothing downstream of an asset row moves a schedule. The
|
|
178
|
+
# workspace tier below answers the same, because it is this feed at a wider
|
|
179
|
+
# scope. Note this view is NOT MCP-readable — the MCP carve-out justifies
|
|
180
|
+
# only the workspace tier; the browse-list argument is what carries this one.
|
|
181
|
+
readable_ids = list(
|
|
182
|
+
ProjectMembership.objects.filter(
|
|
183
|
+
project__program=program,
|
|
184
|
+
project__is_deleted=False,
|
|
185
|
+
# IsProgramMember has already established an authenticated user.
|
|
186
|
+
user=request.user, # type: ignore[misc]
|
|
187
|
+
is_deleted=False,
|
|
188
|
+
).values_list("project_id", flat=True)
|
|
189
|
+
)
|
|
190
|
+
params = _parse_asset_params(request)
|
|
191
|
+
entries, next_cursor = build_asset_feed(readable_ids, **params)
|
|
192
|
+
return _asset_response(entries, next_cursor)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
_TRUE_VALUES = frozenset({"true", "1", "yes"})
|
|
196
|
+
_FALSE_VALUES = frozenset({"false", "0", "no", ""})
|
|
197
|
+
|
|
198
|
+
_WORKSPACE_ASSET_PARAMS = [
|
|
199
|
+
*_ASSET_PARAMS,
|
|
200
|
+
OpenApiParameter(
|
|
201
|
+
"mine",
|
|
202
|
+
bool,
|
|
203
|
+
description=(
|
|
204
|
+
"When true, restrict to assets on tasks assigned to the requesting user "
|
|
205
|
+
"(the 'My Assets' view). Scoped to the caller only — there is no way to "
|
|
206
|
+
"query another user's assets. Default: false (all readable assets)."
|
|
207
|
+
),
|
|
208
|
+
),
|
|
209
|
+
OpenApiParameter(
|
|
210
|
+
"program",
|
|
211
|
+
str,
|
|
212
|
+
description=(
|
|
213
|
+
"Restrict to assets in one program's projects (UUID). Still narrowed to "
|
|
214
|
+
"the caller's readable projects, so an unknown or unreadable program "
|
|
215
|
+
"yields an empty page, never a 403."
|
|
216
|
+
),
|
|
217
|
+
),
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _parse_mine(request: Request) -> bool:
|
|
222
|
+
"""Parse the ``mine`` flag; reject anything that is not an explicit boolean."""
|
|
223
|
+
raw = request.query_params.get("mine")
|
|
224
|
+
if raw is None:
|
|
225
|
+
return False
|
|
226
|
+
lowered = raw.strip().lower()
|
|
227
|
+
if lowered in _TRUE_VALUES:
|
|
228
|
+
return True
|
|
229
|
+
if lowered in _FALSE_VALUES:
|
|
230
|
+
return False
|
|
231
|
+
raise DRFValidationError({"mine": "Must be a boolean ('true' or 'false')."})
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _parse_program(request: Request) -> uuid.UUID | None:
|
|
235
|
+
"""Parse the optional ``program`` UUID filter; 400 on a malformed value."""
|
|
236
|
+
raw = request.query_params.get("program")
|
|
237
|
+
if not raw:
|
|
238
|
+
return None
|
|
239
|
+
try:
|
|
240
|
+
return uuid.UUID(raw)
|
|
241
|
+
except (TypeError, ValueError) as err:
|
|
242
|
+
raise DRFValidationError({"program": "Must be a valid UUID."}) from err
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class WorkspaceAssetsView(McpReadableViewMixin, APIView):
|
|
246
|
+
"""Unified Assets feed across every project the caller can read (ADR-0428).
|
|
247
|
+
|
|
248
|
+
``GET /api/v1/assets/?mine=&program=&kind=&label=&provider=&q=&cursor=&page_size=``
|
|
249
|
+
|
|
250
|
+
The workspace tier of the Assets surface. Any authenticated user reaches the
|
|
251
|
+
endpoint; results are narrowed to the caller's **readable** projects via the
|
|
252
|
+
same ``ProjectMembership`` pattern the program view uses, extended from a single
|
|
253
|
+
program to the whole instance. A user with no readable projects gets an empty
|
|
254
|
+
list, never a 403 — and the feed can never surface an asset from a project the
|
|
255
|
+
caller cannot already open, so it grants **no new reach** (this is what keeps it
|
|
256
|
+
OSS rather than portfolio governance; ADR-0428).
|
|
257
|
+
|
|
258
|
+
**RBAC contract (mirrors ``MeWorkView``)**: with ``mine=true`` the feed is
|
|
259
|
+
hard-scoped to ``task.assignee = request.user``; there is no ``?user=`` escape
|
|
260
|
+
hatch, so it can never be used as a cross-team surveillance surface (Morgan's
|
|
261
|
+
boundary). ``McpReadableViewMixin`` additionally exposes the read to ``mcp:read``
|
|
262
|
+
tokens with a per-token throttle (the agent asset-index use case) while leaving
|
|
263
|
+
human traffic on the default ``user`` throttle.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
# ADR-0678 (#2482): workspace-wide asset roll-up; scoped explicitly.
|
|
267
|
+
mcp_scope = McpScope.AGGREGATE
|
|
268
|
+
|
|
269
|
+
permission_classes = [IsAuthenticated] # noqa: RUF012
|
|
270
|
+
|
|
271
|
+
@extend_schema(
|
|
272
|
+
summary="Unified Assets feed across all readable projects (workspace scope)",
|
|
273
|
+
parameters=_WORKSPACE_ASSET_PARAMS,
|
|
274
|
+
responses={200: OpenApiResponse(AssetFeedResponseSerializer)},
|
|
275
|
+
)
|
|
276
|
+
def get(self, request: Request) -> Response:
|
|
277
|
+
mine = _parse_mine(request)
|
|
278
|
+
program_id = _parse_program(request)
|
|
279
|
+
|
|
280
|
+
# Readable projects across the whole instance (the program view's narrowing
|
|
281
|
+
# with the ``project__program=`` clause dropped). ``program`` re-applies a
|
|
282
|
+
# single-program narrowing on top when present. IsAuthenticated guarantees a
|
|
283
|
+
# concrete user; filter on ``user.pk`` so mypy sees a concrete id.
|
|
284
|
+
#
|
|
285
|
+
# Draft projects are deliberately KEPT (#3144), the same answer as the
|
|
286
|
+
# program tier because this is that feed with the program clause dropped.
|
|
287
|
+
# Excluding would create a visibility discontinuity at the commit moment: a
|
|
288
|
+
# file you attached to your own draft vanishes from the surface built to
|
|
289
|
+
# find it and returns when somebody else commits the plan.
|
|
290
|
+
# ``McpScope.AGGREGATE`` is an ADR-0678 token-scope declaration, not a claim
|
|
291
|
+
# that this is a statistical aggregate; the MCP read surface is exempt from
|
|
292
|
+
# the draft exclusion list outright (projects.lifecycle), and an operator
|
|
293
|
+
# who wants a project out of an agent's index uses the opt-out below.
|
|
294
|
+
user_pk = request.user.pk or -1
|
|
295
|
+
memberships = ProjectMembership.objects.filter(
|
|
296
|
+
user_id=user_pk,
|
|
297
|
+
is_deleted=False,
|
|
298
|
+
project__is_deleted=False,
|
|
299
|
+
)
|
|
300
|
+
if program_id is not None:
|
|
301
|
+
memberships = memberships.filter(project__program_id=program_id)
|
|
302
|
+
# ADR-0678 (#2482): McpScope.AGGREGATE — this feed spans every project the
|
|
303
|
+
# caller belongs to and is assembled by hand, so the mixin's row filter
|
|
304
|
+
# cannot reach it. An agent token never sees assets from an opted-out
|
|
305
|
+
# project; a human caller is unaffected (helper returns None).
|
|
306
|
+
from trueppm_api.apps.projects.mcp_settings import mcp_excluded_project_ids
|
|
307
|
+
|
|
308
|
+
mcp_excluded = mcp_excluded_project_ids(request)
|
|
309
|
+
if mcp_excluded is not None:
|
|
310
|
+
memberships = memberships.exclude(project_id__in=mcp_excluded)
|
|
311
|
+
readable_ids = list(memberships.values_list("project_id", flat=True))
|
|
312
|
+
|
|
313
|
+
params = _parse_asset_params(request)
|
|
314
|
+
if mine:
|
|
315
|
+
params["assignee_id"] = user_pk
|
|
316
|
+
entries, next_cursor = build_asset_feed(readable_ids, **params)
|
|
317
|
+
return _asset_response(entries, next_cursor)
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""Workspace → Program → Project attachment-policy resolution (ADR-0153, #976).
|
|
2
|
+
|
|
3
|
+
Two inheritable settings govern task file attachments:
|
|
4
|
+
|
|
5
|
+
* ``attachments_enabled`` — whether file uploads are permitted at all. NULL on a
|
|
6
|
+
program/project means "inherit"; the Workspace root is non-null (default True).
|
|
7
|
+
* ``allowed_attachment_types`` — the per-scope MIME allow-list. On a
|
|
8
|
+
program/project it is tri-state: ``None`` = inherit the parent's effective set,
|
|
9
|
+
``[]`` = an explicit *empty* allow-list (attachments on, but no type permitted),
|
|
10
|
+
``[...]`` = an explicit set. The resolver therefore tests ``is None``, never
|
|
11
|
+
truthiness — an empty list is a deliberate value, not "inherit".
|
|
12
|
+
|
|
13
|
+
Both are resolved computed-on-read (ADR-0108) — there is no denormalized effective
|
|
14
|
+
column to keep in sync. Clients (web, mobile, MCP) read the serializer's
|
|
15
|
+
``effective_*`` / ``inherited_*`` fields and never re-implement this precedence.
|
|
16
|
+
|
|
17
|
+
Precedence: project override → program override → workspace value → (for the
|
|
18
|
+
type-list, the workspace seed, which itself defaults to
|
|
19
|
+
:data:`SYSTEM_DEFAULT_ATTACHMENT_TYPES`).
|
|
20
|
+
|
|
21
|
+
:data:`SYSTEM_ATTACHMENT_DENYLIST` is a non-overridable security floor: types in
|
|
22
|
+
it (``text/html`` etc., active stored-XSS vectors) are subtracted from every
|
|
23
|
+
resolved allow-list, so a workspace admin "widening" the policy can never
|
|
24
|
+
re-admit them, in any edition.
|
|
25
|
+
|
|
26
|
+
``ENFORCE`` (a workspace admin *locking* the policy so lower scopes cannot change
|
|
27
|
+
it) is an Enterprise capability. OSS ships the neutral provider hook below and
|
|
28
|
+
registers nothing, so ``ENFORCE`` degrades to ``SUGGEST`` (no lock). trueppm-
|
|
29
|
+
enterprise registers a provider in its ``AppConfig.ready()`` — the same
|
|
30
|
+
integrations-registry idiom as ``sharing_settings`` (ADR-0029/0030/0135). OSS
|
|
31
|
+
never imports enterprise.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
from collections.abc import Callable, Iterable
|
|
37
|
+
from typing import TYPE_CHECKING
|
|
38
|
+
|
|
39
|
+
from trueppm_api.core.extension_providers import guard_single_provider
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from trueppm_api.apps.projects.models import Program, Project
|
|
43
|
+
from trueppm_api.apps.workspace.models import Workspace
|
|
44
|
+
|
|
45
|
+
#: The system seed allow-list — the floor of the inheritance chain and the
|
|
46
|
+
#: Workspace column default (see ``Workspace._default_allowed_attachment_types``).
|
|
47
|
+
#: This is the single source of truth for the historic hardcoded set; the
|
|
48
|
+
#: attachment serializer aliases it as ``ALLOWED_ATTACHMENT_MIMES`` so the seed
|
|
49
|
+
#: and the live default can never drift.
|
|
50
|
+
SYSTEM_DEFAULT_ATTACHMENT_TYPES: frozenset[str] = frozenset(
|
|
51
|
+
{
|
|
52
|
+
"application/pdf",
|
|
53
|
+
"image/jpeg",
|
|
54
|
+
"image/png",
|
|
55
|
+
"image/webp",
|
|
56
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
57
|
+
"text/csv",
|
|
58
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
#: Non-overridable security floor. These types are subtracted from every resolved
|
|
63
|
+
#: allow-list and rejected by :func:`is_attachment_mime_allowed`, regardless of
|
|
64
|
+
#: scope, policy, or edition — an admin "widen" can never re-admit them. They are
|
|
65
|
+
#: active stored-XSS vectors when served same-origin (markup that can carry
|
|
66
|
+
#: <script>). External *links* are served off-origin and are unaffected.
|
|
67
|
+
SYSTEM_ATTACHMENT_DENYLIST: frozenset[str] = frozenset(
|
|
68
|
+
{
|
|
69
|
+
"text/html",
|
|
70
|
+
"application/xhtml+xml",
|
|
71
|
+
"image/svg+xml",
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
#: The two inheritable policy fields. The field name is identical on Workspace
|
|
76
|
+
#: (non-null root), Program, and Project (nullable override).
|
|
77
|
+
ATTACHMENT_POLICY_FIELDS = ("attachments_enabled", "allowed_attachment_types")
|
|
78
|
+
|
|
79
|
+
# Enterprise registers a zero-arg predicate that returns True when attachment
|
|
80
|
+
# policy enforcement is licensed/active. OSS leaves it None → enforcement
|
|
81
|
+
# inactive, so a workspace ENFORCE policy never locks downstream overrides.
|
|
82
|
+
_ENFORCEMENT_PROVIDER: Callable[[], bool] | None = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def register_attachment_policy_enforcement_provider(
|
|
86
|
+
provider: Callable[[], bool] | None,
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Register (or clear) the enforcement provider. Enterprise calls this."""
|
|
89
|
+
global _ENFORCEMENT_PROVIDER
|
|
90
|
+
_ENFORCEMENT_PROVIDER = guard_single_provider(
|
|
91
|
+
_ENFORCEMENT_PROVIDER, provider, "attachment policy enforcement"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def attachment_policy_enforcement_active() -> bool:
|
|
96
|
+
"""True only when an enterprise provider is registered AND reports active.
|
|
97
|
+
|
|
98
|
+
OSS has no provider → always False, so a workspace ``ENFORCE`` policy never
|
|
99
|
+
locks downstream attachment overrides in the community edition.
|
|
100
|
+
"""
|
|
101
|
+
return _ENFORCEMENT_PROVIDER is not None and bool(_ENFORCEMENT_PROVIDER())
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _enforced(workspace: Workspace) -> bool:
|
|
105
|
+
"""Whether the workspace value is a hard ceiling (Enterprise lock active)."""
|
|
106
|
+
from trueppm_api.apps.workspace.models import TermOverridePolicy
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
workspace.attachments_override_policy == TermOverridePolicy.ENFORCE
|
|
110
|
+
and attachment_policy_enforcement_active()
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _apply_denylist(types: Iterable[str]) -> list[str]:
|
|
115
|
+
"""Subtract the non-overridable denylist and return a sorted, deduped list."""
|
|
116
|
+
return sorted({t for t in types} - SYSTEM_ATTACHMENT_DENYLIST)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class DeniedAttachmentType(ValueError):
|
|
120
|
+
"""Raised by :func:`clean_attachment_type_list` for a security-denied MIME.
|
|
121
|
+
|
|
122
|
+
The offending (normalized) MIME is the first arg, so serializers can surface a
|
|
123
|
+
clear 400 instead of silently storing a value the resolver would strip on read.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def clean_attachment_type_list(value: Iterable[str]) -> list[str]:
|
|
128
|
+
"""Normalize a write-side allow-list: lowercase, strip ``; charset=`` trailers,
|
|
129
|
+
drop blanks, de-duplicate, and return sorted.
|
|
130
|
+
|
|
131
|
+
Rejects any security-denied type by raising :class:`DeniedAttachmentType` — the
|
|
132
|
+
write-side mirror of the read-side denylist floor, so a denied MIME never lands
|
|
133
|
+
in a stored override (which would otherwise vanish on the next resolved read).
|
|
134
|
+
Shared by the Workspace, Program, and Project settings serializers.
|
|
135
|
+
"""
|
|
136
|
+
cleaned: list[str] = []
|
|
137
|
+
for raw in value:
|
|
138
|
+
mime = (raw or "").split(";", 1)[0].strip().lower()
|
|
139
|
+
if not mime:
|
|
140
|
+
continue
|
|
141
|
+
if mime in SYSTEM_ATTACHMENT_DENYLIST:
|
|
142
|
+
raise DeniedAttachmentType(mime)
|
|
143
|
+
if mime not in cleaned:
|
|
144
|
+
cleaned.append(mime)
|
|
145
|
+
return sorted(cleaned)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _program_of(obj: Program | Project) -> Program | None:
|
|
149
|
+
"""The program scope above ``obj`` (a Project's program), or None."""
|
|
150
|
+
from trueppm_api.apps.projects.models import Program
|
|
151
|
+
|
|
152
|
+
if not isinstance(obj, Program) and getattr(obj, "program_id", None):
|
|
153
|
+
return obj.program
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# --- attachments_enabled (boolean) ----------------------------------------
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def resolve_attachments_enabled(
|
|
161
|
+
obj: Program | Project,
|
|
162
|
+
*,
|
|
163
|
+
workspace: Workspace | None = None,
|
|
164
|
+
) -> bool:
|
|
165
|
+
"""Resolve effective ``attachments_enabled`` for a program or project.
|
|
166
|
+
|
|
167
|
+
``workspace`` may be passed to avoid re-loading the singleton per object when
|
|
168
|
+
resolving a list (the serializer caches it once); otherwise it is loaded here.
|
|
169
|
+
"""
|
|
170
|
+
from trueppm_api.apps.workspace.models import Workspace
|
|
171
|
+
|
|
172
|
+
if workspace is None:
|
|
173
|
+
workspace = Workspace.load()
|
|
174
|
+
|
|
175
|
+
if _enforced(workspace):
|
|
176
|
+
return bool(workspace.attachments_enabled)
|
|
177
|
+
|
|
178
|
+
own = obj.attachments_enabled # nullable override
|
|
179
|
+
if own is not None:
|
|
180
|
+
return bool(own)
|
|
181
|
+
return _parent_enabled(obj, workspace)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def resolve_inherited_attachments_enabled(
|
|
185
|
+
obj: Program | Project,
|
|
186
|
+
*,
|
|
187
|
+
workspace: Workspace | None = None,
|
|
188
|
+
) -> bool:
|
|
189
|
+
"""Value ``obj`` would inherit if its own override were cleared.
|
|
190
|
+
|
|
191
|
+
Drives the settings "Inherit (On/Off)" affordance — it deliberately skips
|
|
192
|
+
``obj``'s own override and resolves from the parent up.
|
|
193
|
+
"""
|
|
194
|
+
from trueppm_api.apps.workspace.models import Workspace
|
|
195
|
+
|
|
196
|
+
if workspace is None:
|
|
197
|
+
workspace = Workspace.load()
|
|
198
|
+
|
|
199
|
+
if _enforced(workspace):
|
|
200
|
+
return bool(workspace.attachments_enabled)
|
|
201
|
+
return _parent_enabled(obj, workspace)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _parent_enabled(obj: Program | Project, workspace: Workspace) -> bool:
|
|
205
|
+
program = _program_of(obj)
|
|
206
|
+
if program is not None and program.attachments_enabled is not None:
|
|
207
|
+
return bool(program.attachments_enabled)
|
|
208
|
+
return bool(workspace.attachments_enabled)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# --- allowed_attachment_types (list, tri-state on children) ----------------
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def resolve_effective_attachment_types(
|
|
215
|
+
obj: Program | Project,
|
|
216
|
+
*,
|
|
217
|
+
workspace: Workspace | None = None,
|
|
218
|
+
) -> list[str]:
|
|
219
|
+
"""Resolve the effective allow-list for a program or project (denylist applied).
|
|
220
|
+
|
|
221
|
+
Tri-state on children: ``None`` = inherit, ``[]`` = explicit empty (no type
|
|
222
|
+
allowed), ``[...]`` = explicit set — so the test is ``is not None``, never
|
|
223
|
+
truthiness.
|
|
224
|
+
"""
|
|
225
|
+
from trueppm_api.apps.workspace.models import Workspace
|
|
226
|
+
|
|
227
|
+
if workspace is None:
|
|
228
|
+
workspace = Workspace.load()
|
|
229
|
+
|
|
230
|
+
if _enforced(workspace):
|
|
231
|
+
base: Iterable[str] = workspace.allowed_attachment_types or []
|
|
232
|
+
else:
|
|
233
|
+
# Tri-state: None = inherit, [] = explicit empty, [...] = explicit set —
|
|
234
|
+
# so test ``is not None``, never truthiness ([] is a deliberate value).
|
|
235
|
+
own = obj.allowed_attachment_types
|
|
236
|
+
base = own if own is not None else _parent_types(obj, workspace)
|
|
237
|
+
return _apply_denylist(base)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def resolve_inherited_attachment_types(
|
|
241
|
+
obj: Program | Project,
|
|
242
|
+
*,
|
|
243
|
+
workspace: Workspace | None = None,
|
|
244
|
+
) -> list[str]:
|
|
245
|
+
"""Allow-list ``obj`` would inherit if its own override were cleared."""
|
|
246
|
+
from trueppm_api.apps.workspace.models import Workspace
|
|
247
|
+
|
|
248
|
+
if workspace is None:
|
|
249
|
+
workspace = Workspace.load()
|
|
250
|
+
|
|
251
|
+
if _enforced(workspace):
|
|
252
|
+
return _apply_denylist(workspace.allowed_attachment_types or [])
|
|
253
|
+
return _apply_denylist(_parent_types(obj, workspace))
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _parent_types(obj: Program | Project, workspace: Workspace) -> list[str]:
|
|
257
|
+
program = _program_of(obj)
|
|
258
|
+
if program is not None and program.allowed_attachment_types is not None:
|
|
259
|
+
return list(program.allowed_attachment_types)
|
|
260
|
+
return list(workspace.allowed_attachment_types or [])
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def is_attachment_mime_allowed(
|
|
264
|
+
obj: Program | Project,
|
|
265
|
+
mime: str,
|
|
266
|
+
*,
|
|
267
|
+
workspace: Workspace | None = None,
|
|
268
|
+
) -> bool:
|
|
269
|
+
"""The single enforcement predicate: is ``mime`` permitted for ``obj``?
|
|
270
|
+
|
|
271
|
+
Normalizes the MIME (drops any ``; charset=`` trailer, lowercases), rejects
|
|
272
|
+
anything on the security denylist outright, then checks the resolved
|
|
273
|
+
effective allow-list (which has already had the denylist subtracted).
|
|
274
|
+
"""
|
|
275
|
+
norm = (mime or "").split(";", 1)[0].strip().lower()
|
|
276
|
+
if not norm or norm in SYSTEM_ATTACHMENT_DENYLIST:
|
|
277
|
+
return False
|
|
278
|
+
return norm in resolve_effective_attachment_types(obj, workspace=workspace)
|