trueppm-api 0.4.0__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 +27 -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 +346 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +483 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +184 -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_squashed_0019_release_0_4.py +302 -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 +143 -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/minter_role.py +38 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +3263 -0
- trueppm_api/apps/access/serializers.py +1007 -0
- trueppm_api/apps/access/services.py +878 -0
- trueppm_api/apps/access/signals.py +171 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +96 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1965 -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/0001_squashed_0003_release_0_4.py +344 -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 +109 -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/0001_squashed_0003_release_0_4.py +90 -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 +459 -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 +178 -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 +863 -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 +597 -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_squashed_0014_release_0_4.py +225 -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/0014_tasklink_created_by.py +27 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +635 -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 +528 -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 +734 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1158 -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 +280 -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 +551 -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 +1276 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +439 -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 +462 -0
- trueppm_api/apps/notifications/email_backend.py +350 -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 +299 -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_squashed_0011_release_0_4.py +204 -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 +1148 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +1094 -0
- trueppm_api/apps/notifications/services.py +1489 -0
- trueppm_api/apps/notifications/tasks.py +762 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +919 -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 +566 -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 +993 -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 +537 -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_squashed_0009_release_0_4.py +128 -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 +541 -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 +458 -0
- trueppm_api/apps/projects/backfill.py +459 -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 +212 -0
- trueppm_api/apps/projects/config_notice.py +1330 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +132 -0
- trueppm_api/apps/projects/decisions_views.py +97 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +423 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4278 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2001 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1827 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1909 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1410 -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/key_views.py +468 -0
- trueppm_api/apps/projects/keys.py +205 -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 +514 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +406 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +192 -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/migration_helpers.py +75 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5659 -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 +59 -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 +75 -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 +76 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0106_squashed_0158_release_0_4.py +2357 -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/0154_object_keys.py +184 -0
- trueppm_api/apps/projects/migrations/0155_task_milestone_at_day_end.py +20 -0
- trueppm_api/apps/projects/migrations/0156_program_import_job_replaced_project_ids.py +17 -0
- trueppm_api/apps/projects/migrations/0157_forecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/0158_baselinetask_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +9209 -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 +903 -0
- trueppm_api/apps/projects/program_schedule.py +767 -0
- trueppm_api/apps/projects/program_views.py +3445 -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 +492 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +891 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/demo_landing_overlay.py +532 -0
- trueppm_api/apps/projects/seed/exporter.py +1717 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2764 -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 +128 -0
- trueppm_api/apps/projects/seed/replay.py +1695 -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 +1169 -0
- trueppm_api/apps/projects/serializers.py +12904 -0
- trueppm_api/apps/projects/services.py +7525 -0
- trueppm_api/apps/projects/share_serializers.py +142 -0
- trueppm_api/apps/projects/share_services.py +373 -0
- trueppm_api/apps/projects/share_views.py +395 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +910 -0
- trueppm_api/apps/projects/signal_privacy_views.py +485 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +288 -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 +1244 -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 +1979 -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 +261 -0
- trueppm_api/apps/projects/template_views.py +829 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1016 -0
- trueppm_api/apps/projects/utilization.py +909 -0
- trueppm_api/apps/projects/views.py +21225 -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/0014_squashed_0015_release_0_4.py +47 -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 +1752 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +144 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/finish_reading.py +198 -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/mc_admission.py +181 -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/0010_squashed_0016_release_0_4.py +106 -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/0015_projectforecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/scheduling/migrations/0016_montecarlorun_finish_readings.py +32 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +542 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +334 -0
- trueppm_api/apps/scheduling/serializers.py +723 -0
- trueppm_api/apps/scheduling/services.py +1251 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +2248 -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 +2908 -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 +446 -0
- trueppm_api/apps/sso/services.py +1473 -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 +369 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +548 -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/0003_squashed_0005_release_0_4.py +77 -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/0006_boardevent_per_project_seq.py +77 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +272 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +488 -0
- trueppm_api/apps/sync/serializers.py +688 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +556 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +957 -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 +159 -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/0004_backfill_default_teams_on_upgrade.py +49 -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 +440 -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/0001_squashed_0004_release_0_4.py +167 -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 +211 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +597 -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 +279 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +249 -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 +100 -0
- trueppm_api/apps/webhooks/migrations/0010_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/0011_webhookeditor.py +48 -0
- trueppm_api/apps/webhooks/migrations/0012_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +437 -0
- trueppm_api/apps/webhooks/serializers.py +326 -0
- trueppm_api/apps/webhooks/tasks.py +680 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +533 -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 +371 -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 +254 -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_squashed_0027_release_0_4.py +199 -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 +967 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +616 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1581 -0
- trueppm_api/asgi.py +49 -0
- trueppm_api/celery.py +40 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +934 -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/demo_read_only.py +314 -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 +1221 -0
- trueppm_api/core/password_reset.py +513 -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 +1034 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +514 -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/core/worker_selfheal.py +205 -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 +2605 -0
- trueppm_api/settings/dev.py +296 -0
- trueppm_api/settings/prod.py +265 -0
- trueppm_api/urls.py +217 -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.0.dist-info/METADATA +273 -0
- trueppm_api-0.4.0.dist-info/RECORD +799 -0
- trueppm_api-0.4.0.dist-info/WHEEL +4 -0
- trueppm_api-0.4.0.dist-info/licenses/LICENSE +201 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICE +7 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICES +104 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/GPL-3.0.txt +674 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/LGPL-3.0.txt +165 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/MPL-2.0.txt +373 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Serializers for board share-link management (#283, ADR-0245).
|
|
2
|
+
|
|
3
|
+
The public board snapshot is built as a plain dict by
|
|
4
|
+
``share_services.serialize_public_board`` (a minimized, whitelisted shape), so it
|
|
5
|
+
has no serializer here — these cover only the authenticated management surface.
|
|
6
|
+
``token_hash`` is never exposed; only the non-revealing ``token_prefix`` is.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from datetime import timedelta
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from django.utils import timezone
|
|
15
|
+
from drf_spectacular.utils import extend_schema_field
|
|
16
|
+
from rest_framework import serializers
|
|
17
|
+
|
|
18
|
+
from trueppm_api.apps.projects.models import ShareContentKind, ShareLink
|
|
19
|
+
from trueppm_api.apps.projects.sharing_settings import (
|
|
20
|
+
default_share_link_expiry_days,
|
|
21
|
+
)
|
|
22
|
+
from trueppm_api.apps.workspace.serializers import display_name_for
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ShareLinkSerializer(serializers.ModelSerializer[ShareLink]):
|
|
26
|
+
"""Read serializer for a share link in the management list.
|
|
27
|
+
|
|
28
|
+
Deliberately omits ``token_hash`` (secret) — the raw token is only ever
|
|
29
|
+
returned once, in the create response, via ``ShareLinkCreateResponseSerializer``.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# ``created_by`` is the creator's integer user PK — the same wire meaning it has
|
|
33
|
+
# on every other schema — and ``created_by_name`` carries the display name, per
|
|
34
|
+
# the ``*_name`` convention. Both are null once the creator is deleted
|
|
35
|
+
# (``on_delete=SET_NULL``).
|
|
36
|
+
created_by: serializers.PrimaryKeyRelatedField[Any] = serializers.PrimaryKeyRelatedField(
|
|
37
|
+
read_only=True, allow_null=True
|
|
38
|
+
)
|
|
39
|
+
created_by_name = serializers.SerializerMethodField()
|
|
40
|
+
is_active = serializers.BooleanField(read_only=True)
|
|
41
|
+
|
|
42
|
+
class Meta:
|
|
43
|
+
model = ShareLink
|
|
44
|
+
fields = [ # noqa: RUF012
|
|
45
|
+
"id",
|
|
46
|
+
"content_kind",
|
|
47
|
+
"token_prefix",
|
|
48
|
+
"label",
|
|
49
|
+
"show_assignees",
|
|
50
|
+
"show_milestone_dates",
|
|
51
|
+
"created_by",
|
|
52
|
+
"created_by_name",
|
|
53
|
+
"created_at",
|
|
54
|
+
"expires_at",
|
|
55
|
+
"revoked_at",
|
|
56
|
+
"access_count",
|
|
57
|
+
"last_accessed_at",
|
|
58
|
+
"is_active",
|
|
59
|
+
"is_expired",
|
|
60
|
+
]
|
|
61
|
+
read_only_fields = fields
|
|
62
|
+
|
|
63
|
+
@extend_schema_field(serializers.CharField(allow_null=True))
|
|
64
|
+
def get_created_by_name(self, obj: ShareLink) -> str | None:
|
|
65
|
+
user = obj.created_by
|
|
66
|
+
if user is None:
|
|
67
|
+
return None
|
|
68
|
+
return display_name_for(user.first_name, user.last_name, user.username)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ShareLinkCreateSerializer(serializers.Serializer[Any]):
|
|
72
|
+
"""Write serializer for minting a link. All fields optional; safe defaults."""
|
|
73
|
+
|
|
74
|
+
# ``label`` shadows DRF's ``Field.label`` attribute; the stub flags the
|
|
75
|
+
# assignment though it is valid at runtime (same pattern as observability).
|
|
76
|
+
label = serializers.CharField( # type: ignore[assignment]
|
|
77
|
+
max_length=120, required=False, allow_blank=True, default=""
|
|
78
|
+
)
|
|
79
|
+
show_assignees = serializers.BooleanField(required=False, default=False)
|
|
80
|
+
# Schedule links only (#2532). Defaults TRUE — omitting it must mint exactly the
|
|
81
|
+
# link a pre-#2532 client minted, so this is an opt-out, not an opt-in.
|
|
82
|
+
show_milestone_dates = serializers.BooleanField(required=False, default=True)
|
|
83
|
+
# Which view the link exposes; defaults to board for backward compatibility
|
|
84
|
+
# with pre-#1486 clients that never sent the field.
|
|
85
|
+
content_kind = serializers.ChoiceField(
|
|
86
|
+
choices=ShareContentKind.choices,
|
|
87
|
+
default=ShareContentKind.BOARD,
|
|
88
|
+
)
|
|
89
|
+
# Optional auto-expiry (#1486, #3177). An explicit ``null`` means never expires;
|
|
90
|
+
# OMITTING the field now means "the instance default" (90 days out of the box).
|
|
91
|
+
#
|
|
92
|
+
# No ``default=`` on purpose — that is what makes the two distinguishable. With a
|
|
93
|
+
# default, DRF materializes the key in ``validated_data`` whether the caller sent
|
|
94
|
+
# it or not, and "absent" and "explicit null" collapse into the same value. Absent
|
|
95
|
+
# is filled in ``validate()`` instead.
|
|
96
|
+
expires_at = serializers.DateTimeField(required=False, allow_null=True)
|
|
97
|
+
|
|
98
|
+
def validate_expires_at(self, value: Any) -> Any:
|
|
99
|
+
"""Reject an expiry in the past — a link that is born already-gone is a
|
|
100
|
+
client bug, not a valid state, and would confusingly mint then 410."""
|
|
101
|
+
if value is not None and value <= timezone.now():
|
|
102
|
+
raise serializers.ValidationError("Expiry must be in the future.")
|
|
103
|
+
return value
|
|
104
|
+
|
|
105
|
+
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
|
106
|
+
"""Force ``show_milestone_dates`` back to True on a non-schedule link (#2532).
|
|
107
|
+
|
|
108
|
+
The board projection publishes milestone cards *with their due dates* and has
|
|
109
|
+
no notion of this flag, so persisting a client's ``false`` would store — and
|
|
110
|
+
echo back through the read serializer — a restriction the public board
|
|
111
|
+
endpoint does not apply. Rather than 400 on a field that is merely
|
|
112
|
+
inapplicable, normalize it, so the stored row can never claim a guarantee the
|
|
113
|
+
server is not keeping.
|
|
114
|
+
"""
|
|
115
|
+
if attrs.get("content_kind") != ShareContentKind.SCHEDULE:
|
|
116
|
+
attrs["show_milestone_dates"] = True
|
|
117
|
+
|
|
118
|
+
# Absent (not null) → the instance default window (#3177). The web dialog always
|
|
119
|
+
# sends a value, so this is the path taken by every non-dialog caller: agent
|
|
120
|
+
# tokens, MCP clients, integrators, scripts. Those previously minted a permanent
|
|
121
|
+
# link by saying nothing, which is the one default a sharing feature should not
|
|
122
|
+
# have. An explicit null is left alone and still means never.
|
|
123
|
+
if "expires_at" not in attrs:
|
|
124
|
+
days = default_share_link_expiry_days()
|
|
125
|
+
attrs["expires_at"] = timezone.now() + timedelta(days=days) if days > 0 else None
|
|
126
|
+
return attrs
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class ShareLinkCreateResponseSerializer(ShareLinkSerializer):
|
|
130
|
+
"""Create response: the full row plus the one-time raw token and its relative path.
|
|
131
|
+
|
|
132
|
+
``token`` and ``share_path`` are present ONLY on this response and never again —
|
|
133
|
+
the web client composes the absolute URL from ``window.location.origin`` so the
|
|
134
|
+
API stays decoupled from the frontend origin.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
token = serializers.CharField(read_only=True)
|
|
138
|
+
share_path = serializers.CharField(read_only=True)
|
|
139
|
+
|
|
140
|
+
class Meta(ShareLinkSerializer.Meta):
|
|
141
|
+
fields = [*ShareLinkSerializer.Meta.fields, "token", "share_path"] # noqa: RUF012
|
|
142
|
+
read_only_fields = fields
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"""Service layer for public read-only board share links (#283, ADR-0245).
|
|
2
|
+
|
|
3
|
+
Keeps token hashing, minimization, and access metering in one auditable place so
|
|
4
|
+
the views stay thin. Token handling mirrors the ``ApiToken`` / ``WorkspaceInvite``
|
|
5
|
+
lineage: the raw token is returned once at mint and only its SHA-256 hash is
|
|
6
|
+
persisted; lookup is O(1) on the unique hash index.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import secrets
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
from typing import Any, cast
|
|
15
|
+
|
|
16
|
+
from django.conf import settings
|
|
17
|
+
from django.core.cache import cache
|
|
18
|
+
from django.db.models import F
|
|
19
|
+
from django.utils import timezone
|
|
20
|
+
|
|
21
|
+
from trueppm_api.apps.projects.authentication import sha256_hex
|
|
22
|
+
from trueppm_api.apps.projects.models import (
|
|
23
|
+
BoardColumnConfig,
|
|
24
|
+
Dependency,
|
|
25
|
+
ShareContentKind,
|
|
26
|
+
ShareLink,
|
|
27
|
+
Task,
|
|
28
|
+
TaskStatus,
|
|
29
|
+
)
|
|
30
|
+
from trueppm_api.apps.workspace.serializers import display_name_for
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
# The board columns shown publicly when a project has no custom BoardColumnConfig:
|
|
35
|
+
# the standard non-backlog workflow lane, in order. Backlog is deliberately never
|
|
36
|
+
# published (ADR-0245) — a shared board is a delivery-status surface, not the intake
|
|
37
|
+
# pool, and unrefined backlog items are the most likely to carry sensitive names.
|
|
38
|
+
_PUBLIC_DEFAULT_STATUSES = (
|
|
39
|
+
TaskStatus.NOT_STARTED,
|
|
40
|
+
TaskStatus.IN_PROGRESS,
|
|
41
|
+
TaskStatus.REVIEW,
|
|
42
|
+
TaskStatus.COMPLETE,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def mint_share_link(
|
|
47
|
+
project: Any,
|
|
48
|
+
user: Any,
|
|
49
|
+
*,
|
|
50
|
+
label: str = "",
|
|
51
|
+
show_assignees: bool = False,
|
|
52
|
+
show_milestone_dates: bool = True,
|
|
53
|
+
content_kind: str = ShareContentKind.BOARD,
|
|
54
|
+
expires_at: Any = None,
|
|
55
|
+
) -> tuple[ShareLink, str]:
|
|
56
|
+
"""Create a share link and return ``(link, raw_token)``.
|
|
57
|
+
|
|
58
|
+
The raw token is non-enumerable (``secrets.token_urlsafe(32)`` ≈ 256 bits) and
|
|
59
|
+
is returned exactly once — only its hash is stored, so it can never be
|
|
60
|
+
retrieved again. ``expires_at`` (optional, #1486) auto-expires the link — after
|
|
61
|
+
it passes, the public endpoint returns 410 exactly like a revoked link.
|
|
62
|
+
``show_milestone_dates`` (#2532, schedule links only) defaults **True** so a
|
|
63
|
+
caller that omits it mints exactly the link it minted before the flag existed.
|
|
64
|
+
"""
|
|
65
|
+
raw = secrets.token_urlsafe(32)
|
|
66
|
+
link = ShareLink.objects.create(
|
|
67
|
+
project=project,
|
|
68
|
+
content_kind=content_kind,
|
|
69
|
+
token_prefix=raw[:12],
|
|
70
|
+
token_hash=sha256_hex(raw),
|
|
71
|
+
label=label,
|
|
72
|
+
show_assignees=show_assignees,
|
|
73
|
+
show_milestone_dates=show_milestone_dates,
|
|
74
|
+
expires_at=expires_at,
|
|
75
|
+
created_by=user if getattr(user, "is_authenticated", False) else None,
|
|
76
|
+
)
|
|
77
|
+
logger.info(
|
|
78
|
+
"share_link.minted id=%s project=%s kind=%s by=%s",
|
|
79
|
+
link.id,
|
|
80
|
+
project.id,
|
|
81
|
+
content_kind,
|
|
82
|
+
getattr(user, "id", None),
|
|
83
|
+
)
|
|
84
|
+
return link, raw
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def revoke_share_link(link: ShareLink, user: Any) -> None:
|
|
88
|
+
"""Soft-revoke a link. Idempotent — re-revoking an already-revoked link is a no-op."""
|
|
89
|
+
if link.revoked_at is not None:
|
|
90
|
+
return
|
|
91
|
+
link.revoked_at = timezone.now()
|
|
92
|
+
link.revoked_by = user if getattr(user, "is_authenticated", False) else None
|
|
93
|
+
link.save(update_fields=["revoked_at", "revoked_by"])
|
|
94
|
+
logger.info(
|
|
95
|
+
"share_link.revoked id=%s project=%s by=%s",
|
|
96
|
+
link.id,
|
|
97
|
+
link.project_id,
|
|
98
|
+
getattr(user, "id", None),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def resolve_share_link(token: str, content_kind: str) -> ShareLink | None:
|
|
103
|
+
"""Return the ``ShareLink`` for a raw token (active *or* revoked), or ``None``.
|
|
104
|
+
|
|
105
|
+
Looks up by SHA-256 hash — O(1) on the unique index, no per-row compare, so
|
|
106
|
+
there is no timing-oracle to enumerate against. Filtered on ``content_kind`` so
|
|
107
|
+
a board token can never resolve a schedule view (forward-compat for #1486). The
|
|
108
|
+
caller distinguishes revoked (→ 410) from unknown (``None`` → 404).
|
|
109
|
+
"""
|
|
110
|
+
if not token:
|
|
111
|
+
return None
|
|
112
|
+
return (
|
|
113
|
+
ShareLink.objects.filter(token_hash=sha256_hex(token), content_kind=content_kind)
|
|
114
|
+
.select_related("project", "created_by")
|
|
115
|
+
.first()
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def record_access(link: ShareLink) -> None:
|
|
120
|
+
"""Bump the access meter with a single atomic UPDATE (no ``save()``, no history).
|
|
121
|
+
|
|
122
|
+
Mirrors ``ApiToken.last_used_at``. An ``F()`` increment is retry-safe; a rare
|
|
123
|
+
double-count on a client retry is acceptable for a view meter.
|
|
124
|
+
"""
|
|
125
|
+
ShareLink.objects.filter(pk=link.pk).update(
|
|
126
|
+
access_count=F("access_count") + 1,
|
|
127
|
+
last_accessed_at=timezone.now(),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# TTL matches the client-facing `Cache-Control: private, max-age=30` in
|
|
132
|
+
# share_views.py (#4226) — a server-side cache hit is never staler than what a
|
|
133
|
+
# browser already tolerates on a re-poll. Short on purpose: the projection changes
|
|
134
|
+
# at most once a day (the demo reset job, or a real workspace's own edits), so a
|
|
135
|
+
# bounded TTL is enough to collapse a burst of concurrent visitors on the same link
|
|
136
|
+
# into one DB round trip without needing explicit invalidation on every write that
|
|
137
|
+
# could touch the payload.
|
|
138
|
+
SHARE_PAYLOAD_CACHE_TTL = 30
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _share_payload_cache_key(link: ShareLink) -> str:
|
|
142
|
+
"""Cache key for one link's built projection.
|
|
143
|
+
|
|
144
|
+
Keyed on pk, not token: the token is only ever hashed on the way in
|
|
145
|
+
(``resolve_share_link``), and by the time this key is built the caller already
|
|
146
|
+
holds the resolved row. ``content_kind`` is folded in even though a pk alone is
|
|
147
|
+
unique, purely so the key is self-describing in a cache dump.
|
|
148
|
+
"""
|
|
149
|
+
return f"share-payload:{link.content_kind}:{link.pk}"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def get_cached_public_payload(
|
|
153
|
+
link: ShareLink, serialize: Callable[[ShareLink], dict[str, Any]]
|
|
154
|
+
) -> dict[str, Any]:
|
|
155
|
+
"""Serve ``serialize(link)`` from Valkey when a recent build exists, else build and cache it.
|
|
156
|
+
|
|
157
|
+
Collapses concurrent visitors on the same share link into one DB-hitting build
|
|
158
|
+
per TTL window instead of one per request (#4226) — the projection queries
|
|
159
|
+
(``serialize_public_board`` / ``serialize_public_schedule``) previously ran on
|
|
160
|
+
every single request with no server-side cache in front of them, despite Valkey
|
|
161
|
+
already being deployed for every instance that serves this view.
|
|
162
|
+
"""
|
|
163
|
+
key = _share_payload_cache_key(link)
|
|
164
|
+
payload = cache.get(key)
|
|
165
|
+
if payload is None:
|
|
166
|
+
payload = serialize(link)
|
|
167
|
+
cache.set(key, payload, timeout=SHARE_PAYLOAD_CACHE_TTL)
|
|
168
|
+
return payload
|
|
169
|
+
# cache.get()'s return type is untyped (Any) — cast rather than trust it, since
|
|
170
|
+
# this branch returns whatever a previous call stored under this key.
|
|
171
|
+
return cast("dict[str, Any]", payload)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _public_columns(project: Any) -> list[dict[str, str]]:
|
|
175
|
+
"""Ordered ``[{key, label}]`` columns to publish (config order, backlog excluded)."""
|
|
176
|
+
try:
|
|
177
|
+
config = BoardColumnConfig.objects.get(project=project)
|
|
178
|
+
cols = config.columns or None
|
|
179
|
+
except BoardColumnConfig.DoesNotExist:
|
|
180
|
+
cols = None
|
|
181
|
+
if cols:
|
|
182
|
+
return [
|
|
183
|
+
{"key": c["status"], "label": c.get("label") or c["status"].replace("_", " ").title()}
|
|
184
|
+
for c in cols
|
|
185
|
+
if c.get("visible", True) and c["status"] != TaskStatus.BACKLOG.value
|
|
186
|
+
]
|
|
187
|
+
return [{"key": s.value, "label": s.label} for s in _PUBLIC_DEFAULT_STATUSES]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _public_card(task: Task, show_assignees: bool) -> dict[str, Any]:
|
|
191
|
+
"""Minimized, whitelisted card. Comments/notes/attachments/points are never emitted."""
|
|
192
|
+
due = task.early_finish or task.planned_start
|
|
193
|
+
card: dict[str, Any] = {
|
|
194
|
+
"short_id": task.short_id,
|
|
195
|
+
"name": task.name,
|
|
196
|
+
"status": task.status,
|
|
197
|
+
"is_milestone": task.is_milestone,
|
|
198
|
+
"percent_complete": task.percent_complete,
|
|
199
|
+
"due_date": due.isoformat() if due else None,
|
|
200
|
+
"assignee": None,
|
|
201
|
+
}
|
|
202
|
+
# Assignee identity is opt-in only (ADR-0245): a display name, never email/id/avatar.
|
|
203
|
+
assignee = task.assignee
|
|
204
|
+
if show_assignees and assignee is not None:
|
|
205
|
+
card["assignee"] = display_name_for(
|
|
206
|
+
assignee.first_name, assignee.last_name, assignee.username
|
|
207
|
+
)
|
|
208
|
+
return card
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def serialize_public_board(link: ShareLink) -> dict[str, Any]:
|
|
212
|
+
"""Build the minimized, bounded public board snapshot for ``link``.
|
|
213
|
+
|
|
214
|
+
Excludes soft-deleted tasks and the backlog column; caps the payload at
|
|
215
|
+
``SHARE_BOARD_MAX_CARDS`` and flags ``truncated`` rather than silently dropping
|
|
216
|
+
cards.
|
|
217
|
+
"""
|
|
218
|
+
project = link.project
|
|
219
|
+
columns = _public_columns(project)
|
|
220
|
+
visible_statuses = [c["key"] for c in columns]
|
|
221
|
+
cap = int(getattr(settings, "SHARE_BOARD_MAX_CARDS", 1000))
|
|
222
|
+
|
|
223
|
+
qs = (
|
|
224
|
+
Task.objects.filter(
|
|
225
|
+
project=project,
|
|
226
|
+
is_deleted=False,
|
|
227
|
+
status__in=visible_statuses,
|
|
228
|
+
)
|
|
229
|
+
.select_related("assignee")
|
|
230
|
+
.order_by("status", "priority_rank", "name")
|
|
231
|
+
)
|
|
232
|
+
rows = list(qs[: cap + 1])
|
|
233
|
+
truncated = len(rows) > cap
|
|
234
|
+
rows = rows[:cap]
|
|
235
|
+
|
|
236
|
+
by_status: dict[str, list[dict[str, Any]]] = {c["key"]: [] for c in columns}
|
|
237
|
+
for task in rows:
|
|
238
|
+
by_status.setdefault(task.status, []).append(_public_card(task, link.show_assignees))
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
"content_kind": link.content_kind,
|
|
242
|
+
"project": {"name": project.name, "short_id": project.code or ""},
|
|
243
|
+
"columns": [
|
|
244
|
+
{"key": c["key"], "label": c["label"], "cards": by_status.get(c["key"], [])}
|
|
245
|
+
for c in columns
|
|
246
|
+
],
|
|
247
|
+
"show_assignees": link.show_assignees,
|
|
248
|
+
"truncated": truncated,
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _public_schedule_task(task: Task, show_assignees: bool) -> dict[str, Any]:
|
|
253
|
+
"""Minimized, whitelisted schedule row for the public timeline (#1486, ADR-0265).
|
|
254
|
+
|
|
255
|
+
Emits ONLY what a read-only Gantt needs to draw bars, structure, and the
|
|
256
|
+
critical path. Internal slack intelligence (``late_start``/``late_finish``/
|
|
257
|
+
``total_float``/``free_float``), PERT/Monte Carlo durations and percentiles,
|
|
258
|
+
baseline overlay, cost, resource units, priority, and blocker fields are
|
|
259
|
+
excluded by omission — criticality is surfaced via the boolean ``is_critical``
|
|
260
|
+
flag, never the raw float. Assignee identity is opt-in only (a display name,
|
|
261
|
+
never email/id/avatar), matching the board projection.
|
|
262
|
+
|
|
263
|
+
``scheduled_start`` (ADR-0752) is a **deliberate** widening of this
|
|
264
|
+
allow-list, not an incidental one: ``actual_start``/``actual_finish`` stay
|
|
265
|
+
withheld, but for an in-progress task ``scheduled_start`` *is*
|
|
266
|
+
``actual_start`` (the span starts where work actually began). The public
|
|
267
|
+
Gantt needs the real span to draw a correct bar — an
|
|
268
|
+
ignore-``actual_start``, back-off-only span would silently draw a
|
|
269
|
+
*different* schedule than the authenticated product on the one surface
|
|
270
|
+
where trust matters most (ADR-0752 §8) — so the widening is accepted and
|
|
271
|
+
recorded here rather than left for a future reader to discover by diffing
|
|
272
|
+
field names. ``when work actually began`` is the same class of
|
|
273
|
+
delivery-status fact this projection already discloses via
|
|
274
|
+
``percent_complete`` and ``planned_start``.
|
|
275
|
+
"""
|
|
276
|
+
row: dict[str, Any] = {
|
|
277
|
+
"short_id": task.short_id,
|
|
278
|
+
"name": task.name,
|
|
279
|
+
# ltree value coerces to str ("1.2.3") for indent / summary structure.
|
|
280
|
+
"wbs_path": str(task.wbs_path) if task.wbs_path else "",
|
|
281
|
+
"duration": task.duration,
|
|
282
|
+
"planned_start": task.planned_start.isoformat() if task.planned_start else None,
|
|
283
|
+
"early_start": task.early_start.isoformat() if task.early_start else None,
|
|
284
|
+
"early_finish": task.early_finish.isoformat() if task.early_finish else None,
|
|
285
|
+
"scheduled_start": task.scheduled_start.isoformat() if task.scheduled_start else None,
|
|
286
|
+
"is_milestone": task.is_milestone,
|
|
287
|
+
# #4079: which edge of its day a milestone's diamond sits on. A pure
|
|
288
|
+
# rendering fact derived from the same dates already disclosed here.
|
|
289
|
+
"milestone_at_day_end": task.milestone_at_day_end,
|
|
290
|
+
"is_critical": bool(task.is_critical),
|
|
291
|
+
"percent_complete": task.percent_complete,
|
|
292
|
+
"status": task.status,
|
|
293
|
+
"assignee": None,
|
|
294
|
+
}
|
|
295
|
+
assignee = task.assignee
|
|
296
|
+
if show_assignees and assignee is not None:
|
|
297
|
+
row["assignee"] = display_name_for(
|
|
298
|
+
assignee.first_name, assignee.last_name, assignee.username
|
|
299
|
+
)
|
|
300
|
+
return row
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def serialize_public_schedule(link: ShareLink) -> dict[str, Any]:
|
|
304
|
+
"""Build the minimized, bounded public schedule snapshot for ``link`` (#1486).
|
|
305
|
+
|
|
306
|
+
Mirrors :func:`serialize_public_board`: excludes soft-deleted tasks, caps the
|
|
307
|
+
payload at ``SHARE_SCHEDULE_MAX_TASKS`` (flagging ``truncated`` rather than
|
|
308
|
+
silently dropping), and emits dependency edges by ``short_id`` (never task
|
|
309
|
+
UUIDs) so the read-only renderer can draw links. All internal risk/float
|
|
310
|
+
intelligence is withheld — see :func:`_public_schedule_task`.
|
|
311
|
+
"""
|
|
312
|
+
project = link.project
|
|
313
|
+
cap = int(getattr(settings, "SHARE_SCHEDULE_MAX_TASKS", 1000))
|
|
314
|
+
|
|
315
|
+
# Exclude the backlog exactly as the board projection does (ADR-0245): the
|
|
316
|
+
# intake pool is un-triaged and the most likely to carry sensitive raw names, so
|
|
317
|
+
# a shared schedule is a delivery-status surface, not the backlog. Backlog tasks
|
|
318
|
+
# are unscheduled anyway and would only render as "Unscheduled" noise.
|
|
319
|
+
qs = (
|
|
320
|
+
Task.objects.filter(project=project, is_deleted=False)
|
|
321
|
+
.exclude(status=TaskStatus.BACKLOG)
|
|
322
|
+
.select_related("assignee")
|
|
323
|
+
.order_by("wbs_path", "early_start", "name")
|
|
324
|
+
)
|
|
325
|
+
if not link.show_milestone_dates:
|
|
326
|
+
# Withheld in SQL, not filtered out of the page in Python (#2532): the row
|
|
327
|
+
# must never reach the response at all — the public renderer draws the
|
|
328
|
+
# diamond and its dated label straight from the payload — and excluding
|
|
329
|
+
# before the cap means hidden milestones can't consume the task budget and
|
|
330
|
+
# truncate real work away. Dropping the rows also drops their dependency
|
|
331
|
+
# edges below, since edges are bound to the published id set.
|
|
332
|
+
qs = qs.exclude(is_milestone=True)
|
|
333
|
+
rows = list(qs[: cap + 1])
|
|
334
|
+
truncated = len(rows) > cap
|
|
335
|
+
rows = rows[:cap]
|
|
336
|
+
|
|
337
|
+
published_ids = {task.id for task in rows}
|
|
338
|
+
tasks = [_public_schedule_task(task, link.show_assignees) for task in rows]
|
|
339
|
+
|
|
340
|
+
# One query for edges, both endpoints among the published (non-deleted) tasks,
|
|
341
|
+
# then map ids → short_ids in Python — no per-task/per-edge N+1. Edges to a
|
|
342
|
+
# task that was truncated away or soft-deleted are dropped so no dangling arrow
|
|
343
|
+
# is ever emitted.
|
|
344
|
+
id_to_short = {task.id: task.short_id for task in rows}
|
|
345
|
+
dependencies: list[dict[str, Any]] = []
|
|
346
|
+
# Bound the edge query to the published (capped, non-deleted) task set in SQL, so
|
|
347
|
+
# a project with far more edges than the task cap can't return rows we'd only
|
|
348
|
+
# discard in Python. The membership re-check below is then a no-op safety net.
|
|
349
|
+
edge_qs = Dependency.objects.filter(
|
|
350
|
+
is_deleted=False,
|
|
351
|
+
predecessor_id__in=published_ids,
|
|
352
|
+
successor_id__in=published_ids,
|
|
353
|
+
).values_list("predecessor_id", "successor_id", "dep_type", "lag")
|
|
354
|
+
for pred_id, succ_id, dep_type, lag in edge_qs:
|
|
355
|
+
if pred_id in published_ids and succ_id in published_ids:
|
|
356
|
+
dependencies.append(
|
|
357
|
+
{
|
|
358
|
+
"predecessor_short_id": id_to_short[pred_id],
|
|
359
|
+
"successor_short_id": id_to_short[succ_id],
|
|
360
|
+
"dep_type": dep_type,
|
|
361
|
+
"lag": lag,
|
|
362
|
+
}
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
"content_kind": link.content_kind,
|
|
367
|
+
"project": {"name": project.name, "short_id": project.code or ""},
|
|
368
|
+
"tasks": tasks,
|
|
369
|
+
"dependencies": dependencies,
|
|
370
|
+
"show_assignees": link.show_assignees,
|
|
371
|
+
"show_milestone_dates": link.show_milestone_dates,
|
|
372
|
+
"truncated": truncated,
|
|
373
|
+
}
|