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,276 @@
|
|
|
1
|
+
"""Per-user app-preference models (ADR-0129).
|
|
2
|
+
|
|
3
|
+
This app holds personal *application* preferences — settings that shape how the
|
|
4
|
+
app behaves *for one user*, not collaborative domain data. The first such
|
|
5
|
+
preference is :attr:`UserProfile.default_landing`, which feeds the role-based
|
|
6
|
+
app front door (the screen the user lands on at login / on hitting ``/``).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import uuid
|
|
12
|
+
|
|
13
|
+
from django.conf import settings
|
|
14
|
+
from django.db import models
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DefaultLanding(models.TextChoices):
|
|
18
|
+
"""Where the app opens for this user.
|
|
19
|
+
|
|
20
|
+
``AUTO`` is the sentinel meaning "use the role-based policy" (the default for
|
|
21
|
+
every user). The three concrete values pin the front door to one surface
|
|
22
|
+
regardless of role; ``PORTFOLIO`` only resolves to a real path when the
|
|
23
|
+
running edition is Enterprise *and* the user is portfolio-entitled, and
|
|
24
|
+
otherwise degrades cleanly to My Work (see ``services.resolve_landing``).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
AUTO = "auto", "Automatic (based on your role)"
|
|
28
|
+
MY_WORK = "my_work", "My Work"
|
|
29
|
+
PROJECT_OVERVIEW = "project_overview", "Project Overview"
|
|
30
|
+
PORTFOLIO = "portfolio", "Portfolio"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class RoleContext(models.TextChoices):
|
|
34
|
+
"""Which role-context "lens" the app presents for this user (#412).
|
|
35
|
+
|
|
36
|
+
A person who wears two hats (e.g. a delivery lead who is both PM and Scrum
|
|
37
|
+
Master) can pin the workspace to a single role's layout, or keep the dual-hat
|
|
38
|
+
``UNIFIED`` view that blends both. This stores the *active* lens; the concrete
|
|
39
|
+
mode-layouts that consume it are a deferred frontend follow-up.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
PM = "pm", "PM"
|
|
43
|
+
SCRUM_MASTER = "scrum_master", "Scrum Master"
|
|
44
|
+
UNIFIED = "unified", "Unified Today"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DateFormat(models.TextChoices):
|
|
48
|
+
"""Date-format *style* for displayed dates (#1953, ADR-0410).
|
|
49
|
+
|
|
50
|
+
A display-only styling preference — it never re-timezones a value, so it
|
|
51
|
+
applies to *every* displayed date (instant timestamps and UTC-pinned calendar
|
|
52
|
+
dates alike). ``AUTO`` follows the viewer's browser locale (resolved
|
|
53
|
+
client-side); the three explicit styles pin a recognizable order. Server-side
|
|
54
|
+
this is opaque — the API always emits aware-UTC ISO-8601 and localization is a
|
|
55
|
+
pure client concern.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
AUTO = "auto", "Automatic (based on your locale)"
|
|
59
|
+
ISO = "iso", "2026-07-14" # YYYY-MM-DD
|
|
60
|
+
US = "us", "Jul 14, 2026" # MMM D, YYYY
|
|
61
|
+
EU = "eu", "14 Jul 2026" # D MMM YYYY
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class UserProfile(models.Model):
|
|
65
|
+
"""Singleton per-user app preferences.
|
|
66
|
+
|
|
67
|
+
Deliberately **not** a ``VersionedModel``: this is a personal app preference,
|
|
68
|
+
not a board-scoped collaborative entity. It carries no ``server_version``,
|
|
69
|
+
never participates in the offline delta/sync protocol, and is never broadcast
|
|
70
|
+
over WebSockets — it is read only through ``/auth/me/`` and written only
|
|
71
|
+
through ``PATCH /auth/me/profile/``. Keeping it out of sync avoids polluting
|
|
72
|
+
the WatermelonDB schema with a non-collaborative singleton.
|
|
73
|
+
|
|
74
|
+
Rows are created lazily (``get_or_create``) on first read/write, so existing
|
|
75
|
+
users need no backfill — the absence of a row is read as ``default_landing``
|
|
76
|
+
== ``AUTO``.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
80
|
+
user = models.OneToOneField(
|
|
81
|
+
settings.AUTH_USER_MODEL,
|
|
82
|
+
on_delete=models.CASCADE,
|
|
83
|
+
related_name="profile",
|
|
84
|
+
)
|
|
85
|
+
default_landing = models.CharField(
|
|
86
|
+
max_length=20,
|
|
87
|
+
choices=DefaultLanding.choices,
|
|
88
|
+
default=DefaultLanding.AUTO,
|
|
89
|
+
help_text="Which screen the app opens on. 'auto' uses the role-based policy.",
|
|
90
|
+
)
|
|
91
|
+
# 'unified' is the default because the dual-hat "Unified Today" view is the
|
|
92
|
+
# headline demo for #412: a user who has not yet picked a lens should see the
|
|
93
|
+
# blended PM + Scrum Master surface, not be forced into one role up front.
|
|
94
|
+
role_context = models.CharField(
|
|
95
|
+
max_length=12,
|
|
96
|
+
choices=RoleContext.choices,
|
|
97
|
+
default=RoleContext.UNIFIED,
|
|
98
|
+
help_text="Active role-context view: PM, Scrum Master, or Unified Today (#412).",
|
|
99
|
+
)
|
|
100
|
+
# Per-user view visibility (ADR-0139). Canonical view keys this user has
|
|
101
|
+
# hidden from their own project nav — applied globally across every project,
|
|
102
|
+
# layered on top of the per-project methodology preset (ADR-0041). Empty =
|
|
103
|
+
# methodology default only; 'overview' is never hideable.
|
|
104
|
+
hidden_views = models.JSONField(default=list, blank=True, help_text="Hidden nav view keys.")
|
|
105
|
+
# Personal display frame (#1953, ADR-0410). Globally-dispersed teams: each user
|
|
106
|
+
# picks the zone their *instant* timestamps (activity/comments/relative times)
|
|
107
|
+
# render in, and the *style* every displayed date reads in. Both are
|
|
108
|
+
# display-only — the API stays aware-UTC ISO-8601 and localization happens only
|
|
109
|
+
# in the client. 'auto' is the zero-config default: it resolves client-side to
|
|
110
|
+
# the browser's detected zone / locale, so display is correct on first load with
|
|
111
|
+
# no write. The two preferences have DIFFERENT scope: timezone re-clocks only
|
|
112
|
+
# instants (re-timezoning a calendar date would shift the displayed day per
|
|
113
|
+
# viewer — the ADR-0144 bug), whereas date_format restyles *all* dates (a style
|
|
114
|
+
# change is timezone-independent and never moves a day).
|
|
115
|
+
timezone = models.CharField(
|
|
116
|
+
max_length=64,
|
|
117
|
+
default="auto",
|
|
118
|
+
help_text=(
|
|
119
|
+
"IANA timezone for displaying instant timestamps, or 'auto' to use the "
|
|
120
|
+
"browser's detected zone. Display-only; API datetimes stay UTC."
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
date_format = models.CharField(
|
|
124
|
+
max_length=8,
|
|
125
|
+
choices=DateFormat.choices,
|
|
126
|
+
default=DateFormat.AUTO,
|
|
127
|
+
help_text="Date-format style for all displayed dates. 'auto' follows browser locale.",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
class Meta:
|
|
131
|
+
verbose_name = "user profile"
|
|
132
|
+
verbose_name_plural = "user profiles"
|
|
133
|
+
|
|
134
|
+
def __str__(self) -> str:
|
|
135
|
+
return f"UserProfile({self.user_id}, default_landing={self.default_landing})"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class ProjectVisit(models.Model):
|
|
139
|
+
"""Per-user "last visited" timestamp for a project (ADR-0150).
|
|
140
|
+
|
|
141
|
+
One row per ``(user, project)``, upserted on each visit, recording when the
|
|
142
|
+
user last opened the project. This replaces the membership-``server_version``
|
|
143
|
+
proxy that ``services.most_recent_project`` used to pick a PM's landing
|
|
144
|
+
project (ADR-0129 flagged the proxy as inadequate: a membership's version
|
|
145
|
+
advances on role edits, not on actual navigation).
|
|
146
|
+
|
|
147
|
+
Like :class:`UserProfile` this is private per-user navigation telemetry —
|
|
148
|
+
plain ``models.Model`` (no ``server_version``), never synced to mobile, never
|
|
149
|
+
broadcast. The upsert-in-place shape keeps the table bounded by the user's
|
|
150
|
+
membership count, and the ``(user, -visited_at)`` index serves both the
|
|
151
|
+
resolver's "my most recent project" lookup and a forward-compatible
|
|
152
|
+
"recently viewed projects" switcher without a schema change.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
156
|
+
user = models.ForeignKey(
|
|
157
|
+
settings.AUTH_USER_MODEL,
|
|
158
|
+
on_delete=models.CASCADE,
|
|
159
|
+
related_name="project_visits",
|
|
160
|
+
)
|
|
161
|
+
project = models.ForeignKey(
|
|
162
|
+
"projects.Project",
|
|
163
|
+
on_delete=models.CASCADE,
|
|
164
|
+
related_name="visits",
|
|
165
|
+
)
|
|
166
|
+
visited_at = models.DateTimeField(help_text="When the user last opened this project.")
|
|
167
|
+
|
|
168
|
+
class Meta:
|
|
169
|
+
verbose_name = "project visit"
|
|
170
|
+
verbose_name_plural = "project visits"
|
|
171
|
+
constraints = [
|
|
172
|
+
models.UniqueConstraint(
|
|
173
|
+
fields=["user", "project"],
|
|
174
|
+
name="uq_project_visit_user_project",
|
|
175
|
+
),
|
|
176
|
+
]
|
|
177
|
+
indexes = [
|
|
178
|
+
models.Index(fields=["user", "-visited_at"], name="projectvisit_user_recent_idx"),
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
def __str__(self) -> str:
|
|
182
|
+
return f"ProjectVisit({self.user_id} → {self.project_id} @ {self.visited_at:%Y-%m-%d})"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class UserPin(models.Model):
|
|
186
|
+
"""A project or program one user has pinned for quick access (#2390, ADR-0627).
|
|
187
|
+
|
|
188
|
+
A pin is **private to its owner**. It is a personal wayfinding marker, not
|
|
189
|
+
collaborative domain data and not a signal about the pinned entity — nothing
|
|
190
|
+
in the API answers "who pinned this" or "how many people pinned this", and
|
|
191
|
+
:attr:`pinned_at` is exposed only on the owner's own ``GET /me/pinned/``.
|
|
192
|
+
That restraint is deliberate: a per-user marker readable by a PM or admin
|
|
193
|
+
becomes an engagement metric by accretion ("she hasn't pinned the client
|
|
194
|
+
project in weeks"), which is precisely the surveillance surface ADR-0627 §D5
|
|
195
|
+
exists to foreclose.
|
|
196
|
+
|
|
197
|
+
**Naming caveat (ADR-0627 §D1.1).** ``pinned`` carries the *opposite* privacy
|
|
198
|
+
semantics elsewhere in this same schema: ``TaskNote.pinned`` and
|
|
199
|
+
``TaskAttachment.is_pinned`` are **shared** curation any project writer may
|
|
200
|
+
toggle. The two meanings never meet on one resource, and this one is enforced
|
|
201
|
+
owner-only, but do not reason by analogy from those fields when extending
|
|
202
|
+
this model — adding ``pinned_by`` or ``pin_count`` here would be a privacy
|
|
203
|
+
regression, and requires a superseding ADR rather than a convention appeal.
|
|
204
|
+
|
|
205
|
+
Exactly one of :attr:`project` / :attr:`program` is set, enforced by a
|
|
206
|
+
database CheckConstraint. This mirrors the explicit-nullable-FK pattern used
|
|
207
|
+
by ``Webhook`` and ``ApiToken`` (ADR-0076) rather than a GenericForeignKey,
|
|
208
|
+
which this codebase does not use anywhere (ADR-0075).
|
|
209
|
+
|
|
210
|
+
Deliberately **not** part of the offline sync delta (ADR-0142): the delta is
|
|
211
|
+
project-scoped while a pin is user-scoped and spans both entity kinds, the
|
|
212
|
+
same reasoning that kept ``UserProfile`` and ``ProjectVisit`` out. Unpinning
|
|
213
|
+
is a hard delete — there is no tombstone, and therefore no unpin *history* to
|
|
214
|
+
mine.
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
218
|
+
user = models.ForeignKey(
|
|
219
|
+
settings.AUTH_USER_MODEL,
|
|
220
|
+
on_delete=models.CASCADE,
|
|
221
|
+
related_name="pins",
|
|
222
|
+
)
|
|
223
|
+
project = models.ForeignKey(
|
|
224
|
+
"projects.Project",
|
|
225
|
+
on_delete=models.CASCADE,
|
|
226
|
+
related_name="pins",
|
|
227
|
+
null=True,
|
|
228
|
+
blank=True,
|
|
229
|
+
)
|
|
230
|
+
program = models.ForeignKey(
|
|
231
|
+
"projects.Program",
|
|
232
|
+
on_delete=models.CASCADE,
|
|
233
|
+
related_name="pins",
|
|
234
|
+
null=True,
|
|
235
|
+
blank=True,
|
|
236
|
+
)
|
|
237
|
+
pinned_at = models.DateTimeField(
|
|
238
|
+
auto_now_add=True,
|
|
239
|
+
help_text="When the user pinned this item. Never exposed on a shared payload.",
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
class Meta:
|
|
243
|
+
verbose_name = "user pin"
|
|
244
|
+
verbose_name_plural = "user pins"
|
|
245
|
+
constraints = [
|
|
246
|
+
models.CheckConstraint(
|
|
247
|
+
# Exactly one target. A row with both or neither is meaningless
|
|
248
|
+
# and would make the `is_pinned` annotation ambiguous.
|
|
249
|
+
condition=(
|
|
250
|
+
models.Q(project__isnull=False, program__isnull=True)
|
|
251
|
+
| models.Q(project__isnull=True, program__isnull=False)
|
|
252
|
+
),
|
|
253
|
+
name="ck_userpin_project_xor_program",
|
|
254
|
+
),
|
|
255
|
+
# Partial uniques rather than one composite: a NULL target column
|
|
256
|
+
# would make a plain UniqueConstraint non-enforcing (NULL != NULL in
|
|
257
|
+
# SQL), so each kind gets its own. They double as the covering
|
|
258
|
+
# indexes the `is_pinned` Exists() subquery probes.
|
|
259
|
+
models.UniqueConstraint(
|
|
260
|
+
fields=["user", "project"],
|
|
261
|
+
condition=models.Q(project__isnull=False),
|
|
262
|
+
name="uq_userpin_user_project",
|
|
263
|
+
),
|
|
264
|
+
models.UniqueConstraint(
|
|
265
|
+
fields=["user", "program"],
|
|
266
|
+
condition=models.Q(program__isnull=False),
|
|
267
|
+
name="uq_userpin_user_program",
|
|
268
|
+
),
|
|
269
|
+
]
|
|
270
|
+
indexes = [
|
|
271
|
+
models.Index(fields=["user", "-pinned_at"], name="userpin_user_recent_idx"),
|
|
272
|
+
]
|
|
273
|
+
|
|
274
|
+
def __str__(self) -> str:
|
|
275
|
+
target = self.project_id or self.program_id
|
|
276
|
+
return f"UserPin({self.user_id} → {target})"
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""Serializers for the profiles app (ADR-0129, ADR-0139)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from rest_framework import serializers
|
|
8
|
+
|
|
9
|
+
from trueppm_api.apps.profiles.constants import HIDEABLE_VIEW_KEYS
|
|
10
|
+
from trueppm_api.apps.profiles.models import UserProfile
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UserProfileSerializer(serializers.ModelSerializer[UserProfile]):
|
|
14
|
+
"""Read/write the caller's own app preferences.
|
|
15
|
+
|
|
16
|
+
``default_landing`` and ``role_context`` choice validation is enforced by the
|
|
17
|
+
model fields' ``choices`` (DRF rejects an out-of-range value with 400).
|
|
18
|
+
``hidden_views`` is a bounded list of canonical view keys (ADR-0139);
|
|
19
|
+
``validate_hidden_views`` rejects unknown keys and de-duplicates.
|
|
20
|
+
``RETIRED_FIELDS`` are 400-rejected by name (see ``validate``).
|
|
21
|
+
``timezone`` / ``date_format`` are the personal display frame (#1953, ADR-0410):
|
|
22
|
+
``date_format`` choice validation is enforced by the model field's ``choices``;
|
|
23
|
+
``timezone`` is validated below against stdlib ``zoneinfo`` (``"auto"`` accepted).
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# max_length on both the list and the child bound the payload so a worker can
|
|
27
|
+
# never interpolate an attacker-supplied giant list into the error string
|
|
28
|
+
# (same DoS guard as ProgramRollupConfigSerializer.enabled_kpis).
|
|
29
|
+
hidden_views = serializers.ListField(
|
|
30
|
+
child=serializers.CharField(max_length=32),
|
|
31
|
+
max_length=32,
|
|
32
|
+
required=False,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
#: Preference keys this endpoint used to accept and no longer does.
|
|
36
|
+
#:
|
|
37
|
+
#: DRF's ``to_internal_value`` iterates *declared fields* and pulls each one out of
|
|
38
|
+
#: the payload, so a key matching no field is never enumerated — a stale client
|
|
39
|
+
#: PATCHing a removed preference gets a ``200`` and no indication that the write
|
|
40
|
+
#: evaporated. An agent's default reading of ``200`` on a PATCH is "my write
|
|
41
|
+
#: landed", and the only counter-signal is the *absence* of a key in the response,
|
|
42
|
+
#: which nothing obliges a caller to check.
|
|
43
|
+
#:
|
|
44
|
+
#: That is the same dishonesty ADR-0942 §3 removed the field to avoid, with the
|
|
45
|
+
#: mechanism inverted: the no-op field lied by echoing ``true``, and a silently
|
|
46
|
+
#: dropped key lies by echoing nothing. §3 asks for "deletion and a ``400`` on a
|
|
47
|
+
#: stale write", so the refusal is explicit and names the key.
|
|
48
|
+
#:
|
|
49
|
+
#: Deliberately a NAMED DENYLIST, not general strict mode: rejecting every unknown
|
|
50
|
+
#: key would make any additive client change a hard failure and break
|
|
51
|
+
#: forward-compatibility. Only keys this endpoint genuinely retired belong here.
|
|
52
|
+
#: An entry may be dropped once no deployed client could still be sending it.
|
|
53
|
+
RETIRED_FIELDS: dict[str, str] = {
|
|
54
|
+
# ADR-0942 §3 / #3137 — the Schedule-in-Deliver placement opt-in. Every view
|
|
55
|
+
# now has exactly one home in the rail, so there is nothing left to place.
|
|
56
|
+
"schedule_in_deliver": (
|
|
57
|
+
"The Schedule-in-Deliver placement preference was removed in 0.4 "
|
|
58
|
+
"(ADR-0942): every view now appears in exactly one navigation band. "
|
|
59
|
+
"Remove this key from your request."
|
|
60
|
+
),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class Meta:
|
|
64
|
+
model = UserProfile
|
|
65
|
+
fields = [
|
|
66
|
+
"default_landing",
|
|
67
|
+
"role_context",
|
|
68
|
+
"hidden_views",
|
|
69
|
+
"timezone",
|
|
70
|
+
"date_format",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
def validate_timezone(self, value: str) -> str:
|
|
74
|
+
# Accept the "auto" sentinel (resolved client-side to the browser zone);
|
|
75
|
+
# otherwise require a real IANA zone. Reuse the codebase precedent:
|
|
76
|
+
# ZoneInfo(value) in a try/except, NOT available_timezones() membership —
|
|
77
|
+
# it accepts exactly the OS-tzdata strings the client's Intl…timeZone emits
|
|
78
|
+
# and rejects an unknown zone with a DRF-standard 400 field error.
|
|
79
|
+
#
|
|
80
|
+
# Peers carrying the same validator: TaskRecurrenceRule, Project, Workspace,
|
|
81
|
+
# Calendar, and the notification digest timezone — every ``timezone``
|
|
82
|
+
# CharField with a serializer write path.
|
|
83
|
+
if value == "auto":
|
|
84
|
+
return value
|
|
85
|
+
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
ZoneInfo(value)
|
|
89
|
+
except (ZoneInfoNotFoundError, ValueError, OSError) as exc:
|
|
90
|
+
raise serializers.ValidationError("Unknown IANA timezone.") from exc
|
|
91
|
+
return value
|
|
92
|
+
|
|
93
|
+
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
|
94
|
+
# Object-level so it can see keys DRF's field loop never looks at: by the time
|
|
95
|
+
# `attrs` exists the retired key is already gone, so the check must read
|
|
96
|
+
# `initial_data` (the raw payload). Guarded with `hasattr` because a serializer
|
|
97
|
+
# constructed for *output* only (no `data=`) has no `initial_data`.
|
|
98
|
+
raw = getattr(self, "initial_data", None)
|
|
99
|
+
if isinstance(raw, dict):
|
|
100
|
+
retired = [k for k in self.RETIRED_FIELDS if k in raw]
|
|
101
|
+
if retired:
|
|
102
|
+
raise serializers.ValidationError({k: [self.RETIRED_FIELDS[k]] for k in retired})
|
|
103
|
+
return attrs
|
|
104
|
+
|
|
105
|
+
def validate_hidden_views(self, value: list[str]) -> list[str]:
|
|
106
|
+
unknown = [v for v in value if v not in HIDEABLE_VIEW_KEYS]
|
|
107
|
+
if unknown:
|
|
108
|
+
preview = ", ".join(sorted(unknown)[:5])
|
|
109
|
+
suffix = f" (+{len(unknown) - 5} more)" if len(unknown) > 5 else ""
|
|
110
|
+
raise serializers.ValidationError(
|
|
111
|
+
f"Unknown or non-hideable view key(s): {preview}{suffix}. "
|
|
112
|
+
f"Expected one of: {', '.join(sorted(HIDEABLE_VIEW_KEYS))}."
|
|
113
|
+
)
|
|
114
|
+
# De-duplicate while preserving caller order — the hidden set is a set in
|
|
115
|
+
# spirit; storing duplicates would be harmless but noisy.
|
|
116
|
+
seen: set[str] = set()
|
|
117
|
+
deduped: list[str] = []
|
|
118
|
+
for v in value:
|
|
119
|
+
if v not in seen:
|
|
120
|
+
seen.add(v)
|
|
121
|
+
deduped.append(v)
|
|
122
|
+
return deduped
|
|
123
|
+
|
|
124
|
+
def update(self, instance: UserProfile, validated_data: dict[str, Any]) -> UserProfile:
|
|
125
|
+
update_fields: list[str] = []
|
|
126
|
+
if "default_landing" in validated_data:
|
|
127
|
+
instance.default_landing = validated_data["default_landing"]
|
|
128
|
+
update_fields.append("default_landing")
|
|
129
|
+
if "role_context" in validated_data:
|
|
130
|
+
instance.role_context = validated_data["role_context"]
|
|
131
|
+
update_fields.append("role_context")
|
|
132
|
+
if "hidden_views" in validated_data:
|
|
133
|
+
instance.hidden_views = validated_data["hidden_views"]
|
|
134
|
+
update_fields.append("hidden_views")
|
|
135
|
+
if "timezone" in validated_data:
|
|
136
|
+
instance.timezone = validated_data["timezone"]
|
|
137
|
+
update_fields.append("timezone")
|
|
138
|
+
if "date_format" in validated_data:
|
|
139
|
+
instance.date_format = validated_data["date_format"]
|
|
140
|
+
update_fields.append("date_format")
|
|
141
|
+
if update_fields:
|
|
142
|
+
instance.save(update_fields=update_fields)
|
|
143
|
+
return instance
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class RecentProjectSerializer(serializers.Serializer[Any]):
|
|
147
|
+
"""One recently-visited project for the ⌘K "Recent" group (ADR-0508, #1557).
|
|
148
|
+
|
|
149
|
+
Serializes a :class:`~trueppm_api.apps.profiles.models.ProjectVisit` row into
|
|
150
|
+
the flat shape the palette renders: the project identity plus its program
|
|
151
|
+
breadcrumb (for cross-program disambiguation — two projects can share a name
|
|
152
|
+
across programs) and the ``visited_at`` recency hint. Read-only; the program
|
|
153
|
+
fields are method-computed so a project with no program (``program`` is
|
|
154
|
+
``SET_NULL``) serializes cleanly to ``null`` rather than raising.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
id = serializers.UUIDField(source="project.id", read_only=True)
|
|
158
|
+
name = serializers.CharField(source="project.name", read_only=True)
|
|
159
|
+
program_id = serializers.SerializerMethodField()
|
|
160
|
+
program_name = serializers.SerializerMethodField()
|
|
161
|
+
visited_at = serializers.DateTimeField(read_only=True)
|
|
162
|
+
|
|
163
|
+
def get_program_id(self, visit: Any) -> str | None:
|
|
164
|
+
return str(visit.project.program_id) if visit.project.program_id else None
|
|
165
|
+
|
|
166
|
+
def get_program_name(self, visit: Any) -> str | None:
|
|
167
|
+
return visit.project.program.name if visit.project.program_id else None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class PinnedItemSerializer(serializers.Serializer[Any]):
|
|
171
|
+
"""One entry in ``GET /auth/me/pinned/`` — a project or a program (#2390).
|
|
172
|
+
|
|
173
|
+
A merged shape rather than two lists: the rail renders one flat jump group,
|
|
174
|
+
and ``kind`` is what the client switches on for the route and the glyph.
|
|
175
|
+
|
|
176
|
+
``pinned_at`` appears on this serializer and nowhere else. It is the caller's
|
|
177
|
+
own timestamp on their own pin; putting it on a shared Project/Program
|
|
178
|
+
payload would make "she hasn't pinned it in weeks" expressible to a
|
|
179
|
+
teammate, which ADR-0627 §D5 forecloses.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
# Plain CharField (not ChoiceField), matching the same decision on
|
|
183
|
+
# `UnifiedSearchResultSerializer.kind`: a ChoiceField emits a `KindEnum`
|
|
184
|
+
# component that collides with the existing AssetItem `kind` (file/link) and
|
|
185
|
+
# makes drf-spectacular rename BOTH — silently turning the long-stable
|
|
186
|
+
# `KindEnum` into `AssetItemKindEnum` and breaking every generated client
|
|
187
|
+
# that referenced it. The two values live in `help_text` instead.
|
|
188
|
+
kind = serializers.CharField(
|
|
189
|
+
help_text="Pinned target: 'project' or 'program'.",
|
|
190
|
+
)
|
|
191
|
+
id = serializers.CharField()
|
|
192
|
+
name = serializers.CharField()
|
|
193
|
+
# Programs carry a short code; projects do not.
|
|
194
|
+
code = serializers.CharField(allow_null=True)
|
|
195
|
+
# Parent program of a pinned project, for the "Program › Project" breadcrumb.
|
|
196
|
+
# Null on a pinned program (it has no parent) and on a standalone project.
|
|
197
|
+
program_id = serializers.CharField(allow_null=True)
|
|
198
|
+
program_name = serializers.CharField(allow_null=True)
|
|
199
|
+
pinned_at = serializers.DateTimeField()
|