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,514 @@
|
|
|
1
|
+
"""Load a bundled sample project (issue #375).
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
python manage.py load_sample_project [--sample <key>] [--owner <username>]
|
|
6
|
+
[--with-personas]
|
|
7
|
+
|
|
8
|
+
Imports a bundled sample seed (default: the Atlas hybrid-large launch demo) and
|
|
9
|
+
flags its projects as sample data. Idempotent — re-running replaces the sample.
|
|
10
|
+
|
|
11
|
+
``--with-personas`` additionally gives the sample's persona accounts a usable
|
|
12
|
+
login password so an evaluator can sign in *as* each persona (Alex the PM, Sarah
|
|
13
|
+
the sponsor, …) rather than only viewing their work. The password is resolved the
|
|
14
|
+
same way the demo seeds resolve it (#1350): ``TRUEPPM_DEMO_PASSWORD`` if set, else
|
|
15
|
+
``"demo"`` under ``DEBUG``, else a random token printed once — so a fixed weak
|
|
16
|
+
password can never silently reach a public instance. Without the flag the personas
|
|
17
|
+
are created with unusable passwords, exactly as before.
|
|
18
|
+
|
|
19
|
+
Because the load is idempotent, the flag applies to persona accounts a previous run
|
|
20
|
+
already created, not only to rows this run mints (#3484). It still never overwrites
|
|
21
|
+
a password a real user set, and never touches a staff/superuser account — and the
|
|
22
|
+
report lists only the accounts the printed password verifiably opens, naming any it
|
|
23
|
+
refused, so the command cannot claim a login state it did not produce.
|
|
24
|
+
|
|
25
|
+
Interactive demo mode (ADR-1197 D5, #3925) uses a *different* path. ``--with-personas``
|
|
26
|
+
is refused outright while ``settings.DEMO_READ_ONLY`` is on, because it passwords
|
|
27
|
+
**every** persona in the pack with one shared secret and its only refusals are staff
|
|
28
|
+
and superuser rows — a persona holding a project or program ``OWNER``/``ADMIN`` role
|
|
29
|
+
gets the password too, so publishing it would publish a Project Admin credential.
|
|
30
|
+
Instead, ``TRUEPPM_DEMO_LOGIN_USERNAME`` + ``TRUEPPM_DEMO_LOGIN_PASSWORD`` enable
|
|
31
|
+
exactly one named account, and only after proving that account holds no
|
|
32
|
+
``ADMIN``/``OWNER`` role anywhere and looks like a row this sample seeded rather
|
|
33
|
+
than a real user who happens to share the username.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
import os
|
|
39
|
+
import secrets
|
|
40
|
+
from typing import Any
|
|
41
|
+
|
|
42
|
+
from django.conf import settings
|
|
43
|
+
from django.contrib.auth import get_user_model
|
|
44
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
45
|
+
|
|
46
|
+
from trueppm_api.apps.access.models import ProgramMembership, ProjectMembership, Role
|
|
47
|
+
from trueppm_api.apps.projects.models import Project
|
|
48
|
+
from trueppm_api.apps.projects.seed.demo_landing_overlay import (
|
|
49
|
+
DemoOverlayFixtureMismatch,
|
|
50
|
+
apply_demo_landing_overlay,
|
|
51
|
+
)
|
|
52
|
+
from trueppm_api.apps.projects.seed.samples import (
|
|
53
|
+
DEFAULT_SAMPLE,
|
|
54
|
+
SAMPLES,
|
|
55
|
+
UnknownSampleError,
|
|
56
|
+
load_sample,
|
|
57
|
+
sample_accounts,
|
|
58
|
+
)
|
|
59
|
+
from trueppm_api.apps.workspace.models import WorkspaceRole
|
|
60
|
+
from trueppm_api.apps.workspace.permissions import workspace_role_for_user
|
|
61
|
+
|
|
62
|
+
User = get_user_model()
|
|
63
|
+
|
|
64
|
+
DEMO_PASSWORD_ENV = "TRUEPPM_DEMO_PASSWORD"
|
|
65
|
+
|
|
66
|
+
#: The interactive demo's single published login (#3925). Both must be set together.
|
|
67
|
+
#: The chart renders this pair onto the demo seed Job and the reset CronJob. The api
|
|
68
|
+
#: Deployment gets a different variable, ``TRUEPPM_DEMO_LOGIN_HINT``, whose value is
|
|
69
|
+
#: "<username> / <password>" — so the password *is* on the api pod, deliberately: it
|
|
70
|
+
#: is published on the login page by design (#3926 serves it from the unauthenticated
|
|
71
|
+
#: GET /api/v1/edition/). Nothing here is confidential; do not reason about it as if
|
|
72
|
+
#: it were.
|
|
73
|
+
DEMO_LOGIN_USERNAME_ENV = "TRUEPPM_DEMO_LOGIN_USERNAME"
|
|
74
|
+
DEMO_LOGIN_PASSWORD_ENV = "TRUEPPM_DEMO_LOGIN_PASSWORD"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Command(BaseCommand):
|
|
78
|
+
help = "Load a bundled sample project into the database."
|
|
79
|
+
|
|
80
|
+
def add_arguments(self, parser: Any) -> None:
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"--sample",
|
|
83
|
+
default=DEFAULT_SAMPLE,
|
|
84
|
+
help=f"Sample key to load. Known: {sorted(SAMPLES)}. Default: {DEFAULT_SAMPLE}.",
|
|
85
|
+
)
|
|
86
|
+
parser.add_argument(
|
|
87
|
+
"--owner",
|
|
88
|
+
help="Username to own the sample program. Defaults to the first superuser.",
|
|
89
|
+
)
|
|
90
|
+
parser.add_argument(
|
|
91
|
+
"--with-personas",
|
|
92
|
+
action="store_true",
|
|
93
|
+
help=(
|
|
94
|
+
"Give the sample's persona accounts a usable login password so you "
|
|
95
|
+
"can sign in as each one. Password: $TRUEPPM_DEMO_PASSWORD, else "
|
|
96
|
+
"'demo' under DEBUG, else a random token printed once."
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def handle(self, *args: Any, **options: Any) -> None:
|
|
101
|
+
sample_key = options["sample"]
|
|
102
|
+
with_personas = options["with_personas"]
|
|
103
|
+
|
|
104
|
+
# Fail BEFORE the import, not after (ADR-1197 D5). In demo read-only mode the
|
|
105
|
+
# seed Job's exit status is the Helm release's exit status, so refusing here
|
|
106
|
+
# fails the install rather than leaving a public instance whose every persona —
|
|
107
|
+
# including three that hold OWNER or ADMIN — shares one published password.
|
|
108
|
+
if with_personas and settings.DEMO_READ_ONLY:
|
|
109
|
+
raise CommandError(
|
|
110
|
+
"--with-personas is refused while TRUEPPM_DEMO_READ_ONLY is on. It gives "
|
|
111
|
+
"EVERY persona in the sample the same password and refuses only staff and "
|
|
112
|
+
"superuser rows, so a persona holding a project or program OWNER/ADMIN "
|
|
113
|
+
"role receives it too — publishing that password would publish a Project "
|
|
114
|
+
f"Admin credential. Enable exactly one non-privileged account instead, via "
|
|
115
|
+
f"{DEMO_LOGIN_USERNAME_ENV} + {DEMO_LOGIN_PASSWORD_ENV} (ADR-1197 D5)."
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Resolve the demo-login request before the import too, for the same reason:
|
|
119
|
+
# a typo in the username, or one half of the env pair, is a refusal that costs
|
|
120
|
+
# nothing to discover before any data is written. The checks that need the
|
|
121
|
+
# imported rows (privilege, provenance) necessarily run afterwards.
|
|
122
|
+
demo_login = self._resolve_demo_login_request(sample_key)
|
|
123
|
+
|
|
124
|
+
owner = self._resolve_owner(options.get("owner"), sample_key)
|
|
125
|
+
|
|
126
|
+
persona_password: str | None = None
|
|
127
|
+
password_source: str | None = None
|
|
128
|
+
if with_personas:
|
|
129
|
+
persona_password, password_source = self._resolve_demo_password()
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
program = load_sample(
|
|
133
|
+
sample_key, owner=owner, create_users=True, persona_password=persona_password
|
|
134
|
+
)
|
|
135
|
+
except UnknownSampleError as exc:
|
|
136
|
+
raise CommandError(str(exc)) from exc
|
|
137
|
+
|
|
138
|
+
count = Project.objects.filter(program=program).count()
|
|
139
|
+
self.stdout.write(
|
|
140
|
+
self.style.SUCCESS(
|
|
141
|
+
f"Loaded sample {program.name!r} with {count} project(s) (marked is_sample)."
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
if with_personas:
|
|
145
|
+
self._report_personas(sample_key, persona_password, password_source)
|
|
146
|
+
else:
|
|
147
|
+
self.stdout.write(
|
|
148
|
+
" Personas created with unusable passwords (view-only). "
|
|
149
|
+
"Re-run with --with-personas to enable persona logins."
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
self._enable_demo_login(program, demo_login)
|
|
153
|
+
self._apply_demo_landing_overlay(program, sample_key)
|
|
154
|
+
|
|
155
|
+
def _apply_demo_landing_overlay(self, program: Any, sample_key: str) -> None:
|
|
156
|
+
"""Make the landing project readable on a first visit (#4050 Part B).
|
|
157
|
+
|
|
158
|
+
Runs **only** on a read-only demo deployment, and **only** for the default
|
|
159
|
+
sample — the overlay is written against ``atlas-platform-launch.json``'s rows
|
|
160
|
+
and has nothing to say about another fixture.
|
|
161
|
+
|
|
162
|
+
Placed here, after the import and after the demo login is enabled, because
|
|
163
|
+
the overlay's last act is a Monte Carlo run that must outlive every other
|
|
164
|
+
write for the forecast not to read as stale (see the module docstring). It is
|
|
165
|
+
also why this is not folded into ``load_sample``: the overlay is a property of
|
|
166
|
+
one *deployment mode*, not of loading a sample.
|
|
167
|
+
|
|
168
|
+
A fixture mismatch is raised, not swallowed. The seed Job's exit status is the
|
|
169
|
+
Helm release's exit status, so a rename in the fixture fails the install rather
|
|
170
|
+
than publishing a demo that is half-fixed with nothing in the log saying which
|
|
171
|
+
half.
|
|
172
|
+
"""
|
|
173
|
+
if not settings.DEMO_READ_ONLY:
|
|
174
|
+
return
|
|
175
|
+
if sample_key != DEFAULT_SAMPLE:
|
|
176
|
+
self.stdout.write(
|
|
177
|
+
f" Demo landing overlay skipped: it is written against "
|
|
178
|
+
f"{DEFAULT_SAMPLE!r} and this run loaded {sample_key!r}."
|
|
179
|
+
)
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
result = apply_demo_landing_overlay(program)
|
|
184
|
+
except DemoOverlayFixtureMismatch as exc:
|
|
185
|
+
raise CommandError(
|
|
186
|
+
f"Demo landing overlay could not be applied: {exc} "
|
|
187
|
+
"(#4050 Part B — the overlay is fixture-coupled by design.)"
|
|
188
|
+
) from exc
|
|
189
|
+
|
|
190
|
+
if result.was_noop:
|
|
191
|
+
self.stdout.write(" Demo landing overlay: already applied; forecast re-run.")
|
|
192
|
+
else:
|
|
193
|
+
self.stdout.write(
|
|
194
|
+
self.style.SUCCESS(
|
|
195
|
+
" Demo landing overlay applied: health override cleared, "
|
|
196
|
+
f"milestone confirmed, {result.unscheduled_removed} unscheduled "
|
|
197
|
+
"row(s) trimmed."
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
self.stdout.write(
|
|
201
|
+
f" Behind-plan signal kept at WBS {result.behind_plan_task_wbs_path} "
|
|
202
|
+
f"(task {result.behind_plan_task_id})."
|
|
203
|
+
)
|
|
204
|
+
self.stdout.write(
|
|
205
|
+
f" Forecast P80 {result.p80.isoformat()} vs commitment "
|
|
206
|
+
f"{result.commitment_finish.isoformat()}."
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def _report_personas(self, sample_key: str, password: str | None, source: str | None) -> None:
|
|
210
|
+
"""Print the persona usernames the printed password actually opens.
|
|
211
|
+
|
|
212
|
+
This is the fix's payoff (#1760): the evaluation guide told readers to
|
|
213
|
+
"Sign in as Alex", but the seeded username is ``atlas-alex`` and the
|
|
214
|
+
password was unusable. Echo both so the walkthrough actually works.
|
|
215
|
+
|
|
216
|
+
The list is built by **checking each account against the password we are
|
|
217
|
+
about to print**, not by echoing the sample's ``accounts[]`` and trusting
|
|
218
|
+
the import to have honored it. That is the #3484 defect: the importer only
|
|
219
|
+
passworded rows it *created*, so every reload printed fifteen logins that
|
|
220
|
+
did not work. The importer now covers pre-existing persona rows as well,
|
|
221
|
+
but it still (correctly) refuses a real account that already has its own
|
|
222
|
+
password and any staff/superuser row — so the report must be able to say
|
|
223
|
+
so rather than assert a state the command did not produce.
|
|
224
|
+
"""
|
|
225
|
+
accounts = sample_accounts(sample_key)
|
|
226
|
+
usernames = [a.get("username", "") for a in accounts if a.get("username")]
|
|
227
|
+
by_username = {u.username: u for u in User.objects.filter(username__in=usernames)}
|
|
228
|
+
|
|
229
|
+
enabled: list[tuple[str, str]] = []
|
|
230
|
+
refused: list[tuple[str, str]] = []
|
|
231
|
+
for account in accounts:
|
|
232
|
+
username = account.get("username", "")
|
|
233
|
+
display = account.get("display_name", "")
|
|
234
|
+
user = by_username.get(username)
|
|
235
|
+
if user is not None and password is not None and user.check_password(password):
|
|
236
|
+
enabled.append((username, display))
|
|
237
|
+
else:
|
|
238
|
+
refused.append((username, self._persona_refusal_reason(user)))
|
|
239
|
+
|
|
240
|
+
# ``TRUEPPM_DEMO_PASSWORD`` (operator opt-in) and DEBUG's "demo" are safe to
|
|
241
|
+
# echo; a randomly generated production token is also printed once because
|
|
242
|
+
# there is no other way to recover it — but it is never a re-derivable value.
|
|
243
|
+
if source == "env":
|
|
244
|
+
detail = f"password set via {DEMO_PASSWORD_ENV}"
|
|
245
|
+
else:
|
|
246
|
+
detail = f"password={password!r}"
|
|
247
|
+
|
|
248
|
+
if enabled:
|
|
249
|
+
self.stdout.write(self.style.SUCCESS(f" Persona logins enabled ({detail}):"))
|
|
250
|
+
for username, display in enabled:
|
|
251
|
+
self.stdout.write(f" {username} ({display})")
|
|
252
|
+
else:
|
|
253
|
+
self.stdout.write(self.style.WARNING(" No persona logins were enabled."))
|
|
254
|
+
|
|
255
|
+
if refused:
|
|
256
|
+
self.stdout.write(
|
|
257
|
+
self.style.WARNING(
|
|
258
|
+
f" {len(refused)} persona account(s) left untouched — "
|
|
259
|
+
f"the password above does not open them:"
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
for username, reason in refused:
|
|
263
|
+
self.stdout.write(f" {username} ({reason})")
|
|
264
|
+
self.stdout.write(
|
|
265
|
+
" To take one of these over deliberately, set its password yourself: "
|
|
266
|
+
"python manage.py changepassword <username>"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
def _resolve_demo_login_request(self, sample_key: str) -> dict[str, str] | None:
|
|
270
|
+
"""Validate the demo-login env pair before anything is written (#3925).
|
|
271
|
+
|
|
272
|
+
Everything checkable without the imported rows is checked here, so a typo or a
|
|
273
|
+
half-set pair costs a refusal rather than a seeded-then-failed install. The
|
|
274
|
+
checks that genuinely need the rows — privilege and provenance — cannot move.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
The resolved request, or ``None`` when neither variable is set.
|
|
278
|
+
"""
|
|
279
|
+
username = (os.environ.get(DEMO_LOGIN_USERNAME_ENV) or "").strip()
|
|
280
|
+
password = os.environ.get(DEMO_LOGIN_PASSWORD_ENV) or ""
|
|
281
|
+
if not username and not password:
|
|
282
|
+
return None
|
|
283
|
+
if not username or not password:
|
|
284
|
+
raise CommandError(
|
|
285
|
+
f"{DEMO_LOGIN_USERNAME_ENV} and {DEMO_LOGIN_PASSWORD_ENV} must be set "
|
|
286
|
+
"together — one without the other cannot open an account, and a demo "
|
|
287
|
+
"that publishes a login hint nobody can use is a broken demo."
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# The invariant lives HERE, not only in the Helm guard that has the same rule.
|
|
291
|
+
# The chart is one caller; `kubectl exec ... manage.py load_sample_project`,
|
|
292
|
+
# `docker compose run` and a CI seed step are not covered by a render-time
|
|
293
|
+
# check, and on a writable instance this account's MEMBER role plus an open
|
|
294
|
+
# `POST /projects/` is a self-granted OWNER.
|
|
295
|
+
if not settings.DEMO_READ_ONLY:
|
|
296
|
+
raise CommandError(
|
|
297
|
+
f"{DEMO_LOGIN_USERNAME_ENV} is set but TRUEPPM_DEMO_READ_ONLY is not on. "
|
|
298
|
+
"That would publish a working login to a WRITABLE instance, which is "
|
|
299
|
+
"worse than publishing none: any authenticated user may POST /projects/ "
|
|
300
|
+
"and is made its OWNER. Arm the read-only fence, or unset the pair."
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
account = next(
|
|
304
|
+
(a for a in sample_accounts(sample_key) if a.get("username") == username), None
|
|
305
|
+
)
|
|
306
|
+
if account is None:
|
|
307
|
+
raise CommandError(
|
|
308
|
+
f"{DEMO_LOGIN_USERNAME_ENV}={username!r} is not one of sample "
|
|
309
|
+
f"{sample_key!r}'s accounts. Only an account the sample itself seeds may "
|
|
310
|
+
"be enabled: anything else is a pre-existing real user whose password "
|
|
311
|
+
"this command must never set."
|
|
312
|
+
)
|
|
313
|
+
return {
|
|
314
|
+
"username": username,
|
|
315
|
+
"password": password,
|
|
316
|
+
"email": account.get("email", "") or "",
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
def _enable_demo_login(self, program: Any, request: dict[str, str] | None) -> None:
|
|
320
|
+
"""Give ONE named sample account a usable password (ADR-1197 D5, #3925).
|
|
321
|
+
|
|
322
|
+
This is the interactive demo's published credential, so every failure here is
|
|
323
|
+
a ``CommandError``: the seed Job is a Helm hook, so raising fails the release
|
|
324
|
+
rather than publishing a login hint for an account that does not exist, or --
|
|
325
|
+
worse -- quietly passwording an account that is not ours to password.
|
|
326
|
+
|
|
327
|
+
Two questions have to be answered, and the sample pack can answer neither:
|
|
328
|
+
|
|
329
|
+
**Is it privileged?** Read the membership rows this import produced, not the
|
|
330
|
+
pack's declared roles -- the roster in the JSON is a claim about the seed, the
|
|
331
|
+
rows are what the API enforces. The query is deliberately NOT scoped to this
|
|
332
|
+
program: a role held somewhere else is exactly as published as a role held
|
|
333
|
+
here, and scoping the check to one program is how it would miss that.
|
|
334
|
+
|
|
335
|
+
**Is it ours?** ``SeedImporter._resolve_accounts`` binds an account by bare
|
|
336
|
+
username, so a pre-existing real user who happens to hold a sample username is
|
|
337
|
+
adopted by the import. ``has_usable_password()`` -- the guard the persona path
|
|
338
|
+
uses -- cannot be reused here, because the scheduled reset must re-apply the
|
|
339
|
+
password on every run and a rotated credential must be able to land. Provenance
|
|
340
|
+
is asserted instead: a seeded row carries the pack's own email and holds
|
|
341
|
+
memberships in this program and nowhere else.
|
|
342
|
+
|
|
343
|
+
Residual, stated rather than hidden: a pre-existing account that shares the
|
|
344
|
+
sample's username AND its ``@atlas.example`` email AND holds no membership
|
|
345
|
+
outside this program would still be passworded. Never point
|
|
346
|
+
``demo.loginHint.username`` at an instance that has real users.
|
|
347
|
+
"""
|
|
348
|
+
if request is None:
|
|
349
|
+
return
|
|
350
|
+
username = request["username"]
|
|
351
|
+
password = request["password"]
|
|
352
|
+
|
|
353
|
+
user = User.objects.filter(username=username).first()
|
|
354
|
+
if user is None:
|
|
355
|
+
raise CommandError(
|
|
356
|
+
f"{DEMO_LOGIN_USERNAME_ENV}={username!r} names a sample account that does "
|
|
357
|
+
"not exist after the load. The seed did not produce it — refusing to "
|
|
358
|
+
"report a demo login that cannot work."
|
|
359
|
+
)
|
|
360
|
+
if user.is_staff or user.is_superuser:
|
|
361
|
+
raise CommandError(
|
|
362
|
+
f"Refusing to set a published password on {username!r}: it is a staff or "
|
|
363
|
+
"superuser account."
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
if (
|
|
367
|
+
ProjectMembership.objects.filter(
|
|
368
|
+
user=user, is_deleted=False, role__gte=Role.ADMIN
|
|
369
|
+
).exists()
|
|
370
|
+
or ProgramMembership.objects.filter(
|
|
371
|
+
user=user, is_deleted=False, role__gte=Role.ADMIN
|
|
372
|
+
).exists()
|
|
373
|
+
):
|
|
374
|
+
raise CommandError(
|
|
375
|
+
f"Refusing to set a published password on {username!r}: it holds an "
|
|
376
|
+
"ADMIN or OWNER role on some project or program. The interactive demo's "
|
|
377
|
+
"credential is public, and the read-only fence is a deployment property "
|
|
378
|
+
"that a future misconfiguration can remove — a published Project Admin "
|
|
379
|
+
"login would then be a full write credential (ADR-1197 D5)."
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# The third privilege axis, and the one a project/program sweep cannot see.
|
|
383
|
+
# A seeded persona holds no WorkspaceMembership row and therefore resolves to
|
|
384
|
+
# the implicit MEMBER, so the bundled pack passes — but an operator who granted
|
|
385
|
+
# this account workspace ADMIN on a long-lived demo instance has handed it the
|
|
386
|
+
# exfil and irreversible surfaces behind IsWorkspaceAdminStrict, and neither
|
|
387
|
+
# membership query above would notice.
|
|
388
|
+
workspace_role = workspace_role_for_user(user)
|
|
389
|
+
if workspace_role is not None and workspace_role >= WorkspaceRole.ADMIN:
|
|
390
|
+
raise CommandError(
|
|
391
|
+
f"Refusing to set a published password on {username!r}: it holds "
|
|
392
|
+
"workspace ADMIN or higher, which gates the export and irreversible "
|
|
393
|
+
"surfaces regardless of any project role (ADR-1197 D5)."
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
foreign = (
|
|
397
|
+
ProjectMembership.objects.filter(user=user, is_deleted=False)
|
|
398
|
+
.exclude(project__program=program)
|
|
399
|
+
.exists()
|
|
400
|
+
or ProgramMembership.objects.filter(user=user, is_deleted=False)
|
|
401
|
+
.exclude(program=program)
|
|
402
|
+
.exists()
|
|
403
|
+
)
|
|
404
|
+
if foreign or (request["email"] and user.email != request["email"]):
|
|
405
|
+
raise CommandError(
|
|
406
|
+
f"Refusing to set a published password on {username!r}: it does not look "
|
|
407
|
+
"like an account this sample seeded (it holds membership outside this "
|
|
408
|
+
"program, or its email is not the pack's). A real user who happens to "
|
|
409
|
+
"share a sample username must never have their password replaced by a "
|
|
410
|
+
"published one."
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
if user.check_password(password):
|
|
414
|
+
# Already correct — a reset re-running the identical seed. Skip the write
|
|
415
|
+
# so a no-op run does not churn the password hash.
|
|
416
|
+
self.stdout.write(f" Interactive demo login for {username!r} already current.")
|
|
417
|
+
return
|
|
418
|
+
|
|
419
|
+
# A seeded persona, not an interactive signup, so the password validators that
|
|
420
|
+
# govern real sign-ups do not apply; the value is an operator's own choice.
|
|
421
|
+
# nosemgrep: unvalidated-password
|
|
422
|
+
user.set_password(password)
|
|
423
|
+
user.save(update_fields=["password"])
|
|
424
|
+
self.stdout.write(
|
|
425
|
+
self.style.SUCCESS(
|
|
426
|
+
f" Interactive demo login enabled for {username!r} "
|
|
427
|
+
"(password from the environment; not echoed)."
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
@staticmethod
|
|
432
|
+
def _persona_refusal_reason(user: Any) -> str:
|
|
433
|
+
"""Why a persona account is not openable with the password just printed."""
|
|
434
|
+
if user is None:
|
|
435
|
+
return "no such account"
|
|
436
|
+
if user.is_staff or user.is_superuser:
|
|
437
|
+
return "privileged account — never re-passworded"
|
|
438
|
+
if user.has_usable_password():
|
|
439
|
+
return "pre-existing account with its own password"
|
|
440
|
+
return "password not applied"
|
|
441
|
+
|
|
442
|
+
def _resolve_demo_password(self) -> tuple[str, str]:
|
|
443
|
+
"""Resolve the persona login password and its source (mirrors #1350).
|
|
444
|
+
|
|
445
|
+
A fixed weak password must never reach a public (non-DEBUG) instance.
|
|
446
|
+
Resolution order: ``TRUEPPM_DEMO_PASSWORD`` env var, then ``"demo"`` under
|
|
447
|
+
``DEBUG``, else a random token printed once so the operator can record it.
|
|
448
|
+
"""
|
|
449
|
+
env_password = os.environ.get(DEMO_PASSWORD_ENV)
|
|
450
|
+
if env_password:
|
|
451
|
+
return env_password, "env"
|
|
452
|
+
if settings.DEBUG:
|
|
453
|
+
return "demo", "debug"
|
|
454
|
+
return secrets.token_urlsafe(16), "generated"
|
|
455
|
+
|
|
456
|
+
def _resolve_owner(self, username: str | None, sample_key: str) -> Any:
|
|
457
|
+
"""Resolve the program owner: explicit user, then superuser, then the sample's own.
|
|
458
|
+
|
|
459
|
+
The third fallback exists for the hosted demo (#3098). ``seed_demo_project``
|
|
460
|
+
built its own persona accounts and owned the program with one of them, so
|
|
461
|
+
the demo deployment never created a superuser — and it must not, because
|
|
462
|
+
the read-only posture (ADR-0658) rests on there being no login-capable
|
|
463
|
+
account at all. Requiring a superuser here would have made the demo stack
|
|
464
|
+
fail to boot the moment it swapped to this command.
|
|
465
|
+
|
|
466
|
+
The account is created with an **unusable** password, exactly like every
|
|
467
|
+
other persona this command creates without ``--with-personas``, so the
|
|
468
|
+
zero-login posture is preserved.
|
|
469
|
+
"""
|
|
470
|
+
if username:
|
|
471
|
+
owner = User.objects.filter(username=username).first()
|
|
472
|
+
if owner is None:
|
|
473
|
+
raise CommandError(f"No user with username {username!r}.")
|
|
474
|
+
return owner
|
|
475
|
+
owner = User.objects.filter(is_superuser=True).order_by("pk").first()
|
|
476
|
+
if owner is not None:
|
|
477
|
+
return owner
|
|
478
|
+
|
|
479
|
+
try:
|
|
480
|
+
accounts = sample_accounts(sample_key)
|
|
481
|
+
except UnknownSampleError as exc:
|
|
482
|
+
raise CommandError(str(exc)) from exc
|
|
483
|
+
lead = next(
|
|
484
|
+
(a for a in accounts if a.get("role") == "OWNER"),
|
|
485
|
+
accounts[0] if accounts else None,
|
|
486
|
+
)
|
|
487
|
+
if lead is None:
|
|
488
|
+
raise CommandError(
|
|
489
|
+
"No superuser to own the sample and the sample declares no accounts. "
|
|
490
|
+
"Pass --owner <username> or create a superuser."
|
|
491
|
+
)
|
|
492
|
+
owner, created = User.objects.get_or_create(
|
|
493
|
+
username=lead["username"],
|
|
494
|
+
defaults={
|
|
495
|
+
"email": lead.get("email", ""),
|
|
496
|
+
"first_name": (lead.get("display_name") or "").split(" ")[0],
|
|
497
|
+
"last_name": " ".join((lead.get("display_name") or "").split(" ")[1:]),
|
|
498
|
+
},
|
|
499
|
+
)
|
|
500
|
+
if created:
|
|
501
|
+
# Unusable password (a sentinel hash, not a credential) — this is a
|
|
502
|
+
# seeded persona, not an interactive signup, so validators do not apply.
|
|
503
|
+
# nosemgrep: unvalidated-password
|
|
504
|
+
owner.set_password(None)
|
|
505
|
+
owner.save(update_fields=["password"])
|
|
506
|
+
# Read the state back rather than asserting the create branch's intent: on a
|
|
507
|
+
# reload the row already exists and this never touched it, so hard-coding
|
|
508
|
+
# "(unusable password)" was the same false claim as #3484's persona report.
|
|
509
|
+
state = "usable password" if owner.has_usable_password() else "unusable password"
|
|
510
|
+
self.stdout.write(
|
|
511
|
+
f"No superuser found; owning the sample with its own persona "
|
|
512
|
+
f"{lead['username']!r} ({state})."
|
|
513
|
+
)
|
|
514
|
+
return owner
|