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,465 @@
|
|
|
1
|
+
"""Service-layer helpers for the resources app."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from typing import TYPE_CHECKING, Any, NamedTuple
|
|
7
|
+
|
|
8
|
+
from trueppm_api.apps.projects.models import Project, TaskActivityEvent, TaskActivityEventType
|
|
9
|
+
from trueppm_api.apps.resources.models import ProjectResource, Resource, TaskResource
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from trueppm_api.apps.projects.models import Task
|
|
13
|
+
|
|
14
|
+
#: Allocation bounds shared with ``TaskResourceSerializer.validate_units``. 1% is the
|
|
15
|
+
#: smallest meaningful assignment; 200% catches a data-entry slip while still allowing
|
|
16
|
+
#: deliberate overtime.
|
|
17
|
+
MIN_ASSIGNMENT_UNITS = Decimal("0.01")
|
|
18
|
+
MAX_ASSIGNMENT_UNITS = Decimal("2.0")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def ensure_project_resource(project: Project, resource: Resource) -> ProjectResource:
|
|
22
|
+
"""Idempotently add a resource to a project's roster.
|
|
23
|
+
|
|
24
|
+
Centralises the auto-roster pattern: any code path that creates a
|
|
25
|
+
task–resource assignment (TaskResource) must call this so the resource
|
|
26
|
+
appears in Team → Roster, Allocation, and Heatmap views (#241).
|
|
27
|
+
|
|
28
|
+
Returns the existing ``ProjectResource`` row when one is already present
|
|
29
|
+
(whether live or soft-deleted), otherwise creates a new live row. Soft-
|
|
30
|
+
deleted rows are intentionally left alone — the soft-delete is a deliberate
|
|
31
|
+
PM action and reactivation belongs to the explicit roster UI, not to the
|
|
32
|
+
side-effect of a task assignment.
|
|
33
|
+
"""
|
|
34
|
+
obj, _ = ProjectResource.objects.get_or_create(
|
|
35
|
+
project=project,
|
|
36
|
+
resource=resource,
|
|
37
|
+
)
|
|
38
|
+
return obj
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def ensure_project_resources(project_id: Any, resource_ids: list[Any]) -> None:
|
|
42
|
+
"""Batched :func:`ensure_project_resource` — two statements for any number of ids.
|
|
43
|
+
|
|
44
|
+
Same contract as the single-row helper: a resource already on the roster is left
|
|
45
|
+
exactly as it is (live *or* soft-deleted — reactivation belongs to the explicit
|
|
46
|
+
roster UI, never to the side effect of a task assignment), and only a resource with
|
|
47
|
+
no row at all gets one.
|
|
48
|
+
|
|
49
|
+
Two details are load-bearing and easy to lose when converting a ``get_or_create``
|
|
50
|
+
loop to ``bulk_create``:
|
|
51
|
+
|
|
52
|
+
* ``ProjectResource`` is a ``VersionedModel``, and ``bulk_create`` does **not** call
|
|
53
|
+
``Model.save()`` — so the INSERT-path ``server_version = 1`` that
|
|
54
|
+
:meth:`VersionedModel.save` would set has to be written here explicitly. A row
|
|
55
|
+
left at the field default of 0 would publish a bogus optimistic-lock token.
|
|
56
|
+
``sync_seq`` is correctly left at 0: ``ProjectResource`` is outside the sync union
|
|
57
|
+
(it has no ``OWNER_RESOLVERS`` entry), so ``save()`` would not have allocated one
|
|
58
|
+
either.
|
|
59
|
+
* ``ignore_conflicts=True`` makes the INSERT an ``ON CONFLICT DO NOTHING`` against
|
|
60
|
+
``uniq_project_resource_project_resource``, so a concurrent writer rostering the
|
|
61
|
+
same resource cannot turn this into an ``IntegrityError``. That is the same
|
|
62
|
+
last-writer-wins outcome the previous ``get_or_create`` produced, without the
|
|
63
|
+
per-row savepoint.
|
|
64
|
+
|
|
65
|
+
No signal receiver is wired to ``ProjectResource`` (``post_save`` senders in this
|
|
66
|
+
codebase are ``Task``, ``Program``, ``ProjectMembership``, ``Historical*`` and the
|
|
67
|
+
user model), so skipping ``save()`` skips nothing else.
|
|
68
|
+
"""
|
|
69
|
+
if not resource_ids:
|
|
70
|
+
return
|
|
71
|
+
wanted = list(dict.fromkeys(resource_ids))
|
|
72
|
+
existing = set(
|
|
73
|
+
ProjectResource.objects.filter(project_id=project_id, resource_id__in=wanted).values_list(
|
|
74
|
+
"resource_id", flat=True
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
missing = [rid for rid in wanted if rid not in existing]
|
|
78
|
+
if not missing:
|
|
79
|
+
return
|
|
80
|
+
ProjectResource.objects.bulk_create(
|
|
81
|
+
[
|
|
82
|
+
ProjectResource(project_id=project_id, resource_id=rid, server_version=1)
|
|
83
|
+
for rid in missing
|
|
84
|
+
],
|
|
85
|
+
ignore_conflicts=True,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def rostered_resource_ids(project: Project) -> set[str]:
|
|
90
|
+
"""The resource ids on ``project``'s live roster, as strings.
|
|
91
|
+
|
|
92
|
+
The membership index every inline-authoring surface resolves an owner against
|
|
93
|
+
(ADR-0774 §3). Scoped deliberately: ``Resource`` is a workspace-**global** library
|
|
94
|
+
with no project FK, so resolving against it would let a value authored on one
|
|
95
|
+
project bind to a person who is a member of none of the caller's — the
|
|
96
|
+
reach-across-projects hole ``data-interchange.spec.md`` §5.3 exists to close.
|
|
97
|
+
|
|
98
|
+
``.active()`` (#3572) so a deactivated person cannot be bound as an owner by an
|
|
99
|
+
``@mention`` or an import column: they are off the roster, and an authoring
|
|
100
|
+
surface that still resolved them would re-create the very membership the
|
|
101
|
+
deactivation removed.
|
|
102
|
+
"""
|
|
103
|
+
return {
|
|
104
|
+
str(rid)
|
|
105
|
+
for rid in ProjectResource.objects.active()
|
|
106
|
+
.filter(project=project)
|
|
107
|
+
.values_list("resource_id", flat=True)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def task_is_summary(task: Task) -> bool:
|
|
112
|
+
"""True when ``task`` has at least one live structural descendant.
|
|
113
|
+
|
|
114
|
+
A summary task rolls up from its children, so a direct resource assignment on it
|
|
115
|
+
creates ambiguous scheduling semantics (ADR-0024). Shared by every assignment write
|
|
116
|
+
path so the rule is enforced identically wherever an assignment is authored.
|
|
117
|
+
|
|
118
|
+
Tests descendancy with the ltree ``<path>.*{1}`` lquery against the GiST index on
|
|
119
|
+
``projects_task.wbs_path`` rather than loading children — the question is only
|
|
120
|
+
"does one exist", and this stays O(index seek) on a deep WBS.
|
|
121
|
+
"""
|
|
122
|
+
from django.db import connection
|
|
123
|
+
|
|
124
|
+
if not task.wbs_path:
|
|
125
|
+
return False
|
|
126
|
+
with connection.cursor() as cursor:
|
|
127
|
+
cursor.execute(
|
|
128
|
+
"SELECT EXISTS("
|
|
129
|
+
" SELECT 1 FROM projects_task c"
|
|
130
|
+
" WHERE c.project_id = %s"
|
|
131
|
+
" AND c.is_deleted = false"
|
|
132
|
+
" AND c.id != %s"
|
|
133
|
+
" AND c.wbs_path IS NOT NULL"
|
|
134
|
+
" AND c.wbs_path ~ (%s || '.*{1}')::lquery"
|
|
135
|
+
")",
|
|
136
|
+
[task.project_id, task.pk, str(task.wbs_path)],
|
|
137
|
+
)
|
|
138
|
+
return bool(cursor.fetchone()[0])
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def record_assignment_event(
|
|
142
|
+
*,
|
|
143
|
+
task_id: Any,
|
|
144
|
+
event_type: str,
|
|
145
|
+
resource: Resource,
|
|
146
|
+
actor: Any,
|
|
147
|
+
units_from: Decimal | None = None,
|
|
148
|
+
units_to: Decimal | None = None,
|
|
149
|
+
) -> None:
|
|
150
|
+
"""Write one task-activity audit row for a resource-assignment change (ADR-0394).
|
|
151
|
+
|
|
152
|
+
``TaskResource`` is a through-table with no ``HistoricalRecords`` (like ``RiskTask``),
|
|
153
|
+
so assignment add/remove/re-allocation is recorded in ``TaskActivityEvent`` following
|
|
154
|
+
the ADR-0207 precedent. Written synchronously inside the request transaction so the
|
|
155
|
+
audit row commits or rolls back with the assignment mutation itself — it is a DB row,
|
|
156
|
+
not an external side effect, so (unlike the board broadcast) it is not deferred to
|
|
157
|
+
``transaction.on_commit``. ``actor`` is the acting member; callers on a request path
|
|
158
|
+
always have one, and a system path (an import) records its own provenance instead of
|
|
159
|
+
calling this with a null actor.
|
|
160
|
+
|
|
161
|
+
``units`` carries the allocation: a single value for add/remove, or a
|
|
162
|
+
``{"from", "to"}`` delta for ``assignee_units_changed`` (mirroring the
|
|
163
|
+
``cpm_recalculated`` date-delta shape). Decimals are stringified so the JSON detail
|
|
164
|
+
is exact and stable.
|
|
165
|
+
"""
|
|
166
|
+
build_assignment_event(
|
|
167
|
+
task_id=task_id,
|
|
168
|
+
event_type=event_type,
|
|
169
|
+
resource=resource,
|
|
170
|
+
actor=actor,
|
|
171
|
+
units_from=units_from,
|
|
172
|
+
units_to=units_to,
|
|
173
|
+
).save()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def build_assignment_event(
|
|
177
|
+
*,
|
|
178
|
+
task_id: Any,
|
|
179
|
+
event_type: str,
|
|
180
|
+
resource: Resource,
|
|
181
|
+
actor: Any,
|
|
182
|
+
units_from: Decimal | None = None,
|
|
183
|
+
units_to: Decimal | None = None,
|
|
184
|
+
) -> TaskActivityEvent:
|
|
185
|
+
"""Build — but do not save — the audit row :func:`record_assignment_event` writes.
|
|
186
|
+
|
|
187
|
+
The batched counterpart of that helper: :func:`apply_task_owners` accumulates the
|
|
188
|
+
rows a whole inline-``owners`` payload earns and writes them with one
|
|
189
|
+
``bulk_create`` instead of one INSERT per owner. Both paths build the ``detail``
|
|
190
|
+
payload here so the two spellings of the same audit row cannot drift.
|
|
191
|
+
|
|
192
|
+
``TaskActivityEvent`` is a plain ``models.Model`` with no signal receivers and no
|
|
193
|
+
``server_version``, so ``bulk_create`` loses nothing ``save()`` provided.
|
|
194
|
+
``created_at`` is ``auto_now_add``, which ``bulk_create`` still honors — the insert
|
|
195
|
+
compiler calls ``Field.pre_save`` per object, in list order, exactly as the
|
|
196
|
+
per-row loop did.
|
|
197
|
+
"""
|
|
198
|
+
detail: dict[str, object] = {
|
|
199
|
+
"resource_id": str(resource.pk),
|
|
200
|
+
"resource_name": resource.name,
|
|
201
|
+
}
|
|
202
|
+
if event_type == TaskActivityEventType.ASSIGNEE_UNITS_CHANGED:
|
|
203
|
+
detail["units"] = {
|
|
204
|
+
"from": str(units_from) if units_from is not None else None,
|
|
205
|
+
"to": str(units_to) if units_to is not None else None,
|
|
206
|
+
}
|
|
207
|
+
else:
|
|
208
|
+
detail["units"] = str(units_to) if units_to is not None else None
|
|
209
|
+
return TaskActivityEvent(task_id=task_id, actor=actor, event_type=event_type, detail=detail)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class _OwnerWritePlan(NamedTuple):
|
|
213
|
+
"""Everything one inline-``owners`` payload resolves to, before anything is written.
|
|
214
|
+
|
|
215
|
+
``written`` is the return value of :func:`apply_task_owners` (rows in input order);
|
|
216
|
+
``upserts`` is the de-duplicated set of rows the INSERT actually has to carry;
|
|
217
|
+
``audit`` and ``events`` are the audit rows and board events the payload earns.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
written: list[TaskResource]
|
|
221
|
+
upserts: list[TaskResource]
|
|
222
|
+
audit: list[TaskActivityEvent]
|
|
223
|
+
events: list[tuple[str, str]]
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _plan_owner_writes(task: Task, owners: list[dict[str, object]], actor: Any) -> _OwnerWritePlan:
|
|
227
|
+
"""Resolve ``owners`` against a single read of the task's existing assignment rows.
|
|
228
|
+
|
|
229
|
+
Replaces the per-owner ``filter(...).first()`` probe *and* the SELECT inside
|
|
230
|
+
``update_or_create`` with one ``resource_id__in`` read, then replays the payload
|
|
231
|
+
entry by entry against an in-memory view of that row set. The replay is not
|
|
232
|
+
decoration: a payload naming the same resource twice has to keep producing the
|
|
233
|
+
audit trail the per-row loop produced (an add, then a units delta), not collapse
|
|
234
|
+
into one add at the final allocation.
|
|
235
|
+
|
|
236
|
+
A row is queued only when the write actually changes something. Re-committing an
|
|
237
|
+
owner at the same units is not an ownership change and must not read as one, so it
|
|
238
|
+
earns no audit row and no broadcast.
|
|
239
|
+
"""
|
|
240
|
+
resources: list[Resource] = []
|
|
241
|
+
for owner in owners:
|
|
242
|
+
resource = owner["resource"]
|
|
243
|
+
assert isinstance(resource, Resource)
|
|
244
|
+
resources.append(resource)
|
|
245
|
+
|
|
246
|
+
state: dict[Any, TaskResource] = {
|
|
247
|
+
row.resource_id: row
|
|
248
|
+
for row in TaskResource.objects.filter(task=task, resource_id__in={r.pk for r in resources})
|
|
249
|
+
}
|
|
250
|
+
written: list[TaskResource] = []
|
|
251
|
+
upserts: dict[Any, TaskResource] = {}
|
|
252
|
+
audit: list[TaskActivityEvent] = []
|
|
253
|
+
events: list[tuple[str, str]] = []
|
|
254
|
+
|
|
255
|
+
for owner, resource in zip(owners, resources, strict=True):
|
|
256
|
+
units = owner.get("units")
|
|
257
|
+
# Quantize to the column's 2dp before writing. Without this the audit row
|
|
258
|
+
# stringifies the caller's literal ("1.0") while the drawer path records the
|
|
259
|
+
# DB-normalized value ("1.00") — two spellings of the same allocation in one
|
|
260
|
+
# activity feed, and a spurious units-changed delta on the next write.
|
|
261
|
+
new_units = (units if isinstance(units, Decimal) else Decimal("1.0")).quantize(
|
|
262
|
+
Decimal("0.01")
|
|
263
|
+
)
|
|
264
|
+
prior = state.get(resource.pk)
|
|
265
|
+
|
|
266
|
+
if prior is None:
|
|
267
|
+
# The pk comes from the field's ``uuid4`` default at construction, not from
|
|
268
|
+
# the database, so the board event below can name the row before it exists.
|
|
269
|
+
row = TaskResource(task=task, resource=resource, units=new_units)
|
|
270
|
+
state[resource.pk] = row
|
|
271
|
+
upserts[resource.pk] = row
|
|
272
|
+
written.append(row)
|
|
273
|
+
events.append(("assignment_created", str(row.pk)))
|
|
274
|
+
if actor is not None:
|
|
275
|
+
audit.append(
|
|
276
|
+
build_assignment_event(
|
|
277
|
+
task_id=task.pk,
|
|
278
|
+
event_type=TaskActivityEventType.ASSIGNEE_ADDED,
|
|
279
|
+
resource=resource,
|
|
280
|
+
actor=actor,
|
|
281
|
+
units_to=new_units,
|
|
282
|
+
)
|
|
283
|
+
)
|
|
284
|
+
continue
|
|
285
|
+
|
|
286
|
+
written.append(prior)
|
|
287
|
+
if prior.units == new_units:
|
|
288
|
+
continue
|
|
289
|
+
units_from = prior.units
|
|
290
|
+
prior.units = new_units
|
|
291
|
+
upserts[resource.pk] = prior
|
|
292
|
+
events.append(("assignment_updated", str(prior.pk)))
|
|
293
|
+
if actor is not None:
|
|
294
|
+
audit.append(
|
|
295
|
+
build_assignment_event(
|
|
296
|
+
task_id=task.pk,
|
|
297
|
+
event_type=TaskActivityEventType.ASSIGNEE_UNITS_CHANGED,
|
|
298
|
+
resource=resource,
|
|
299
|
+
actor=actor,
|
|
300
|
+
units_from=units_from,
|
|
301
|
+
units_to=new_units,
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
return _OwnerWritePlan(
|
|
306
|
+
written=written, upserts=list(upserts.values()), audit=audit, events=events
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def apply_task_owners(
|
|
311
|
+
task: Task, owners: list[dict[str, object]], *, actor: Any = None, broadcast: bool = True
|
|
312
|
+
) -> list[TaskResource]:
|
|
313
|
+
"""Write inline ``owners`` authored on a task write into ``TaskResource`` rows.
|
|
314
|
+
|
|
315
|
+
The single primitive behind every Project Designer authoring surface that says
|
|
316
|
+
"this person owns this row" — the ``@ana`` token, the import Owner column, the
|
|
317
|
+
seeded-landing owner action (ADR-0774, #2718). It exists because the obvious
|
|
318
|
+
implementation, ``Task.assignee``, is silently wrong: every capacity, utilization,
|
|
319
|
+
heat-map and sprint-capacity computation sums ``TaskResource.units`` and never reads
|
|
320
|
+
``assignee``, so a bare-assignee task contributes zero load forever with no warning.
|
|
321
|
+
|
|
322
|
+
**Upsert, never replace-set.** Applied through the ``(task, resource)`` unique
|
|
323
|
+
constraint: a resource already assigned has its ``units`` updated, a new one is
|
|
324
|
+
added, and anyone *not* named is left alone. ``@ana`` means "Ana owns this", not
|
|
325
|
+
"Ana is now the only person here" — a replace-set would let one token edit silently
|
|
326
|
+
delete a co-assignee written by somebody else. Removal stays on
|
|
327
|
+
``DELETE /task-resources/{id}/``.
|
|
328
|
+
|
|
329
|
+
Each assigned resource is auto-rostered (#241) so an owner can never be assigned
|
|
330
|
+
work yet be invisible in Team → Roster, Allocation, and Heatmap.
|
|
331
|
+
|
|
332
|
+
**Parity with ``TaskResourceViewSet`` is the point of doing all of this here.** An
|
|
333
|
+
assignment authored inline must be indistinguishable, downstream, from one made in
|
|
334
|
+
the task drawer — so this also writes the ADR-0394 audit row and fires the
|
|
335
|
+
``assignment_*`` board event. Both matter for concrete reasons:
|
|
336
|
+
|
|
337
|
+
- Without the audit row, an owner set by token is missing from the task activity
|
|
338
|
+
feed while the same assignment made in the drawer appears — a plan whose ownership
|
|
339
|
+
history depends on which affordance was used.
|
|
340
|
+
- Without the broadcast, `task_updated` alone would reach other clients. That
|
|
341
|
+
invalidates task queries but **not** the allocation timeline or the heat map,
|
|
342
|
+
which `useProjectWebSocket` refreshes on `assignment_*` only — so a colleague
|
|
343
|
+
watching capacity would keep seeing a stale zero, which is this issue's original
|
|
344
|
+
complaint reappearing over the wire.
|
|
345
|
+
|
|
346
|
+
The broadcast is deferred with ``transaction.on_commit`` (it is an external side
|
|
347
|
+
effect that must not fire for a rolled-back write); the audit row is not (it is a DB
|
|
348
|
+
row that must commit or roll back with the assignment).
|
|
349
|
+
|
|
350
|
+
**In-transaction cost is constant in the number of owners, not linear (#3575).** At
|
|
351
|
+
most five statements for any payload size: one read of the task's existing
|
|
352
|
+
assignment rows, one upsert of the rows that changed, one read of the roster, one
|
|
353
|
+
insert of the missing roster rows, one insert of the audit rows. The previous
|
|
354
|
+
per-owner loop cost roughly six statements *each* (a redundant ``.first()`` probe
|
|
355
|
+
whose only job was to read ``prior.units``, ``update_or_create``'s savepoint +
|
|
356
|
+
``SELECT … FOR UPDATE`` + write, a ``get_or_create`` roster probe, and an audit
|
|
357
|
+
INSERT), so a 50-row bulk paste with two owners apiece opened ~750 statements inside
|
|
358
|
+
one atomic block.
|
|
359
|
+
|
|
360
|
+
Read that claim narrowly: it is about the statements *inside the transaction*, which
|
|
361
|
+
is where the cost mattered. Two things downstream stay linear in the payload and are
|
|
362
|
+
unchanged by this — the audit rows (N rows, but in one INSERT) and the post-commit
|
|
363
|
+
fanout, which is still one ``broadcast_board_event`` per changed row. Batching that
|
|
364
|
+
fanout would need a new frozen event type and a client handler, so it is separate
|
|
365
|
+
work, not a claim this function makes.
|
|
366
|
+
|
|
367
|
+
**Why an upsert rather than the row lock it replaces.** ``update_or_create`` took a
|
|
368
|
+
``SELECT … FOR UPDATE`` on the assignment row; ``bulk_create(update_conflicts=True)``
|
|
369
|
+
takes none, and instead issues ``INSERT … ON CONFLICT (task_id, resource_id) DO
|
|
370
|
+
UPDATE SET units``, resolved against ``uniq_task_resource_task_resource``. That is
|
|
371
|
+
the same last-writer-wins outcome the lock produced — two concurrent requests
|
|
372
|
+
naming the same owner serialize on the unique index inside the database and both
|
|
373
|
+
commit, rather than one raising ``IntegrityError`` — but it survives being batched,
|
|
374
|
+
which a per-row lock does not.
|
|
375
|
+
|
|
376
|
+
Two behavioral differences follow, both confined to the losing side of a genuine
|
|
377
|
+
concurrent add of the *same* owner to the *same* task, and both accepted:
|
|
378
|
+
|
|
379
|
+
* The loser's in-memory row keeps the pk it generated rather than the winner's, so
|
|
380
|
+
its ``assignment_created`` event names an id the database discarded. Inert — every
|
|
381
|
+
``assignment_*`` event funnels to the same ``scheduleInvalidate`` on the client,
|
|
382
|
+
which refetches rather than reading the id, and all three callers discard this
|
|
383
|
+
function's return value.
|
|
384
|
+
* The loser records a second ``assignee_added`` audit row. Under the old lock its
|
|
385
|
+
``get_or_create`` caught the ``IntegrityError``, re-read the row and returned
|
|
386
|
+
``created=False``, and because ``prior`` was still ``None`` from the probe *before*
|
|
387
|
+
it, the loser recorded nothing at all. Both writers now read ``prior is None`` and
|
|
388
|
+
both write an add, so the activity feed can show one person added twice. Reading
|
|
389
|
+
that back off the database to suppress it would cost the query the batching exists
|
|
390
|
+
to remove, for a duplicate line in an append-only feed; a lost audit row would have
|
|
391
|
+
been the worse trade.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
task: The task being authored. Must already be saved.
|
|
395
|
+
owners: Validated ``[{"resource": Resource, "units": Decimal}, ...]``. The
|
|
396
|
+
caller (``TaskSerializer._resolve_owners``) is responsible for having
|
|
397
|
+
scoped every resource to ``task.project``'s roster.
|
|
398
|
+
actor: The acting user, for the audit row. ``None`` skips it — correct for a
|
|
399
|
+
system path such as an import, which records its own provenance.
|
|
400
|
+
broadcast: Emit the ``assignment_*`` board events. Pass ``False`` only when the
|
|
401
|
+
caller already emits a coarser event covering the same rows — the recurring
|
|
402
|
+
occurrence sweep (#2902) writes many tasks per project per run and
|
|
403
|
+
deliberately emits one bulk ``tasks_bulk_mutated`` per project instead of one
|
|
404
|
+
event per row, and both event families funnel to the same
|
|
405
|
+
``scheduleInvalidate('tasks')`` on the client, so the per-assignment events
|
|
406
|
+
would be pure duplicate load. ``TaskSerializer`` (#3643) applies the same
|
|
407
|
+
reasoning to both batch write paths, via
|
|
408
|
+
``TaskSerializer._owners_broadcast_enabled()``: ``POST /tasks/bulk/``
|
|
409
|
+
(``task_bulk._register_bulk_commit_hooks``) and the mobile sync upload
|
|
410
|
+
(``sync.views.ProjectSyncView._apply_and_record``, coalesced for #809) each
|
|
411
|
+
fire one ``tasks_bulk_mutated`` per request unconditionally covering every
|
|
412
|
+
id the batch touched, so both pass ``False`` here. Do **not** generalize
|
|
413
|
+
this to the plain single-row REST create/PATCH (``TaskViewSet``) — that
|
|
414
|
+
path has no coarser event of its own, so ``assignment_*`` is the only
|
|
415
|
+
WebSocket signal a connected client gets from it.
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
The ``TaskResource`` rows written, in input order.
|
|
419
|
+
"""
|
|
420
|
+
from django.db import transaction
|
|
421
|
+
|
|
422
|
+
if not owners:
|
|
423
|
+
return []
|
|
424
|
+
|
|
425
|
+
plan = _plan_owner_writes(task, owners, actor)
|
|
426
|
+
|
|
427
|
+
if plan.upserts:
|
|
428
|
+
# One statement for new rows and re-allocations alike. ``TaskResource`` is a
|
|
429
|
+
# plain ``models.Model`` — no ``server_version``, no ``HistoricalRecords``, and
|
|
430
|
+
# no signal receiver anywhere in the codebase — so bypassing ``save()`` here
|
|
431
|
+
# bypasses nothing that ``save()`` was doing.
|
|
432
|
+
TaskResource.objects.bulk_create(
|
|
433
|
+
plan.upserts,
|
|
434
|
+
update_conflicts=True,
|
|
435
|
+
update_fields=["units"],
|
|
436
|
+
unique_fields=["task", "resource"],
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
# Rostered from ``written``, not ``upserts``: a re-write at unchanged units still
|
|
440
|
+
# has to guarantee the roster row, exactly as the per-owner ``get_or_create`` did.
|
|
441
|
+
# ``task.project_id`` rather than ``task.project`` so the count stays constant even
|
|
442
|
+
# when the caller's Task has no Project in its FK cache.
|
|
443
|
+
ensure_project_resources(task.project_id, [row.resource_id for row in plan.written])
|
|
444
|
+
|
|
445
|
+
if plan.audit:
|
|
446
|
+
TaskActivityEvent.objects.bulk_create(plan.audit)
|
|
447
|
+
|
|
448
|
+
written = plan.written
|
|
449
|
+
events = plan.events
|
|
450
|
+
|
|
451
|
+
if events and broadcast:
|
|
452
|
+
project_id = str(task.project_id)
|
|
453
|
+
task_id = str(task.pk)
|
|
454
|
+
|
|
455
|
+
def _broadcast() -> None:
|
|
456
|
+
from trueppm_api.apps.sync.broadcast import broadcast_board_event
|
|
457
|
+
|
|
458
|
+
for event_type, assignment_id in events:
|
|
459
|
+
broadcast_board_event(
|
|
460
|
+
project_id, event_type, {"id": assignment_id, "task_id": task_id}
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
transaction.on_commit(_broadcast)
|
|
464
|
+
|
|
465
|
+
return written
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""URL routing for the resources app."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from rest_framework.routers import DefaultRouter
|
|
6
|
+
|
|
7
|
+
from trueppm_api.apps.resources.views import (
|
|
8
|
+
ProjectResourceViewSet,
|
|
9
|
+
ResourceSkillViewSet,
|
|
10
|
+
ResourceViewSet,
|
|
11
|
+
SkillViewSet,
|
|
12
|
+
TaskResourceViewSet,
|
|
13
|
+
TaskSkillRequirementViewSet,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
router = DefaultRouter()
|
|
17
|
+
router.register(r"resources", ResourceViewSet, basename="resource")
|
|
18
|
+
router.register(r"task-resources", TaskResourceViewSet, basename="task-resource")
|
|
19
|
+
router.register(r"skills", SkillViewSet, basename="skill")
|
|
20
|
+
router.register(r"resource-skills", ResourceSkillViewSet, basename="resource-skill")
|
|
21
|
+
router.register(r"project-resources", ProjectResourceViewSet, basename="project-resource")
|
|
22
|
+
router.register(
|
|
23
|
+
r"task-skill-requirements",
|
|
24
|
+
TaskSkillRequirementViewSet,
|
|
25
|
+
basename="task-skill-requirement",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
urlpatterns = router.urls
|