trueppm-api 0.4.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trueppm_api/__init__.py +14 -0
- trueppm_api/apps/access/__init__.py +0 -0
- trueppm_api/apps/access/apps.py +19 -0
- trueppm_api/apps/access/groups.py +518 -0
- trueppm_api/apps/access/management/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/create_admin.py +164 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +209 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +167 -0
- trueppm_api/apps/access/migrations/0002_alter_projectmembership_id.py +29 -0
- trueppm_api/apps/access/migrations/0003_projectmembership_soft_delete.py +24 -0
- trueppm_api/apps/access/migrations/0004_alter_projectmembership_role.py +34 -0
- trueppm_api/apps/access/migrations/0005_program_entity_and_membership.py +76 -0
- trueppm_api/apps/access/migrations/0006_role_ordinal_spacing.py +113 -0
- trueppm_api/apps/access/migrations/0007_projectmembership_joined_at_and_more.py +35 -0
- trueppm_api/apps/access/migrations/0008_projectmembership_source_group.py +25 -0
- trueppm_api/apps/access/migrations/0009_projectmembership_pm_proj_serverver_idx.py +20 -0
- trueppm_api/apps/access/migrations/0010_programmembership_joined_at_and_more.py +38 -0
- trueppm_api/apps/access/migrations/0011_alter_programmembership_unique_together_and_more.py +36 -0
- trueppm_api/apps/access/migrations/0012_programmembership_role_title.py +17 -0
- trueppm_api/apps/access/migrations/0013_userdefinedmentiongroup.py +82 -0
- trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
- trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
- trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
- trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +132 -0
- trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
- trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
- trueppm_api/apps/access/migrations/__init__.py +0 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +2837 -0
- trueppm_api/apps/access/serializers.py +946 -0
- trueppm_api/apps/access/services.py +667 -0
- trueppm_api/apps/access/signals.py +103 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +95 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1931 -0
- trueppm_api/apps/agents/__init__.py +6 -0
- trueppm_api/apps/agents/apps.py +11 -0
- trueppm_api/apps/agents/canonical.py +73 -0
- trueppm_api/apps/agents/deferred.py +108 -0
- trueppm_api/apps/agents/management/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
- trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
- trueppm_api/apps/agents/middleware.py +44 -0
- trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
- trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
- trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
- trueppm_api/apps/agents/migrations/__init__.py +0 -0
- trueppm_api/apps/agents/models.py +371 -0
- trueppm_api/apps/agents/refusal.py +103 -0
- trueppm_api/apps/agents/serializers.py +85 -0
- trueppm_api/apps/agents/services.py +397 -0
- trueppm_api/apps/agents/signals.py +30 -0
- trueppm_api/apps/agents/urls.py +15 -0
- trueppm_api/apps/agents/views.py +108 -0
- trueppm_api/apps/csvimport/__init__.py +12 -0
- trueppm_api/apps/csvimport/apps.py +11 -0
- trueppm_api/apps/csvimport/mapping.py +363 -0
- trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
- trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
- trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
- trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
- trueppm_api/apps/csvimport/models.py +111 -0
- trueppm_api/apps/csvimport/parser.py +1825 -0
- trueppm_api/apps/csvimport/services.py +48 -0
- trueppm_api/apps/csvimport/tasks.py +418 -0
- trueppm_api/apps/csvimport/template.py +37 -0
- trueppm_api/apps/csvimport/urls.py +50 -0
- trueppm_api/apps/csvimport/views.py +793 -0
- trueppm_api/apps/history/__init__.py +0 -0
- trueppm_api/apps/history/apps.py +12 -0
- trueppm_api/apps/history/changelog.py +359 -0
- trueppm_api/apps/history/diff_policy.py +177 -0
- trueppm_api/apps/history/serializers.py +104 -0
- trueppm_api/apps/history/signals.py +82 -0
- trueppm_api/apps/history/tasks.py +105 -0
- trueppm_api/apps/history/urls.py +38 -0
- trueppm_api/apps/history/views.py +443 -0
- trueppm_api/apps/idempotency/__init__.py +0 -0
- trueppm_api/apps/idempotency/apps.py +11 -0
- trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
- trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
- trueppm_api/apps/idempotency/mixins.py +226 -0
- trueppm_api/apps/idempotency/models.py +75 -0
- trueppm_api/apps/idempotency/tasks.py +61 -0
- trueppm_api/apps/integrations/__init__.py +13 -0
- trueppm_api/apps/integrations/apps.py +59 -0
- trueppm_api/apps/integrations/connections.py +806 -0
- trueppm_api/apps/integrations/encryption.py +92 -0
- trueppm_api/apps/integrations/external_sources.py +837 -0
- trueppm_api/apps/integrations/git_automation_services.py +231 -0
- trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
- trueppm_api/apps/integrations/http.py +403 -0
- trueppm_api/apps/integrations/me_work.py +218 -0
- trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
- trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
- trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
- trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
- trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
- trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
- trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
- trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
- trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
- trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
- trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
- trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
- trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
- trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +622 -0
- trueppm_api/apps/integrations/notification_channels.py +57 -0
- trueppm_api/apps/integrations/opengraph.py +229 -0
- trueppm_api/apps/integrations/outgoing.py +201 -0
- trueppm_api/apps/integrations/providers.py +519 -0
- trueppm_api/apps/integrations/registry.py +345 -0
- trueppm_api/apps/integrations/serializers.py +453 -0
- trueppm_api/apps/integrations/services.py +171 -0
- trueppm_api/apps/integrations/tasks.py +731 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1106 -0
- trueppm_api/apps/jiraimport/__init__.py +8 -0
- trueppm_api/apps/jiraimport/apps.py +11 -0
- trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
- trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
- trueppm_api/apps/jiraimport/models.py +70 -0
- trueppm_api/apps/jiraimport/parser.py +350 -0
- trueppm_api/apps/jiraimport/services.py +46 -0
- trueppm_api/apps/jiraimport/tasks.py +277 -0
- trueppm_api/apps/jiraimport/urls.py +15 -0
- trueppm_api/apps/jiraimport/views.py +153 -0
- trueppm_api/apps/msproject/__init__.py +0 -0
- trueppm_api/apps/msproject/apps.py +11 -0
- trueppm_api/apps/msproject/dataclasses.py +153 -0
- trueppm_api/apps/msproject/exporter.py +500 -0
- trueppm_api/apps/msproject/extended_attributes.py +77 -0
- trueppm_api/apps/msproject/importer.py +1095 -0
- trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
- trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
- trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
- trueppm_api/apps/msproject/migrations/__init__.py +0 -0
- trueppm_api/apps/msproject/models.py +81 -0
- trueppm_api/apps/msproject/parser.py +1231 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +436 -0
- trueppm_api/apps/msproject/urls.py +39 -0
- trueppm_api/apps/msproject/views.py +531 -0
- trueppm_api/apps/notifications/__init__.py +7 -0
- trueppm_api/apps/notifications/apps.py +15 -0
- trueppm_api/apps/notifications/backfill.py +79 -0
- trueppm_api/apps/notifications/categories.py +112 -0
- trueppm_api/apps/notifications/delivery_limits.py +258 -0
- trueppm_api/apps/notifications/digests.py +447 -0
- trueppm_api/apps/notifications/email_backend.py +330 -0
- trueppm_api/apps/notifications/email_health.py +128 -0
- trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
- trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +294 -0
- trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
- trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
- trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
- trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
- trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
- trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
- trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
- trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
- trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
- trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
- trueppm_api/apps/notifications/migrations/__init__.py +0 -0
- trueppm_api/apps/notifications/models.py +1197 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +961 -0
- trueppm_api/apps/notifications/services.py +1451 -0
- trueppm_api/apps/notifications/tasks.py +711 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +904 -0
- trueppm_api/apps/observability/__init__.py +0 -0
- trueppm_api/apps/observability/apps.py +31 -0
- trueppm_api/apps/observability/logging.py +213 -0
- trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
- trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
- trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
- trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
- trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
- trueppm_api/apps/observability/migrations/__init__.py +0 -0
- trueppm_api/apps/observability/models.py +141 -0
- trueppm_api/apps/observability/otel/__init__.py +45 -0
- trueppm_api/apps/observability/otel/attributes.py +247 -0
- trueppm_api/apps/observability/otel/export_health.py +533 -0
- trueppm_api/apps/observability/otel/instrumentation.py +351 -0
- trueppm_api/apps/observability/otel/metrics.py +551 -0
- trueppm_api/apps/observability/otel/provider.py +511 -0
- trueppm_api/apps/observability/otel/request_attributes.py +204 -0
- trueppm_api/apps/observability/purge_registry.py +124 -0
- trueppm_api/apps/observability/retention.py +139 -0
- trueppm_api/apps/observability/selectors.py +969 -0
- trueppm_api/apps/observability/serializers.py +142 -0
- trueppm_api/apps/observability/services.py +407 -0
- trueppm_api/apps/observability/tasks.py +266 -0
- trueppm_api/apps/observability/urls.py +27 -0
- trueppm_api/apps/observability/views.py +523 -0
- trueppm_api/apps/profiles/__init__.py +0 -0
- trueppm_api/apps/profiles/apps.py +15 -0
- trueppm_api/apps/profiles/constants.py +54 -0
- trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
- trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
- trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
- trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
- trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
- trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
- trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
- trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
- trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
- trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
- trueppm_api/apps/profiles/migrations/__init__.py +0 -0
- trueppm_api/apps/profiles/models.py +276 -0
- trueppm_api/apps/profiles/serializers.py +199 -0
- trueppm_api/apps/profiles/services.py +466 -0
- trueppm_api/apps/profiles/views.py +124 -0
- trueppm_api/apps/projects/__init__.py +0 -0
- trueppm_api/apps/projects/actual_date_rules.py +163 -0
- trueppm_api/apps/projects/amend.py +133 -0
- trueppm_api/apps/projects/apps.py +38 -0
- trueppm_api/apps/projects/asset_feed.py +459 -0
- trueppm_api/apps/projects/asset_views.py +317 -0
- trueppm_api/apps/projects/attachment_policy.py +278 -0
- trueppm_api/apps/projects/authentication.py +449 -0
- trueppm_api/apps/projects/backfill.py +287 -0
- trueppm_api/apps/projects/backlog_services.py +324 -0
- trueppm_api/apps/projects/backlog_views.py +302 -0
- trueppm_api/apps/projects/batch_operation_views.py +264 -0
- trueppm_api/apps/projects/blocker_services.py +510 -0
- trueppm_api/apps/projects/board_activity.py +462 -0
- trueppm_api/apps/projects/board_activity_views.py +186 -0
- trueppm_api/apps/projects/board_lanes.py +134 -0
- trueppm_api/apps/projects/bulk_settings.py +194 -0
- trueppm_api/apps/projects/bundled_templates.py +215 -0
- trueppm_api/apps/projects/calendar_settings.py +180 -0
- trueppm_api/apps/projects/ceremony_views.py +244 -0
- trueppm_api/apps/projects/commit_moment.py +204 -0
- trueppm_api/apps/projects/config_notice.py +1325 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +93 -0
- trueppm_api/apps/projects/decisions_views.py +94 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +353 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4256 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2000 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1825 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1905 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1409 -0
- trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
- trueppm_api/apps/projects/inbound_sync.py +555 -0
- trueppm_api/apps/projects/iteration_label.py +85 -0
- trueppm_api/apps/projects/lifecycle.py +191 -0
- trueppm_api/apps/projects/management/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
- trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
- trueppm_api/apps/projects/management/commands/export_program.py +50 -0
- trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
- trueppm_api/apps/projects/management/commands/load_sample_project.py +247 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +405 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +178 -0
- trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
- trueppm_api/apps/projects/mcp_settings.py +305 -0
- trueppm_api/apps/projects/methodology.py +178 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5595 -0
- trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
- trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
- trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
- trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
- trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
- trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
- trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
- trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
- trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
- trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
- trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
- trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
- trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
- trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
- trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
- trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
- trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
- trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
- trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
- trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
- trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
- trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
- trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
- trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
- trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
- trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
- trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
- trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
- trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
- trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
- trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
- trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
- trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
- trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
- trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
- trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
- trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
- trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
- trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
- trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
- trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
- trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
- trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
- trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
- trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
- trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
- trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
- trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
- trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
- trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
- trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
- trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
- trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
- trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
- trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
- trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
- trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
- trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
- trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
- trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
- trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
- trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
- trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
- trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
- trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
- trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
- trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
- trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
- trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
- trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
- trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
- trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
- trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
- trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
- trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
- trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
- trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
- trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
- trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
- trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
- trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
- trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
- trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
- trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
- trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
- trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
- trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +44 -0
- trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
- trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
- trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
- trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
- trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
- trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
- trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
- trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
- trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +61 -0
- trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
- trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
- trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
- trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +62 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
- trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
- trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
- trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
- trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
- trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
- trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
- trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
- trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
- trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
- trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
- trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
- trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
- trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
- trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
- trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
- trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
- trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
- trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
- trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
- trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
- trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
- trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
- trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
- trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
- trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
- trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
- trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
- trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
- trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
- trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
- trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
- trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
- trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
- trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
- trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
- trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
- trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +8946 -0
- trueppm_api/apps/projects/poker_services.py +154 -0
- trueppm_api/apps/projects/poker_views.py +398 -0
- trueppm_api/apps/projects/product_backlog_services.py +450 -0
- trueppm_api/apps/projects/program_label_services.py +94 -0
- trueppm_api/apps/projects/program_label_views.py +189 -0
- trueppm_api/apps/projects/program_rollup.py +575 -0
- trueppm_api/apps/projects/program_schedule.py +745 -0
- trueppm_api/apps/projects/program_views.py +3220 -0
- trueppm_api/apps/projects/project_templates.py +422 -0
- trueppm_api/apps/projects/receivers.py +234 -0
- trueppm_api/apps/projects/refusal_codes.py +140 -0
- trueppm_api/apps/projects/reorder_services.py +145 -0
- trueppm_api/apps/projects/restructure_hooks.py +54 -0
- trueppm_api/apps/projects/retro_board_services.py +368 -0
- trueppm_api/apps/projects/retro_services.py +285 -0
- trueppm_api/apps/projects/risk_import.py +396 -0
- trueppm_api/apps/projects/row_identity.py +113 -0
- trueppm_api/apps/projects/schema_migrations.py +228 -0
- trueppm_api/apps/projects/schemas/seed_v1.json +493 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +892 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/exporter.py +1644 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2390 -0
- trueppm_api/apps/projects/seed/reanchor.py +551 -0
- trueppm_api/apps/projects/seed/reldates.py +126 -0
- trueppm_api/apps/projects/seed/replace.py +126 -0
- trueppm_api/apps/projects/seed/replay.py +1691 -0
- trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
- trueppm_api/apps/projects/seed/samples.py +427 -0
- trueppm_api/apps/projects/seed/validation.py +1149 -0
- trueppm_api/apps/projects/serializers.py +12310 -0
- trueppm_api/apps/projects/services.py +6966 -0
- trueppm_api/apps/projects/share_serializers.py +132 -0
- trueppm_api/apps/projects/share_services.py +325 -0
- trueppm_api/apps/projects/share_views.py +374 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +909 -0
- trueppm_api/apps/projects/signal_privacy_views.py +476 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +266 -0
- trueppm_api/apps/projects/sprint_cadence.py +453 -0
- trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
- trueppm_api/apps/projects/standup.py +304 -0
- trueppm_api/apps/projects/standup_views.py +99 -0
- trueppm_api/apps/projects/structural_operation_services.py +727 -0
- trueppm_api/apps/projects/structural_operation_views.py +311 -0
- trueppm_api/apps/projects/surface_visibility.py +126 -0
- trueppm_api/apps/projects/task_batch_services.py +379 -0
- trueppm_api/apps/projects/task_bulk.py +1152 -0
- trueppm_api/apps/projects/task_classification.py +561 -0
- trueppm_api/apps/projects/task_classification_defaults.py +95 -0
- trueppm_api/apps/projects/task_duration_settings.py +142 -0
- trueppm_api/apps/projects/task_grouping.py +754 -0
- trueppm_api/apps/projects/tasks.py +1917 -0
- trueppm_api/apps/projects/template_divergence.py +168 -0
- trueppm_api/apps/projects/template_services.py +160 -0
- trueppm_api/apps/projects/template_tasks.py +258 -0
- trueppm_api/apps/projects/template_views.py +799 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1011 -0
- trueppm_api/apps/projects/utilization.py +885 -0
- trueppm_api/apps/projects/views.py +20572 -0
- trueppm_api/apps/projects/wbs_paths.py +119 -0
- trueppm_api/apps/resources/__init__.py +0 -0
- trueppm_api/apps/resources/apps.py +8 -0
- trueppm_api/apps/resources/capacity.py +121 -0
- trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
- trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
- trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
- trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
- trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
- trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
- trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
- trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
- trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
- trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
- trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
- trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
- trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
- trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
- trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
- trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
- trueppm_api/apps/resources/migrations/__init__.py +0 -0
- trueppm_api/apps/resources/models.py +298 -0
- trueppm_api/apps/resources/serializers.py +426 -0
- trueppm_api/apps/resources/services.py +465 -0
- trueppm_api/apps/resources/urls.py +28 -0
- trueppm_api/apps/resources/views.py +1749 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +119 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
- trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
- trueppm_api/apps/scheduling/graph_guard.py +263 -0
- trueppm_api/apps/scheduling/management/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
- trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
- trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
- trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
- trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
- trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
- trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
- trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
- trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
- trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
- trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
- trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
- trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
- trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
- trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
- trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
- trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +516 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +283 -0
- trueppm_api/apps/scheduling/serializers.py +564 -0
- trueppm_api/apps/scheduling/services.py +1182 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +1984 -0
- trueppm_api/apps/scheduling/telemetry.py +80 -0
- trueppm_api/apps/scheduling/units.py +35 -0
- trueppm_api/apps/scheduling/urls.py +57 -0
- trueppm_api/apps/scheduling/views.py +2564 -0
- trueppm_api/apps/sso/__init__.py +0 -0
- trueppm_api/apps/sso/apps.py +19 -0
- trueppm_api/apps/sso/extensions.py +130 -0
- trueppm_api/apps/sso/management/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
- trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
- trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
- trueppm_api/apps/sso/migrations/__init__.py +0 -0
- trueppm_api/apps/sso/models.py +148 -0
- trueppm_api/apps/sso/serializers.py +437 -0
- trueppm_api/apps/sso/services.py +1385 -0
- trueppm_api/apps/sso/urls.py +47 -0
- trueppm_api/apps/sso/views.py +935 -0
- trueppm_api/apps/sync/__init__.py +0 -0
- trueppm_api/apps/sync/apps.py +15 -0
- trueppm_api/apps/sync/backfill.py +208 -0
- trueppm_api/apps/sync/broadcast.py +320 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +471 -0
- trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
- trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
- trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
- trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
- trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
- trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +225 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +371 -0
- trueppm_api/apps/sync/serializers.py +687 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +501 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +930 -0
- trueppm_api/apps/sync/ws_auth.py +193 -0
- trueppm_api/apps/taskruns/__init__.py +0 -0
- trueppm_api/apps/taskruns/apps.py +11 -0
- trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
- trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
- trueppm_api/apps/taskruns/models.py +68 -0
- trueppm_api/apps/taskruns/serializers.py +92 -0
- trueppm_api/apps/taskruns/tasks.py +60 -0
- trueppm_api/apps/taskruns/tracker.py +286 -0
- trueppm_api/apps/taskruns/urls.py +46 -0
- trueppm_api/apps/taskruns/views.py +195 -0
- trueppm_api/apps/teams/__init__.py +8 -0
- trueppm_api/apps/teams/apps.py +16 -0
- trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
- trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +142 -0
- trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
- trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
- trueppm_api/apps/teams/migrations/__init__.py +0 -0
- trueppm_api/apps/teams/models.py +145 -0
- trueppm_api/apps/teams/permissions.py +149 -0
- trueppm_api/apps/teams/serializers.py +95 -0
- trueppm_api/apps/teams/services.py +434 -0
- trueppm_api/apps/teams/signals.py +61 -0
- trueppm_api/apps/teams/urls.py +34 -0
- trueppm_api/apps/teams/views.py +201 -0
- trueppm_api/apps/timetracking/__init__.py +0 -0
- trueppm_api/apps/timetracking/apps.py +9 -0
- trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
- trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
- trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
- trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
- trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
- trueppm_api/apps/timetracking/models.py +178 -0
- trueppm_api/apps/timetracking/serializers.py +175 -0
- trueppm_api/apps/timetracking/services.py +117 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +526 -0
- trueppm_api/apps/webhooks/__init__.py +0 -0
- trueppm_api/apps/webhooks/apps.py +8 -0
- trueppm_api/apps/webhooks/backfill.py +90 -0
- trueppm_api/apps/webhooks/dispatch.py +144 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +243 -0
- trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
- trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
- trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
- trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
- trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
- trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
- trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
- trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +90 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +372 -0
- trueppm_api/apps/webhooks/serializers.py +318 -0
- trueppm_api/apps/webhooks/tasks.py +663 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +383 -0
- trueppm_api/apps/workflow_engine/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/apps.py +23 -0
- trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
- trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
- trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
- trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
- trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/models.py +265 -0
- trueppm_api/apps/workflow_engine/tasks.py +368 -0
- trueppm_api/apps/workspace/__init__.py +0 -0
- trueppm_api/apps/workspace/apps.py +10 -0
- trueppm_api/apps/workspace/backfill.py +111 -0
- trueppm_api/apps/workspace/export.py +242 -0
- trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
- trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
- trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
- trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
- trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
- trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
- trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
- trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
- trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
- trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
- trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
- trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
- trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
- trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
- trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
- trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
- trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
- trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
- trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
- trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
- trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
- trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
- trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
- trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
- trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
- trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
- trueppm_api/apps/workspace/migrations/__init__.py +0 -0
- trueppm_api/apps/workspace/models.py +857 -0
- trueppm_api/apps/workspace/permissions.py +187 -0
- trueppm_api/apps/workspace/serializers.py +590 -0
- trueppm_api/apps/workspace/services.py +919 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +555 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1502 -0
- trueppm_api/asgi.py +42 -0
- trueppm_api/celery.py +31 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +916 -0
- trueppm_api/core/constant_time.py +51 -0
- trueppm_api/core/csp.py +54 -0
- trueppm_api/core/db.py +87 -0
- trueppm_api/core/db_session.py +37 -0
- trueppm_api/core/exception_handlers.py +157 -0
- trueppm_api/core/export_downloads.py +56 -0
- trueppm_api/core/extension_providers.py +56 -0
- trueppm_api/core/extension_signals.py +147 -0
- trueppm_api/core/idempotent.py +260 -0
- trueppm_api/core/metadata.py +42 -0
- trueppm_api/core/middleware.py +51 -0
- trueppm_api/core/openapi.py +949 -0
- trueppm_api/core/password_reset.py +500 -0
- trueppm_api/core/protect_conflict.py +100 -0
- trueppm_api/core/ratelimit.py +148 -0
- trueppm_api/core/redis_throttle.py +82 -0
- trueppm_api/core/refresh_cookie_policy.py +156 -0
- trueppm_api/core/request_body.py +86 -0
- trueppm_api/core/security_checks.py +884 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +344 -0
- trueppm_api/core/valkey.py +214 -0
- trueppm_api/core/valkey_cache.py +66 -0
- trueppm_api/core/valkey_checks.py +104 -0
- trueppm_api/core/valkey_config.py +39 -0
- trueppm_api/core/worker_broker_wait.py +149 -0
- trueppm_api/core/worker_heartbeat.py +134 -0
- trueppm_api/fields.py +36 -0
- trueppm_api/permissions.py +30 -0
- trueppm_api/py.typed +0 -0
- trueppm_api/routing.py +11 -0
- trueppm_api/settings/__init__.py +0 -0
- trueppm_api/settings/base.py +2455 -0
- trueppm_api/settings/dev.py +273 -0
- trueppm_api/settings/prod.py +228 -0
- trueppm_api/urls.py +153 -0
- trueppm_api/workflows/__init__.py +38 -0
- trueppm_api/workflows/backends/__init__.py +6 -0
- trueppm_api/workflows/backends/default.py +297 -0
- trueppm_api/workflows/consumers/__init__.py +11 -0
- trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
- trueppm_api/workflows/interface.py +148 -0
- trueppm_api/workflows/registry.py +106 -0
- trueppm_api/workflows/services.py +90 -0
- trueppm_api/wsgi.py +11 -0
- trueppm_api-0.4.0b2.dist-info/METADATA +70 -0
- trueppm_api-0.4.0b2.dist-info/RECORD +759 -0
- trueppm_api-0.4.0b2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Service layer for the CSV import outbox (mirrors jiraimport.services)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from django.utils import timezone
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def enqueue_csv_import(import_request_id: str) -> None:
|
|
13
|
+
"""Attempt to dispatch a PENDING CsvImportRequest row to Celery.
|
|
14
|
+
|
|
15
|
+
Safe to call from ``transaction.on_commit()`` and from the drain body. On a
|
|
16
|
+
broker outage the row stays PENDING so ``drain_csv_import_queue`` retries
|
|
17
|
+
within 30 seconds; a no-op if the row is no longer PENDING.
|
|
18
|
+
"""
|
|
19
|
+
from trueppm_api.apps.csvimport.models import CsvImportRequest, CsvImportStatus
|
|
20
|
+
from trueppm_api.apps.csvimport.tasks import import_csv
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
req = CsvImportRequest.objects.get(id=import_request_id, status=CsvImportStatus.PENDING)
|
|
24
|
+
except CsvImportRequest.DoesNotExist:
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
result = import_csv.delay(
|
|
29
|
+
project_id=str(req.project_id),
|
|
30
|
+
file_content_b64=req.file_content_b64,
|
|
31
|
+
filename=req.filename,
|
|
32
|
+
column_map=req.column_map,
|
|
33
|
+
date_order=req.date_order,
|
|
34
|
+
initiated_by_id=req.initiated_by_id,
|
|
35
|
+
import_request_id=str(req.id),
|
|
36
|
+
)
|
|
37
|
+
except Exception:
|
|
38
|
+
logger.warning(
|
|
39
|
+
"enqueue_csv_import: could not dispatch CsvImportRequest %s — drain will retry",
|
|
40
|
+
import_request_id,
|
|
41
|
+
)
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
CsvImportRequest.objects.filter(id=req.id, status=CsvImportStatus.PENDING).update(
|
|
45
|
+
status=CsvImportStatus.DISPATCHED,
|
|
46
|
+
celery_task_id=result.id,
|
|
47
|
+
dispatched_at=timezone.now(),
|
|
48
|
+
)
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"""Celery tasks for CSV / Excel import (mirrors jiraimport.tasks)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import logging
|
|
7
|
+
from collections.abc import Generator
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from datetime import timedelta
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
from celery import shared_task
|
|
13
|
+
|
|
14
|
+
from trueppm_api.core.idempotent import idempotent_task
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
# Type-only: the runtime import stays inside the task, where the rest of
|
|
18
|
+
# this module's Django/app imports are deferred for app-loading order.
|
|
19
|
+
from trueppm_api.apps.scheduling.graph_guard import InfeasibleGraphError
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
# A CSV import running longer than this is considered orphaned by the drain
|
|
24
|
+
# (slightly longer than the 9-minute soft limit so the task can record its own
|
|
25
|
+
# failure before the drain resets the row).
|
|
26
|
+
_IMPORT_ORPHAN_MINUTES = 15
|
|
27
|
+
|
|
28
|
+
# Rows inside an open transaction.on_commit() callback are invisible until the
|
|
29
|
+
# commit lands, so the drain skips rows younger than this to avoid racing an
|
|
30
|
+
# in-flight upload it would otherwise double-dispatch.
|
|
31
|
+
_DRAIN_MIN_AGE_MINUTES = 10
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _NoOpTracker:
|
|
35
|
+
"""Fallback when TaskRunTracker is not available."""
|
|
36
|
+
|
|
37
|
+
task_run_id = None
|
|
38
|
+
|
|
39
|
+
def update(self, pct: int, msg: str = "") -> None:
|
|
40
|
+
logger.info("import.csv progress: %d%% -- %s", pct, msg)
|
|
41
|
+
|
|
42
|
+
def set_result(self, result: dict[str, Any]) -> None:
|
|
43
|
+
logger.info("import.csv result: %s", result)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@contextmanager
|
|
47
|
+
def _get_tracker(
|
|
48
|
+
celery_task: Any,
|
|
49
|
+
project_id: str,
|
|
50
|
+
initiated_by_id: int | None,
|
|
51
|
+
) -> Generator[Any, None, None]:
|
|
52
|
+
"""Try to use TaskRunTracker; fall back to no-op if not available."""
|
|
53
|
+
try:
|
|
54
|
+
from trueppm_api.apps.taskruns.tracker import TaskRunTracker
|
|
55
|
+
|
|
56
|
+
with TaskRunTracker(
|
|
57
|
+
celery_task,
|
|
58
|
+
project_id=project_id,
|
|
59
|
+
task_name="import.csv",
|
|
60
|
+
initiated_by_id=initiated_by_id,
|
|
61
|
+
) as tracker:
|
|
62
|
+
yield tracker
|
|
63
|
+
except ImportError:
|
|
64
|
+
yield _NoOpTracker()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@shared_task( # type: ignore[untyped-decorator]
|
|
68
|
+
bind=True,
|
|
69
|
+
name="csv.import_file",
|
|
70
|
+
soft_time_limit=540,
|
|
71
|
+
time_limit=600,
|
|
72
|
+
acks_late=True,
|
|
73
|
+
reject_on_worker_lost=True,
|
|
74
|
+
)
|
|
75
|
+
def import_csv(
|
|
76
|
+
self: Any,
|
|
77
|
+
project_id: str,
|
|
78
|
+
file_content_b64: str,
|
|
79
|
+
filename: str,
|
|
80
|
+
column_map: dict[str, str] | None = None,
|
|
81
|
+
date_order: str = "auto",
|
|
82
|
+
initiated_by_id: int | None = None,
|
|
83
|
+
import_request_id: str | None = None,
|
|
84
|
+
) -> dict[str, Any]:
|
|
85
|
+
"""Parse an uploaded spreadsheet and import it into an existing project.
|
|
86
|
+
|
|
87
|
+
Parses offline, validates the derived dependency graph with the shared guard
|
|
88
|
+
**before** persisting anything, then reuses ``msproject.importer`` to
|
|
89
|
+
bulk-create the Task + Dependency network and triggers a CPM recalculation
|
|
90
|
+
through the scheduling outbox (ADR-0632).
|
|
91
|
+
|
|
92
|
+
A deterministic failure — an unreadable file, or a spreadsheet whose
|
|
93
|
+
predecessor column encodes a cycle — marks the outbox row DEAD so the drain
|
|
94
|
+
stops re-dispatching it forever. Any other unexpected exception raised while
|
|
95
|
+
persisting (e.g. a database error the parser's own row-level caps did not
|
|
96
|
+
anticipate) marks the row DEAD too, for the same reason (#2761). Row-level
|
|
97
|
+
problems (a bad date, an unknown predecessor) are **not** failures: those
|
|
98
|
+
rows import without the offending field and the errors ride back in the
|
|
99
|
+
summary. A row that cannot become a plan task at all is parked in the
|
|
100
|
+
parser's Import review branch (#2732) and imports through this same path —
|
|
101
|
+
there is no second write path for it, so the graph guard below still covers
|
|
102
|
+
every row that reaches the database.
|
|
103
|
+
"""
|
|
104
|
+
from django.conf import settings
|
|
105
|
+
|
|
106
|
+
from trueppm_api.apps.csvimport.parser import (
|
|
107
|
+
REVIEW_BRANCH_NAME,
|
|
108
|
+
CsvImportError,
|
|
109
|
+
parse_spreadsheet,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
with _get_tracker(self, project_id, initiated_by_id) as tracker:
|
|
113
|
+
file_content = base64.b64decode(file_content_b64)
|
|
114
|
+
tracker.update(5, f"Parsing {filename}...")
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
parsed = parse_spreadsheet(
|
|
118
|
+
file_content,
|
|
119
|
+
filename,
|
|
120
|
+
column_map=column_map or None,
|
|
121
|
+
date_order=date_order or "auto",
|
|
122
|
+
max_rows=settings.CSV_IMPORT_MAX_ROWS,
|
|
123
|
+
max_uncompressed_bytes=settings.CSV_IMPORT_MAX_UNCOMPRESSED_MB * 1024 * 1024,
|
|
124
|
+
)
|
|
125
|
+
except CsvImportError as exc:
|
|
126
|
+
# Unparseable input is deterministic: retrying the same bytes always
|
|
127
|
+
# fails. Mark the row DEAD (terminal) so the orphan drain stops, and
|
|
128
|
+
# record why so the launching Schedule can show it (#2151).
|
|
129
|
+
if import_request_id:
|
|
130
|
+
_mark_import_dead(import_request_id, str(exc))
|
|
131
|
+
raise
|
|
132
|
+
|
|
133
|
+
# Validate the derived dependency graph BEFORE any write. A spreadsheet's
|
|
134
|
+
# predecessor column is hand-maintained and routinely cyclic; persisting
|
|
135
|
+
# an infeasible network would crash the CPM engine on the next recalc.
|
|
136
|
+
# Validate in uid space — a relabeling of the eventual PKs — so nothing
|
|
137
|
+
# is created if the graph is bad.
|
|
138
|
+
from trueppm_api.apps.scheduling.graph_guard import (
|
|
139
|
+
InfeasibleGraphError,
|
|
140
|
+
validate_task_graph,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
edges = [
|
|
144
|
+
(str(link.predecessor_uid), str(task.uid))
|
|
145
|
+
for task in parsed.project_data.tasks
|
|
146
|
+
for link in task.predecessor_links
|
|
147
|
+
]
|
|
148
|
+
tracker.update(20, "Validating dependency graph...")
|
|
149
|
+
try:
|
|
150
|
+
validate_task_graph(edges)
|
|
151
|
+
except InfeasibleGraphError as exc:
|
|
152
|
+
if import_request_id:
|
|
153
|
+
_mark_import_dead(import_request_id, _describe_bad_graph(exc))
|
|
154
|
+
raise
|
|
155
|
+
|
|
156
|
+
from django.db import transaction
|
|
157
|
+
|
|
158
|
+
from trueppm_api.apps.msproject.importer import (
|
|
159
|
+
broadcast_import_project_record_change,
|
|
160
|
+
import_project,
|
|
161
|
+
)
|
|
162
|
+
from trueppm_api.apps.projects.models import TaskSource
|
|
163
|
+
|
|
164
|
+
# Make the additive CSV import idempotent under re-dispatch. import_project
|
|
165
|
+
# defaults to wipe_existing=False, so a second delivery would bulk-create
|
|
166
|
+
# the whole network again. The outbox row is the idempotency token: claim
|
|
167
|
+
# it (DISPATCHED -> DONE) and run the bulk_create in ONE transaction. A
|
|
168
|
+
# duplicate delivery — a drain re-dispatch after a worker-death window, or
|
|
169
|
+
# an acks_late / reject_on_worker_lost redelivery of an already completed
|
|
170
|
+
# message — finds no DISPATCHED row to claim and skips the import. The
|
|
171
|
+
# claim's row lock serializes any concurrent delivery; because claim and
|
|
172
|
+
# import share the transaction, a mid-import failure rolls the claim back
|
|
173
|
+
# too, returning the row to DISPATCHED for a clean retry.
|
|
174
|
+
summary: dict[str, Any]
|
|
175
|
+
try:
|
|
176
|
+
with transaction.atomic():
|
|
177
|
+
if import_request_id and not _claim_import(import_request_id):
|
|
178
|
+
logger.info(
|
|
179
|
+
"import.csv: request %s already completed — skipping duplicate delivery",
|
|
180
|
+
import_request_id,
|
|
181
|
+
)
|
|
182
|
+
return {"skipped": True, "tasks_created": 0}
|
|
183
|
+
# source_kind is passed explicitly because this path shares
|
|
184
|
+
# import_project with the MS Project importer (ADR-0786, #2730) — the
|
|
185
|
+
# default would label spreadsheet rows as an MS Project import, and
|
|
186
|
+
# the divergence digest would then attribute them to the wrong file.
|
|
187
|
+
summary = import_project(
|
|
188
|
+
project_id,
|
|
189
|
+
parsed.project_data,
|
|
190
|
+
tracker=tracker,
|
|
191
|
+
source_kind=TaskSource.CSV_IMPORT,
|
|
192
|
+
source_id=import_request_id or None,
|
|
193
|
+
)
|
|
194
|
+
if import_request_id:
|
|
195
|
+
# ADR-0810 (#2756): the ⌘Z undo ledger, in the same
|
|
196
|
+
# transaction as the write it records — the row already
|
|
197
|
+
# flipped DISPATCHED -> DONE above via _claim_import.
|
|
198
|
+
from trueppm_api.apps.projects.task_batch_services import (
|
|
199
|
+
finalize_import_fix_operation,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
finalize_import_fix_operation(
|
|
203
|
+
import_request_id, project_id, summary.get("created_task_ids") or []
|
|
204
|
+
)
|
|
205
|
+
except Exception as exc:
|
|
206
|
+
# Any unanticipated failure during persistence (e.g. a Postgres
|
|
207
|
+
# DataError from an oversized ltree path that slipped past the
|
|
208
|
+
# parser's own caps) must not leave the row DISPATCHED for the
|
|
209
|
+
# orphan-recovery drain to retry identically forever (#2761). The
|
|
210
|
+
# atomic() block above rolls the claim back to DISPATCHED on any
|
|
211
|
+
# exception, so a row is always here to flip terminal.
|
|
212
|
+
if import_request_id:
|
|
213
|
+
_mark_import_dead(import_request_id, f"Import failed unexpectedly: {exc}")
|
|
214
|
+
raise
|
|
215
|
+
|
|
216
|
+
summary["row_errors"] = [e.as_dict() for e in parsed.row_errors]
|
|
217
|
+
summary["row_error_count"] = len(parsed.row_errors)
|
|
218
|
+
summary["error_count"] = parsed.error_count
|
|
219
|
+
summary["warning_count"] = parsed.warning_count
|
|
220
|
+
summary["rows_read"] = parsed.total_rows
|
|
221
|
+
summary["rows_skipped"] = parsed.truncated_rows
|
|
222
|
+
summary["warnings"] = list(parsed.warnings)
|
|
223
|
+
summary["filename"] = filename
|
|
224
|
+
# Rows parked in the Import review branch rather than dropped (#2732).
|
|
225
|
+
# ``tasks_created`` counts every row written, review branch included, so
|
|
226
|
+
# the plan count is reported separately — "imported 11 tasks" must not
|
|
227
|
+
# silently include three placeholders the operator still has to fix.
|
|
228
|
+
summary["parked_row_count"] = len(parsed.unresolved_rows)
|
|
229
|
+
summary["review_branch_name"] = REVIEW_BRANCH_NAME if parsed.unresolved_rows else ""
|
|
230
|
+
summary["plan_tasks_created"] = max(
|
|
231
|
+
summary.get("tasks_created", 0) - parsed.review_task_count, 0
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if import_request_id:
|
|
235
|
+
_record_summary(import_request_id, summary)
|
|
236
|
+
tracker.set_result(summary)
|
|
237
|
+
|
|
238
|
+
# Deliberately `tasks_created`, not the `plan_tasks_created` written just
|
|
239
|
+
# above: the Import review branch is a committed write that live peers
|
|
240
|
+
# have to learn about, so this guard counts rows written rather than plan
|
|
241
|
+
# rows. Reading the plan count here would make an all-parked import
|
|
242
|
+
# silent again — the exact behavior #2732 exists to end.
|
|
243
|
+
if summary.get("tasks_created", 0) > 0:
|
|
244
|
+
# Trigger CPM via the outbox (survives a broker outage at this point)
|
|
245
|
+
# and emit tasks_restructured so live clients render the imported tree
|
|
246
|
+
# immediately, ahead of the async recalc's cpm_complete. Both run just
|
|
247
|
+
# after the import's atomic() has committed (no ambient transaction
|
|
248
|
+
# remains -> on_commit fires immediately), so peers never see the
|
|
249
|
+
# event for a rolled-back import.
|
|
250
|
+
from trueppm_api.apps.scheduling.services import enqueue_recalculate
|
|
251
|
+
from trueppm_api.apps.sync.broadcast import broadcast_board_event
|
|
252
|
+
|
|
253
|
+
enqueue_recalculate(project_id)
|
|
254
|
+
transaction.on_commit(
|
|
255
|
+
lambda: broadcast_board_event(project_id, "tasks_restructured", {})
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# An import can pull the project start back under an imported task
|
|
259
|
+
# (#867/#873). tasks_restructured does not invalidate the project record,
|
|
260
|
+
# so without this the boundary moves silently for everyone else on the
|
|
261
|
+
# project. Outside the tasks_created guard on purpose: the shift is a
|
|
262
|
+
# separate condition, and the helper is a no-op when nothing moved.
|
|
263
|
+
broadcast_import_project_record_change(project_id, summary)
|
|
264
|
+
|
|
265
|
+
return summary
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _claim_import(import_request_id: str) -> bool:
|
|
269
|
+
"""Atomically claim a DISPATCHED CsvImportRequest, flipping it to DONE.
|
|
270
|
+
|
|
271
|
+
The conditional ``UPDATE ... WHERE status=DISPATCHED`` is the exactly-once
|
|
272
|
+
idempotency token for the import: run inside the same transaction as the
|
|
273
|
+
bulk_create, the row transitions to DONE atomically with the imported
|
|
274
|
+
network. Returns ``True`` when this delivery won the claim (import),
|
|
275
|
+
``False`` when the row was already terminal — a duplicate delivery whose
|
|
276
|
+
import must be skipped so the additive path does not create the network
|
|
277
|
+
twice. Clears the payload (only needed for pre-terminal retry).
|
|
278
|
+
"""
|
|
279
|
+
from trueppm_api.apps.csvimport.models import CsvImportRequest, CsvImportStatus
|
|
280
|
+
|
|
281
|
+
claimed = CsvImportRequest.objects.filter(
|
|
282
|
+
id=import_request_id, status=CsvImportStatus.DISPATCHED
|
|
283
|
+
).update(status=CsvImportStatus.DONE, file_content_b64="")
|
|
284
|
+
return bool(claimed)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _uid_to_row(uid: str) -> str:
|
|
288
|
+
"""Render a parser task uid as the spreadsheet row the uploader can open.
|
|
289
|
+
|
|
290
|
+
``parse_spreadsheet`` numbers tasks ``uid = data_row_index + 1``, and the
|
|
291
|
+
header occupies row 1, so the row a person sees in Excel is ``uid + 1``.
|
|
292
|
+
Reporting the uid instead would be off by one against the file on their
|
|
293
|
+
screen. Non-numeric uids cannot arise from this parser, but the fallback
|
|
294
|
+
keeps a malformed id from turning a diagnostic into a crash.
|
|
295
|
+
"""
|
|
296
|
+
try:
|
|
297
|
+
return f"row {int(uid) + 1}"
|
|
298
|
+
except (TypeError, ValueError):
|
|
299
|
+
return f"task {uid}"
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _describe_bad_graph(exc: InfeasibleGraphError) -> str:
|
|
303
|
+
"""Explain a rejected dependency graph in terms of the uploaded file.
|
|
304
|
+
|
|
305
|
+
Built from ``exc.reason``/``exc.offending`` rather than ``str(exc)``: the
|
|
306
|
+
exception's own message is a domain signal ("Infeasible task graph
|
|
307
|
+
(cyclic_dependency): ['2', '5', '2']") carrying an internal reason code and
|
|
308
|
+
a list repr in uid space. The uploader needs the rows of their spreadsheet,
|
|
309
|
+
and needs the two reasons distinguished — a self-referencing row is a
|
|
310
|
+
different edit from a multi-row loop.
|
|
311
|
+
"""
|
|
312
|
+
rows = [_uid_to_row(uid) for uid in exc.offending]
|
|
313
|
+
if exc.reason == "self_reference" and rows:
|
|
314
|
+
return f"The Predecessors column on {rows[0]} lists that same row as its own predecessor."
|
|
315
|
+
if rows:
|
|
316
|
+
return (
|
|
317
|
+
"The Predecessors column forms a circular dependency: "
|
|
318
|
+
f"{' -> '.join(rows)}. Break the loop and re-upload."
|
|
319
|
+
)
|
|
320
|
+
return "The Predecessors column forms a circular dependency. Break the loop and re-upload."
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _record_summary(import_request_id: str, summary: dict[str, Any]) -> None:
|
|
324
|
+
"""Persist the terminal summary the launching Schedule polls for."""
|
|
325
|
+
from trueppm_api.apps.csvimport.models import CsvImportRequest
|
|
326
|
+
|
|
327
|
+
CsvImportRequest.objects.filter(id=import_request_id).update(result_summary=summary)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _mark_import_dead(import_request_id: str, detail: str = "") -> None:
|
|
331
|
+
"""Flip the row to DEAD after a terminal failure and clear the payload.
|
|
332
|
+
|
|
333
|
+
DEAD is excluded from orphan-drain recovery, so a deterministically bad file
|
|
334
|
+
(unreadable, or a cyclic predecessor column) is not re-dispatched forever.
|
|
335
|
+
The detail is persisted rather than only logged: an async import failure has
|
|
336
|
+
to be visible on the Schedule that launched it (#2151), and the summary is
|
|
337
|
+
the only surface that outlives the worker.
|
|
338
|
+
"""
|
|
339
|
+
from trueppm_api.apps.csvimport.models import CsvImportRequest, CsvImportStatus
|
|
340
|
+
|
|
341
|
+
CsvImportRequest.objects.filter(id=import_request_id).exclude(
|
|
342
|
+
status=CsvImportStatus.DONE
|
|
343
|
+
).update(
|
|
344
|
+
status=CsvImportStatus.DEAD,
|
|
345
|
+
file_content_b64="",
|
|
346
|
+
result_summary={"error": detail or "The file could not be imported."},
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@idempotent_task(
|
|
351
|
+
lock_key_template="drain_csv_import_queue",
|
|
352
|
+
lock_ttl=60,
|
|
353
|
+
on_contention="skip",
|
|
354
|
+
soft_time_limit=25,
|
|
355
|
+
time_limit=30,
|
|
356
|
+
acks_late=True,
|
|
357
|
+
reject_on_worker_lost=True,
|
|
358
|
+
name="csv.drain_import_queue",
|
|
359
|
+
)
|
|
360
|
+
def drain_csv_import_queue(self: object) -> None:
|
|
361
|
+
"""Dispatch pending CsvImportRequest rows; recover orphaned dispatched rows.
|
|
362
|
+
|
|
363
|
+
Runs every 30 seconds via Celery Beat. Recovers dispatched rows older than
|
|
364
|
+
``_IMPORT_ORPHAN_MINUTES`` (worker died mid-import).
|
|
365
|
+
"""
|
|
366
|
+
_do_csv_import_drain()
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _do_csv_import_drain() -> None:
|
|
370
|
+
"""Business logic for drain_csv_import_queue — extracted for testability."""
|
|
371
|
+
from django.utils import timezone
|
|
372
|
+
|
|
373
|
+
from trueppm_api.apps.csvimport.models import CsvImportRequest, CsvImportStatus
|
|
374
|
+
|
|
375
|
+
now = timezone.now()
|
|
376
|
+
orphan_cutoff = now - timedelta(minutes=_IMPORT_ORPHAN_MINUTES)
|
|
377
|
+
pending_cutoff = now - timedelta(minutes=_DRAIN_MIN_AGE_MINUTES)
|
|
378
|
+
|
|
379
|
+
recovered = CsvImportRequest.objects.filter(
|
|
380
|
+
status=CsvImportStatus.DISPATCHED,
|
|
381
|
+
dispatched_at__lt=orphan_cutoff,
|
|
382
|
+
).update(status=CsvImportStatus.PENDING, celery_task_id="")
|
|
383
|
+
if recovered:
|
|
384
|
+
logger.warning("drain_csv_import_queue: recovered %d orphaned row(s)", recovered)
|
|
385
|
+
|
|
386
|
+
pending = list(
|
|
387
|
+
CsvImportRequest.objects.filter(
|
|
388
|
+
status=CsvImportStatus.PENDING,
|
|
389
|
+
requested_at__lt=pending_cutoff,
|
|
390
|
+
)
|
|
391
|
+
)
|
|
392
|
+
dispatched = 0
|
|
393
|
+
for req in pending:
|
|
394
|
+
try:
|
|
395
|
+
result = import_csv.delay(
|
|
396
|
+
project_id=str(req.project_id),
|
|
397
|
+
file_content_b64=req.file_content_b64,
|
|
398
|
+
filename=req.filename,
|
|
399
|
+
column_map=req.column_map,
|
|
400
|
+
date_order=req.date_order,
|
|
401
|
+
initiated_by_id=req.initiated_by_id,
|
|
402
|
+
import_request_id=str(req.id),
|
|
403
|
+
)
|
|
404
|
+
except Exception:
|
|
405
|
+
logger.warning(
|
|
406
|
+
"drain_csv_import_queue: broker unavailable — CsvImportRequest %s stays pending",
|
|
407
|
+
req.id,
|
|
408
|
+
)
|
|
409
|
+
continue
|
|
410
|
+
CsvImportRequest.objects.filter(id=req.id, status=CsvImportStatus.PENDING).update(
|
|
411
|
+
status=CsvImportStatus.DISPATCHED,
|
|
412
|
+
celery_task_id=result.id,
|
|
413
|
+
dispatched_at=now,
|
|
414
|
+
)
|
|
415
|
+
dispatched += 1
|
|
416
|
+
|
|
417
|
+
if dispatched or recovered:
|
|
418
|
+
logger.info("drain_csv_import_queue: dispatched=%d recovered=%d", dispatched, recovered)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""The downloadable known-good CSV template (#743).
|
|
2
|
+
|
|
3
|
+
An operator with a messy spreadsheet needs a target shape, not a spec to read.
|
|
4
|
+
The template is deliberately a *worked example* rather than a bare header row:
|
|
5
|
+
the sample rows demonstrate WBS nesting, a lag on a predecessor, a milestone
|
|
6
|
+
with zero duration, and a comment row — the four things the alias table alone
|
|
7
|
+
does not communicate.
|
|
8
|
+
|
|
9
|
+
Header spellings here are chosen to be the exact-match aliases in
|
|
10
|
+
``mapping.py``, so a file built from this template maps with zero manual
|
|
11
|
+
correction. ``test_template_maps_cleanly`` asserts that, which is what keeps the
|
|
12
|
+
two files from drifting apart.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
TEMPLATE_FILENAME = "trueppm-import-template.csv"
|
|
18
|
+
|
|
19
|
+
CSV_TEMPLATE = """\
|
|
20
|
+
# TruePPM import template. Lines starting with '#' are ignored.
|
|
21
|
+
# Duration is in working days. Dates are YYYY-MM-DD.
|
|
22
|
+
# Predecessors reference the ID column: "3" is finish-to-start on task 3,
|
|
23
|
+
# "3FS+2d" adds 2 days of lag, and "5SS" starts with task 5.
|
|
24
|
+
# Labels accept several values in one cell, separated by , ; or / — and you can
|
|
25
|
+
# map more than one column to Labels if your sheet spreads them out.
|
|
26
|
+
ID,WBS,Name,Duration,Start,Finish,% Complete,Assignee,Predecessors,Labels,Notes
|
|
27
|
+
1,1,Discovery,,2026-09-01,,,,,,Phase
|
|
28
|
+
2,1.1,Stakeholder interviews,5,2026-09-01,,100,A. Rivera,,discovery,
|
|
29
|
+
3,1.2,Requirements draft,8,,,60,A. Rivera,2,discovery,
|
|
30
|
+
4,2,Build,,,,,,,,Phase
|
|
31
|
+
5,2.1,Data model,10,,,0,J. Chen,3,backend,
|
|
32
|
+
6,2.2,API endpoints,15,,,0,J. Chen,5SS,backend,Can start with the data model
|
|
33
|
+
7,2.3,Web UI,12,,,0,P. Osei,5,frontend,
|
|
34
|
+
8,3,Launch readiness,,,,,,,,Phase
|
|
35
|
+
9,3.1,Cutover rehearsal,2,,,0,P. Osei,"6,7","launch; ops",
|
|
36
|
+
10,3.2,Go live,0,,,0,,9FS+2d,launch,Milestone
|
|
37
|
+
"""
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""URL routing for CSV / Excel import."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from django.urls import path
|
|
6
|
+
|
|
7
|
+
from trueppm_api.apps.csvimport.views import (
|
|
8
|
+
CsvImportPreviewView,
|
|
9
|
+
CsvImportStatusView,
|
|
10
|
+
CsvImportTemplateView,
|
|
11
|
+
CsvImportUndoView,
|
|
12
|
+
CsvImportView,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
urlpatterns = [
|
|
16
|
+
# Declared before the <project_pk> routes and mounted ahead of the projects
|
|
17
|
+
# router so the literal "import-templates" segment is never captured as a
|
|
18
|
+
# project primary key.
|
|
19
|
+
path(
|
|
20
|
+
"import-templates/csv/",
|
|
21
|
+
CsvImportTemplateView.as_view(),
|
|
22
|
+
name="csv-import-template",
|
|
23
|
+
),
|
|
24
|
+
# Preview before import: declared first so "preview" is not swallowed by the
|
|
25
|
+
# commit route.
|
|
26
|
+
path(
|
|
27
|
+
"projects/<project_pk>/import/csv/preview/",
|
|
28
|
+
CsvImportPreviewView.as_view(),
|
|
29
|
+
name="project-import-csv-preview",
|
|
30
|
+
),
|
|
31
|
+
path(
|
|
32
|
+
"projects/<project_pk>/import/csv/",
|
|
33
|
+
CsvImportView.as_view(),
|
|
34
|
+
name="project-import-csv",
|
|
35
|
+
),
|
|
36
|
+
path(
|
|
37
|
+
"projects/<project_pk>/import/csv/<pk>/",
|
|
38
|
+
CsvImportStatusView.as_view(),
|
|
39
|
+
name="project-import-csv-status",
|
|
40
|
+
),
|
|
41
|
+
# Declared after the status route: "undo" would otherwise never be reached
|
|
42
|
+
# if a router matched it as a <pk> segment first, though plain path()
|
|
43
|
+
# entries don't have that ambiguity — kept last anyway for readability,
|
|
44
|
+
# status then action, mirroring the rest of this file's ordering.
|
|
45
|
+
path(
|
|
46
|
+
"projects/<project_pk>/import/csv/<pk>/undo/",
|
|
47
|
+
CsvImportUndoView.as_view(),
|
|
48
|
+
name="project-import-csv-undo",
|
|
49
|
+
),
|
|
50
|
+
]
|