simpleaudit-studio 0.2.4__tar.gz → 0.2.6__tar.gz

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.
Files changed (155) hide show
  1. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/.env.example +2 -2
  2. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/.env.local.example +3 -3
  3. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/.github/workflows/ci.yml +7 -5
  4. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/.gitignore +1 -1
  5. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/Dockerfile +8 -4
  6. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/Makefile +1 -2
  7. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/PKG-INFO +7 -3
  8. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/README.md +2 -1
  9. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/views.py +2 -5
  10. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/comparison.py +1 -1
  11. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/events.py +1 -4
  12. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/services.py +4 -3
  13. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/views.py +2 -3
  14. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/deploy/compose/Dockerfile +9 -5
  15. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/deploy/e2e_smoke.py +10 -8
  16. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/deploy/mock_openai_server.py +4 -8
  17. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/deploy/seed_bulk.py +15 -9
  18. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/docs/deployment.md +1 -1
  19. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/e2e/test_ui.py +2 -2
  20. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/engine.py +10 -10
  21. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/health.py +2 -2
  22. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/logging.py +2 -2
  23. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/management/commands/purge_test_data.py +6 -1
  24. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/management/commands/run_worker.py +0 -1
  25. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/management/commands/seed_demo_audits.py +2 -1
  26. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/management/commands/seed_platform.py +7 -2
  27. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/readiness.py +2 -4
  28. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/seed.py +3 -1
  29. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/simpleaudit_package.py +2 -3
  30. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/factories.py +9 -5
  31. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_api_e2e.py +1 -6
  32. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_audit_archive.py +4 -1
  33. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_audit_clone.py +7 -1
  34. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_audit_poll_results.py +7 -1
  35. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_audit_rename.py +4 -1
  36. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_audit_run_freeze.py +2 -3
  37. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_audit_sse.py +6 -1
  38. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_audit_submission.py +8 -2
  39. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_audit_timing.py +4 -1
  40. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_auth_projects.py +0 -1
  41. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_bootstrap.py +1 -1
  42. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_engine_integration.py +64 -52
  43. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_health_panel.py +11 -7
  44. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_middleware.py +1 -2
  45. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_minimal_config.py +6 -6
  46. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_model_registry.py +1 -1
  47. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_scenarios.py +4 -2
  48. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_seed_platform.py +2 -4
  49. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_smoke_all_pages.py +13 -5
  50. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_workspaces.py +8 -3
  51. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/ui.py +33 -27
  52. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/worker.py +8 -8
  53. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/manage.py +1 -1
  54. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/serializers.py +1 -1
  55. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/views.py +5 -6
  56. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/pyproject.toml +18 -4
  57. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/admin.py +7 -1
  58. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/views.py +9 -4
  59. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/simpleaudit_studio/cli.py +11 -11
  60. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/uv.lock +49 -6
  61. simpleaudit_studio-0.2.4/requirements.txt +0 -28
  62. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/.dockerignore +0 -0
  63. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/.github/workflows/publish.yml +0 -0
  64. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/LICENSE +0 -0
  65. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/__init__.py +0 -0
  66. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/admin.py +0 -0
  67. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/apps.py +0 -0
  68. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/auth_urls.py +0 -0
  69. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/migrations/0001_initial.py +0 -0
  70. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/migrations/0002_user_workos_user_id.py +0 -0
  71. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/migrations/__init__.py +0 -0
  72. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/models.py +0 -0
  73. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/project_urls.py +0 -0
  74. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/serializers.py +0 -0
  75. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/services.py +0 -0
  76. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/accounts/workos_auth.py +0 -0
  77. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/__init__.py +0 -0
  78. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/admin.py +1 -1
  79. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/apps.py +0 -0
  80. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/migrations/0001_initial.py +0 -0
  81. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/migrations/0002_add_audit_run_archived.py +0 -0
  82. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/migrations/0003_auditrun_updated_at.py +0 -0
  83. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/migrations/__init__.py +0 -0
  84. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/models.py +0 -0
  85. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/serializers.py +0 -0
  86. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/audits/urls.py +0 -0
  87. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/config/__init__.py +0 -0
  88. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/config/asgi.py +0 -0
  89. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/config/settings.py +1 -1
  90. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/config/urls.py +8 -8
  91. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/config/wsgi.py +0 -0
  92. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/deploy/postgres-init.sql +0 -0
  93. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/docker-compose.yml +0 -0
  94. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/docs/architecture.md +0 -0
  95. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/docs/domain-model.md +0 -0
  96. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/__init__.py +0 -0
  97. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/admin.py +0 -0
  98. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/apps.py +0 -0
  99. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/context_processors.py +0 -0
  100. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/exceptions.py +0 -0
  101. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/fixtures/demo_audit_results.json +0 -0
  102. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/hashing.py +0 -0
  103. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/health_api.py +0 -0
  104. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/management/__init__.py +0 -0
  105. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/management/commands/__init__.py +0 -0
  106. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/management/commands/bootstrap_platform.py +0 -0
  107. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/middleware.py +0 -0
  108. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/minimal_config.py +0 -0
  109. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/permissions.py +0 -0
  110. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/startup_checks.py +0 -0
  111. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/__init__.py +0 -0
  112. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_audit_events.py +0 -0
  113. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_comparison.py +2 -2
  114. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_health.py +0 -0
  115. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_purge_test_data.py +1 -1
  116. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_scenario_import_export.py +0 -0
  117. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/infra/tests/test_workspaces_ui.py +0 -0
  118. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/__init__.py +0 -0
  119. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/admin.py +0 -0
  120. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/apps.py +0 -0
  121. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/migrations/0001_initial.py +0 -0
  122. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/migrations/__init__.py +0 -0
  123. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/models.py +0 -0
  124. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/services.py +1 -1
  125. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/model_registry/urls.py +0 -0
  126. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/__init__.py +0 -0
  127. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/apps.py +0 -0
  128. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/migrations/0001_initial.py +0 -0
  129. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/migrations/__init__.py +0 -0
  130. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/models.py +0 -0
  131. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/serializers.py +0 -0
  132. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/services.py +2 -2
  133. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/scenarios/urls.py +0 -0
  134. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/simpleaudit_studio/__init__.py +0 -0
  135. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/static/favicon-16x16.svg +0 -0
  136. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/static/favicon-32x32.svg +0 -0
  137. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/static/favicon.svg +0 -0
  138. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/static/js/diff.min.js +0 -0
  139. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/static/logo.png +0 -0
  140. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/static/logo.svg +0 -0
  141. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/404.html +0 -0
  142. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/audit_detail.html +0 -0
  143. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/auth/login.html +0 -0
  144. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/auth/register.html +0 -0
  145. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/auth/workos_login.html +0 -0
  146. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/auth/workos_verify.html +0 -0
  147. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/base.html +0 -0
  148. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/compare.html +0 -0
  149. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/dashboard.html +0 -0
  150. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/health.html +0 -0
  151. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/models.html +0 -0
  152. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/new_audit.html +0 -0
  153. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/scenario_result_detail.html +0 -0
  154. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/scenarios.html +0 -0
  155. {simpleaudit_studio-0.2.4 → simpleaudit_studio-0.2.6}/templates/workspaces.html +0 -0
@@ -82,10 +82,10 @@ SSE_MAX_CONNECTIONS_PER_USER=10
82
82
 
83
83
  # --- SimpleAudit engine ---------------------------------------------------------
84
84
  # No configuration needed here. The engine is a normal pip dependency pinned in
85
- # requirements.txt; its version + optional git commit are read from the installed
85
+ # pyproject.toml; its version + optional git commit are read from the installed
86
86
  # package metadata automatically (infra/simpleaudit_package.py). To develop
87
87
  # against an unmerged revision, create a gitignored simpleaudit-dependency.yaml
88
- # at the project root with a single `git_ref:` key — do NOT edit requirements.txt.
88
+ # at the project root with a single `git_ref:` key — do NOT edit pyproject.toml.
89
89
 
90
90
  # --- Model API credentials (resolved at execution time, never stored) ----------
91
91
  # Add one variable per model endpoint's `secret_reference`, e.g.:
@@ -9,7 +9,7 @@
9
9
  # (see `.env.example`).
10
10
  #
11
11
  # Prerequisites:
12
- # 1. A Python 3.12+ virtualenv with `pip install -r requirements.txt`
12
+ # 1. A Python 3.12+ virtualenv with `uv sync --extra dev --extra postgres`
13
13
  # 2. Docker, for Postgres + Hatchet (the "partial Docker" pattern below)
14
14
  # 3. A SimpleAudit engine checkout (for the worker to execute audits)
15
15
  # =============================================================================
@@ -66,14 +66,14 @@ WORKER_POOL=cpu
66
66
 
67
67
  # --- SimpleAudit engine ---------------------------------------------------------
68
68
  # No configuration needed here. The engine is a normal pip dependency pinned in
69
- # requirements.txt; its version + optional git commit are read from the installed
69
+ # pyproject.toml; its version + optional git commit are read from the installed
70
70
  # package metadata automatically (infra/simpleaudit_package.py).
71
71
  #
72
72
  # To develop against an unmerged engine revision, create a gitignored
73
73
  # simpleaudit-dependency.yaml at the project root containing ONLY:
74
74
  # git_ref: <full-40-char-sha>
75
75
  # That value is recorded as the run's git_commit provenance. Do NOT edit
76
- # requirements.txt for a dev override.
76
+ # pyproject.toml for a dev override.
77
77
 
78
78
  # --- Model API credentials (resolved at execution time, never stored) ----------
79
79
  # Add one variable per model endpoint's `secret_reference`, e.g.:
@@ -37,12 +37,14 @@ jobs:
37
37
  - uses: actions/setup-python@v5
38
38
  with:
39
39
  python-version: "3.12"
40
- cache: pip
40
+ - uses: astral-sh/setup-uv@v7
41
41
  - name: Install dependencies
42
- run: pip install -r requirements.txt
42
+ run: uv sync --frozen --extra dev --extra postgres
43
+ - name: Lint (ruff)
44
+ run: uv run ruff check .
43
45
  - name: Django checks
44
- run: python manage.py check
46
+ run: uv run python manage.py check
45
47
  - name: Migrations from empty database
46
- run: python manage.py migrate
48
+ run: uv run python manage.py migrate
47
49
  - name: Run tests
48
- run: python manage.py test infra
50
+ run: uv run python manage.py test infra
@@ -16,7 +16,7 @@ staticfiles/
16
16
  .env.local
17
17
 
18
18
  # Dev-only SimpleAudit engine override (git_ref). Never committed — production
19
- # relies on the requirements.txt pin. See infra/simpleaudit_package.py.
19
+ # relies on the pyproject.toml pin. See infra/simpleaudit_package.py.
20
20
  simpleaudit-dependency.yaml
21
21
 
22
22
  # IDE
@@ -22,16 +22,20 @@ WORKDIR /app
22
22
 
23
23
  # --- System dependencies -----------------------------------------------------
24
24
  # curl: healthchecks
25
- # git: required to install SimpleAudit engine from its pinned git ref
26
25
  RUN apt-get update \
27
26
  && apt-get install -y --no-install-recommends \
28
27
  curl \
29
- git \
30
28
  && rm -rf /var/lib/apt/lists/*
31
29
 
32
30
  # --- Python dependencies -----------------------------------------------------
33
- COPY requirements.txt .
34
- RUN pip install -r requirements.txt
31
+ # pyproject.toml is the single source of truth; uv.lock pins exact versions.
32
+ # The postgres extra is included because Django imports the psycopg driver at
33
+ # startup even when SIMPLEAUDIT_MINIMAL=1 uses SQLite for the domain DB.
34
+ # uv sync creates /app/.venv; the PATH update keeps the `python` entrypoint.
35
+ COPY pyproject.toml uv.lock README.md ./
36
+ RUN pip install uv \
37
+ && uv sync --frozen --no-install-project --no-dev --extra postgres
38
+ ENV PATH="/app/.venv/bin:$PATH"
35
39
 
36
40
  # --- Application code --------------------------------------------------------
37
41
  COPY . .
@@ -14,8 +14,7 @@ help: ## Show available targets
14
14
  @grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-14s\033[0m %s\n", $$1, $$2}'
15
15
 
16
16
  venv: ## Create the local virtualenv and install dependencies
17
- python3 -m venv $(VENV)
18
- $(VENV)/bin/pip install -r requirements.txt
17
+ uv sync --extra dev --extra postgres
19
18
 
20
19
  local-setup: ## Local dev: venv + .env + migrations + bootstrap + seed (idempotent)
21
20
  @test -d $(VENV) || $(MAKE) venv
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simpleaudit-studio
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Production AI model audit platform — one-liner demo mode via uvx
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -15,14 +15,17 @@ Requires-Dist: pydantic<3,>=2.7
15
15
  Requires-Dist: python-dotenv==1.1.1
16
16
  Requires-Dist: requests==2.32.5
17
17
  Requires-Dist: sentry-sdk[django]>=2.0
18
- Requires-Dist: simpleaudit==0.2.0
18
+ Requires-Dist: simpleaudit==0.2.1
19
19
  Requires-Dist: workos==10.4.0
20
20
  Provides-Extra: dev
21
21
  Requires-Dist: factory-boy==3.3.2; extra == 'dev'
22
22
  Requires-Dist: pytest; extra == 'dev'
23
23
  Requires-Dist: pytest-django; extra == 'dev'
24
+ Requires-Dist: ruff; extra == 'dev'
24
25
  Provides-Extra: postgres
25
26
  Requires-Dist: psycopg[binary]==3.2.13; extra == 'postgres'
27
+ Provides-Extra: server
28
+ Requires-Dist: gunicorn==23.0.0; extra == 'server'
26
29
  Description-Content-Type: text/markdown
27
30
 
28
31
  ---
@@ -54,13 +57,14 @@ uvx simpleaudit-studio
54
57
 
55
58
  [`uvx`](https://docs.astral.sh/uv/#uvx) installs the [`simpleaudit-studio`](https://pypi.org/project/simpleaudit-studio/) package and runs it in an isolated environment. No Docker, no Postgres, no manual setup. Opens at http://localhost:8000 (login: `studio` / `admin123`). A mock model server is pre-seeded so you can start exploring audit results immediately.
56
59
 
60
+ 🌐 Or skip the setup entirely — try the live demo: <a href="https://sushantgautam-simpleaudit-studio.hf.space" target="_blank" rel="noopener noreferrer">sushantgautam-simpleaudit-studio.hf.space</a>
61
+
57
62
  Want the **latest unreleased code** from this repo instead of the PyPI release? Same one-liner, pointed at git:
58
63
 
59
64
  ```bash
60
65
  uvx --from "git+https://github.com/SushantGautam/SimpleAuditStudio" spin
61
66
  ```
62
67
 
63
- 🌐 Or skip the setup entirely — try the live demo: <a href="https://sushantgautam-simpleaudit-studio.hf.space" target="_blank" rel="noopener noreferrer">sushantgautam-simpleaudit-studio.hf.space</a>
64
68
 
65
69
  ### 🤖 Using Real Models
66
70
 
@@ -27,13 +27,14 @@ uvx simpleaudit-studio
27
27
 
28
28
  [`uvx`](https://docs.astral.sh/uv/#uvx) installs the [`simpleaudit-studio`](https://pypi.org/project/simpleaudit-studio/) package and runs it in an isolated environment. No Docker, no Postgres, no manual setup. Opens at http://localhost:8000 (login: `studio` / `admin123`). A mock model server is pre-seeded so you can start exploring audit results immediately.
29
29
 
30
+ 🌐 Or skip the setup entirely — try the live demo: <a href="https://sushantgautam-simpleaudit-studio.hf.space" target="_blank" rel="noopener noreferrer">sushantgautam-simpleaudit-studio.hf.space</a>
31
+
30
32
  Want the **latest unreleased code** from this repo instead of the PyPI release? Same one-liner, pointed at git:
31
33
 
32
34
  ```bash
33
35
  uvx --from "git+https://github.com/SushantGautam/SimpleAuditStudio" spin
34
36
  ```
35
37
 
36
- 🌐 Or skip the setup entirely — try the live demo: <a href="https://sushantgautam-simpleaudit-studio.hf.space" target="_blank" rel="noopener noreferrer">sushantgautam-simpleaudit-studio.hf.space</a>
37
38
 
38
39
  ### 🤖 Using Real Models
39
40
 
@@ -2,7 +2,6 @@
2
2
  import logging
3
3
 
4
4
  from django.contrib.auth import authenticate, login
5
- from django.db import connection
6
5
  from django.http import JsonResponse
7
6
  from rest_framework import status
8
7
  from rest_framework.authtoken.models import Token
@@ -10,28 +9,26 @@ from rest_framework.decorators import api_view, permission_classes
10
9
  from rest_framework.permissions import AllowAny, IsAuthenticated
11
10
  from rest_framework.response import Response
12
11
 
13
- from infra.exceptions import StableAPIError
14
12
  from accounts.models import Project, ProjectMembership, User
15
13
  from accounts.serializers import (
16
14
  MemberAddSerializer,
17
15
  MemberRoleSerializer,
18
16
  ProjectMembershipSerializer,
19
- ProjectSerializer,
20
17
  RegisterSerializer,
21
18
  UserSerializer,
22
19
  WorkspaceCreateSerializer,
23
20
  WorkspaceItemSerializer,
24
21
  WorkspaceUpdateSerializer,
25
22
  )
26
- from infra.readiness import ready_payload
27
23
  from accounts.services import (
28
24
  DEFAULT_PROJECT_SLUG,
29
- bootstrap_admin_and_default_project,
30
25
  create_workspace,
31
26
  delete_workspace,
32
27
  ensure_project_access,
33
28
  update_workspace,
34
29
  )
30
+ from infra.exceptions import StableAPIError
31
+ from infra.readiness import ready_payload
35
32
 
36
33
  logger = logging.getLogger(__name__)
37
34
 
@@ -160,7 +160,7 @@ def compare_runs(project, run_ids: list[int]) -> dict:
160
160
  elif label == "Scenario set version" and r.scenario_set_version:
161
161
  url = f"/scenarios/?set={r.scenario_set_version.scenario_set_id}"
162
162
  cells.append(_cell(text, url))
163
- differs = len(set(c["text"] for c in cells)) > 1
163
+ differs = len({c["text"] for c in cells}) > 1
164
164
  input_rows.append({"label": label, "cells": cells, "differs": differs})
165
165
 
166
166
  return {
@@ -15,8 +15,6 @@ events are inputs only; the worker bridges them into these rows.
15
15
  """
16
16
  from __future__ import annotations
17
17
 
18
- import json
19
-
20
18
  from django.db import models, transaction
21
19
 
22
20
 
@@ -124,8 +122,7 @@ def upsert_scenario_result(
124
122
  result=result or {},
125
123
  )
126
124
  return
127
- if attempts > existing.attempts:
128
- existing.attempts = attempts
125
+ existing.attempts = max(existing.attempts, attempts)
129
126
  existing.status = status
130
127
  if result is not None:
131
128
  existing.result = result
@@ -1,16 +1,15 @@
1
1
  """Audit run creation and freeze services."""
2
2
  import logging
3
3
 
4
- from django.conf import settings
5
4
  from django.db import transaction
6
5
  from django.utils import timezone
7
6
 
7
+ from accounts.models import Project
8
8
  from audits.events import append_event
9
9
  from audits.models import AuditRun
10
10
  from infra.exceptions import StableAPIError
11
11
  from infra.simpleaudit_package import resolve_engine_provenance
12
12
  from model_registry.models import ModelEndpoint
13
- from accounts.models import Project
14
13
  from scenarios.models import ScenarioSetVersion
15
14
  from scenarios.services import require_project_role
16
15
 
@@ -131,7 +130,9 @@ def submit_audit_run(run: AuditRun) -> str | None:
131
130
  reason is recorded in ``runtime_metadata["submission"]`` so an operator (or a
132
131
  later retry command) can resubmit without re-freezing inputs.
133
132
  """
134
- from infra.worker import submit_run_workflow # lazy: needs no live server at import time
133
+ from infra.worker import (
134
+ submit_run_workflow, # lazy: needs no live server at import time
135
+ )
135
136
 
136
137
  items = list(
137
138
  run.scenario_set_version.items.select_related("scenario", "revision").order_by("position")
@@ -9,6 +9,8 @@ from rest_framework.decorators import api_view, permission_classes
9
9
  from rest_framework.permissions import IsAuthenticated
10
10
  from rest_framework.response import Response
11
11
 
12
+ from accounts.models import Project
13
+ from accounts.services import ensure_project_access
12
14
  from audits.events import ScenarioResult, list_events
13
15
  from audits.models import AuditRun
14
16
  from audits.serializers import AuditRunCreateSerializer, AuditRunSerializer
@@ -16,9 +18,7 @@ from audits.services import create_audit_run, submit_audit_run
16
18
  from infra.exceptions import StableAPIError
17
19
  from infra.middleware import set_correlation_context
18
20
  from model_registry.models import ModelEndpoint
19
- from accounts.models import Project
20
21
  from scenarios.models import ScenarioSetVersion
21
- from accounts.services import ensure_project_access
22
22
 
23
23
  logger = logging.getLogger("simpleaudit.audit")
24
24
 
@@ -214,7 +214,6 @@ def stream_audit_run_events(request, project_id, run_id):
214
214
  with a higher id — standard SSE replay semantics. The stream ends when a
215
215
  terminal run event (completed/failed/cancelled) is observed.
216
216
  """
217
- from django.contrib.auth.decorators import login_required
218
217
  from django.http import Http404
219
218
 
220
219
  # This is called via URL dispatch; enforce auth manually since we're not using DRF.
@@ -23,16 +23,20 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
23
23
 
24
24
  WORKDIR /app
25
25
 
26
- # curl: healthchecks; git: required to install the SimpleAudit engine from its
27
- # pinned git ref in requirements.txt.
26
+ # curl: healthchecks
28
27
  RUN apt-get update \
29
28
  && apt-get install -y --no-install-recommends \
30
29
  curl \
31
- git \
32
30
  && rm -rf /var/lib/apt/lists/*
33
31
 
34
- COPY requirements.txt .
35
- RUN pip install -r requirements.txt
32
+ # pyproject.toml is the single source of truth; uv.lock pins exact versions.
33
+ # The compose stack runs against external Postgres (postgres extra) and serves
34
+ # via gunicorn (server extra). uv sync creates /app/.venv; the PATH update
35
+ # keeps the `python`/`gunicorn` entrypoints working.
36
+ COPY pyproject.toml uv.lock README.md ./
37
+ RUN pip install uv \
38
+ && uv sync --frozen --no-install-project --no-dev --extra postgres --extra server
39
+ ENV PATH="/app/.venv/bin:$PATH"
36
40
 
37
41
  COPY . .
38
42
 
@@ -25,12 +25,13 @@ def _load_env(path=".env"):
25
25
  env = {}
26
26
  if not os.path.exists(path):
27
27
  return env
28
- for line in open(path):
29
- line = line.strip()
30
- if not line or line.startswith("#") or "=" not in line:
31
- continue
32
- k, _, v = line.partition("=")
33
- env[k.strip()] = v.strip()
28
+ with open(path) as f:
29
+ for line in f:
30
+ line = line.strip()
31
+ if not line or line.startswith("#") or "=" not in line:
32
+ continue
33
+ k, _, v = line.partition("=")
34
+ env[k.strip()] = v.strip()
34
35
  return env
35
36
 
36
37
 
@@ -56,7 +57,7 @@ class Client:
56
57
  raw = e.read().decode()
57
58
  try:
58
59
  parsed = json.loads(raw)
59
- except Exception:
60
+ except Exception: # noqa: BLE001 - fall back to raw body if not JSON
60
61
  parsed = raw
61
62
  return e.code, parsed
62
63
 
@@ -77,7 +78,8 @@ def main():
77
78
  sys.exit(2)
78
79
 
79
80
  c = Client(base_url)
80
- step = lambda m: print(f"\n=== {m} ===")
81
+ def step(m):
82
+ print(f"\n=== {m} ===")
81
83
  # Unique suffix so the smoke test is repeatable without colliding with the
82
84
  # (project, display_name) / set-name uniqueness constraints on prior runs.
83
85
  import time as _t
@@ -23,11 +23,7 @@ import re
23
23
  import threading
24
24
  import time
25
25
  import uuid
26
-
27
- try:
28
- from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
29
- except Exception: # pragma: no cover - stdlib always present
30
- raise
26
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
31
27
 
32
28
  JUDGE_JSON_RE = re.compile(r"\{.*\}", re.DOTALL)
33
29
 
@@ -91,11 +87,11 @@ def _completion_body(messages, model) -> dict:
91
87
 
92
88
 
93
89
  class Handler(BaseHTTPRequestHandler):
94
- def log_message(self, fmt, *args): # noqa: N802 - stdlib signature
90
+ def log_message(self, fmt, *args):
95
91
  # Keep logs quiet but useful.
96
92
  print("[mock-openai] %s" % (fmt % args), flush=True)
97
93
 
98
- def do_GET(self): # noqa: N802
94
+ def do_GET(self):
99
95
  if self.path.rstrip("/") in ("", "/healthz"):
100
96
  self._send_json({"status": "ok"})
101
97
  elif self.path.rstrip("/") == "/v1/models":
@@ -108,7 +104,7 @@ class Handler(BaseHTTPRequestHandler):
108
104
  else:
109
105
  self._send_json({"error": "not found"}, status=404)
110
106
 
111
- def do_POST(self): # noqa: N802
107
+ def do_POST(self):
112
108
  length = int(self.headers.get("Content-Length", 0) or 0)
113
109
  raw = self.rfile.read(length) if length else b""
114
110
  try:
@@ -11,23 +11,29 @@ Run inside the web container:
11
11
  Or locally with SQLite:
12
12
  SIMPLEAUDIT_LOCAL_SQLITE=1 .venv/bin/python manage.py shell < deploy/seed_bulk.py
13
13
  """
14
+ import hashlib
14
15
  import os
15
- import sys
16
16
  import random
17
- import hashlib
18
- from datetime import datetime, timedelta
17
+ from datetime import UTC, datetime, timedelta
19
18
 
20
19
  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
21
20
  # Do NOT set SIMPLEAUDIT_LOCAL_SQLITE — use whatever DB the environment provides
22
21
 
23
22
  import django
23
+
24
24
  django.setup()
25
25
 
26
- from accounts.models import User, Project
27
- from scenarios.models import Scenario, ScenarioRevision, ScenarioSet, ScenarioSetVersion, ScenarioSetVersionItem
28
- from model_registry.models import ModelEndpoint
29
- from audits.models import AuditRun
26
+ from accounts.models import Project, User
30
27
  from audits.events import ScenarioResult, append_event
28
+ from audits.models import AuditRun
29
+ from model_registry.models import ModelEndpoint
30
+ from scenarios.models import (
31
+ Scenario,
32
+ ScenarioRevision,
33
+ ScenarioSet,
34
+ ScenarioSetVersion,
35
+ ScenarioSetVersionItem,
36
+ )
31
37
 
32
38
  random.seed(42)
33
39
 
@@ -256,7 +262,7 @@ def main():
256
262
  version=ver,
257
263
  scenario_count=len(items_for_ver),
258
264
  content_hash=make_hash(f"{st.name}-v{ver}-{random.random()}"),
259
- published_at=datetime.now() - timedelta(days=random.randint(0, 90)),
265
+ published_at=datetime.now(UTC) - timedelta(days=random.randint(0, 90)),
260
266
  published_by=admin,
261
267
  )
262
268
  version_batch.append((vs, items_for_ver))
@@ -293,7 +299,7 @@ def main():
293
299
  print(f"Audit runs before: {AuditRun.objects.count()}")
294
300
  all_versions = list(ScenarioSetVersion.objects.all())
295
301
  run_batch = []
296
- base_time = datetime.now() - timedelta(days=90)
302
+ base_time = datetime.now(UTC) - timedelta(days=90)
297
303
 
298
304
  for i in range(500):
299
305
  status = random.choice(RUN_STATUS_DISTRIBUTION)
@@ -188,7 +188,7 @@ MAX_CONCURRENT_AUDITS=2
188
188
  MAX_SCENARIOS_PER_RUN=500
189
189
 
190
190
  # SimpleAudit engine: no env vars needed. It is a pip dependency pinned in
191
- # requirements.txt; provenance is read from installed package metadata.
191
+ # pyproject.toml; provenance is read from installed package metadata.
192
192
 
193
193
  # Optional observability
194
194
  OTEL_EXPORTER_OTLP_ENDPOINT=
@@ -35,7 +35,7 @@ def wait_for_health(timeout: int = 60) -> None:
35
35
  resp = urllib.request.urlopen(f"{BASE_URL}/healthz", timeout=3)
36
36
  if resp.status == 200:
37
37
  return
38
- except Exception:
38
+ except Exception: # noqa: BLE001,S110 - retry loop; not healthy yet
39
39
  pass
40
40
  time.sleep(2)
41
41
  raise RuntimeError(f"Web service did not become healthy within {timeout}s")
@@ -198,7 +198,7 @@ def main() -> int:
198
198
  fn(page)
199
199
  print(f" PASS {name}")
200
200
  passed += 1
201
- except Exception as e:
201
+ except Exception as e: # noqa: BLE001 - test harness reports any failure
202
202
  print(f" FAIL {name}: {e}")
203
203
  failed += 1
204
204
  errors.append((name, str(e)))
@@ -18,7 +18,6 @@ from __future__ import annotations
18
18
 
19
19
  import asyncio
20
20
  import os
21
- import sys
22
21
  from typing import Any
23
22
 
24
23
 
@@ -29,18 +28,19 @@ class EngineError(RuntimeError):
29
28
  def _ensure_engine_available() -> None:
30
29
  """Ensure the SimpleAudit engine package is importable.
31
30
 
32
- The engine is a normal pip dependency (see ``requirements.txt``); there is no
33
- path-based fallback. If it is not installed (e.g. a web-only process or a
34
- test environment without the engine), raise a clean ``EngineError`` so the
35
- caller can record a durable failure instead of crashing on import.
31
+ The engine is a normal pip dependency (declared in ``pyproject.toml``);
32
+ there is no path-based fallback. If it is not installed (e.g. a web-only
33
+ process or a test environment without the engine), raise a clean
34
+ ``EngineError`` so the caller can record a durable failure instead of
35
+ crashing on import.
36
36
  """
37
37
  try:
38
38
  import simpleaudit # noqa: F401
39
39
  return
40
40
  except ModuleNotFoundError as exc:
41
41
  raise EngineError(
42
- "SimpleAudit engine is not installed. Install it from requirements.txt "
43
- "(pip install -r requirements.txt) before running audits."
42
+ "SimpleAudit engine is not installed. Install the project "
43
+ "dependencies (uv sync) before running audits."
44
44
  ) from exc
45
45
 
46
46
 
@@ -168,7 +168,7 @@ def build_model_auditor(*, target: dict, auditor: dict, judge: dict, generation:
168
168
  _ensure_engine_available()
169
169
  try:
170
170
  from simpleaudit.model_auditor import ModelAuditor
171
- except Exception as exc: # noqa: BLE001 - surface any import failure uniformly
171
+ except Exception as exc:
172
172
  raise EngineError(f"Failed to import SimpleAudit ModelAuditor: {exc}") from exc
173
173
 
174
174
  gen = dict(generation or {})
@@ -263,7 +263,7 @@ def build_model_auditor(*, target: dict, auditor: dict, judge: dict, generation:
263
263
  )
264
264
  except EngineError:
265
265
  raise
266
- except Exception as exc: # noqa: BLE001 - any construction failure (bad base_url, provider, etc.)
266
+ except Exception as exc:
267
267
  raise EngineError(f"Failed to construct ModelAuditor: {type(exc).__name__}: {exc}") from exc
268
268
  return instance, language
269
269
 
@@ -309,7 +309,7 @@ def run_scenario(
309
309
  )
310
310
  except EngineError:
311
311
  raise
312
- except Exception as exc: # noqa: BLE001 - unexpected engine crash
312
+ except Exception as exc:
313
313
  raise EngineError(f"Scenario execution crashed: {type(exc).__name__}: {exc}") from exc
314
314
 
315
315
  payload = result.to_dict()
@@ -70,7 +70,7 @@ def _postgres_probe() -> dict[str, Any]:
70
70
  row = cursor.fetchone()
71
71
  if row:
72
72
  signals["db_size_bytes"] = int(row[0])
73
- except Exception: # noqa: BLE001 - non-Postgres or permission issue
73
+ except Exception: # noqa: BLE001,S110 - non-Postgres or permission issue; size is optional
74
74
  pass
75
75
  return {"status": "up", **signals}
76
76
 
@@ -177,7 +177,7 @@ def _engine_probe() -> dict[str, Any]:
177
177
 
178
178
  def _model_endpoints_probe() -> dict[str, Any]:
179
179
  """Ping each registered model endpoint's base_url, grouped by connection."""
180
- from model_registry.models import ModelConnection, RegisteredModel
180
+ from model_registry.models import ModelConnection
181
181
 
182
182
  groups: list[dict[str, Any]] = []
183
183
 
@@ -1,13 +1,13 @@
1
1
  """Structured JSON logging for containers."""
2
2
  import json
3
3
  import logging
4
- from datetime import datetime, timezone
4
+ from datetime import UTC, datetime
5
5
 
6
6
 
7
7
  class JsonFormatter(logging.Formatter):
8
8
  def format(self, record: logging.LogRecord) -> str:
9
9
  payload = {
10
- "timestamp": datetime.now(timezone.utc).isoformat(),
10
+ "timestamp": datetime.now(UTC).isoformat(),
11
11
  "level": record.levelname,
12
12
  "logger": record.name,
13
13
  "message": record.getMessage(),
@@ -14,7 +14,12 @@ from django.db import transaction
14
14
  from audits.events import AuditEvent, ScenarioResult
15
15
  from audits.models import AuditRun
16
16
  from model_registry.models import ModelEndpoint
17
- from scenarios.models import Scenario, ScenarioSet, ScenarioSetVersion, ScenarioSetVersionItem
17
+ from scenarios.models import (
18
+ Scenario,
19
+ ScenarioSet,
20
+ ScenarioSetVersion,
21
+ ScenarioSetVersionItem,
22
+ )
18
23
 
19
24
 
20
25
  class Command(BaseCommand):
@@ -7,7 +7,6 @@ The worker connects to the external Hatchet server (HATCHET_SERVER_URL /
7
7
  HATCHET_GRPC_URL) and executes audit scenario/finalize tasks. It runs in its own
8
8
  process, separate from the web/API server, which never executes model calls.
9
9
  """
10
- import os
11
10
 
12
11
  from django.conf import settings
13
12
  from django.core.management.base import BaseCommand
@@ -54,9 +54,10 @@ class Command(BaseCommand):
54
54
  )
55
55
 
56
56
  def handle(self, *args, **options):
57
- from accounts.models import Project
58
57
  from django.contrib.auth import get_user_model
59
58
 
59
+ from accounts.models import Project
60
+
60
61
  User = get_user_model()
61
62
  project_id = options["project"]
62
63
  try:
@@ -16,7 +16,11 @@ import logging
16
16
 
17
17
  from django.core.management.base import BaseCommand, CommandError
18
18
 
19
- from infra.seed import DEFAULT_PACKS, import_scenario_pack, seed_default_model_connections
19
+ from infra.seed import (
20
+ DEFAULT_PACKS,
21
+ import_scenario_pack,
22
+ seed_default_model_connections,
23
+ )
20
24
 
21
25
  logger = logging.getLogger(__name__)
22
26
 
@@ -47,9 +51,10 @@ class Command(BaseCommand):
47
51
  )
48
52
 
49
53
  def handle(self, *args, **options):
50
- from accounts.models import Project
51
54
  from django.contrib.auth import get_user_model
52
55
 
56
+ from accounts.models import Project
57
+
53
58
  User = get_user_model()
54
59
 
55
60
  project_id = options["project"]