aegis-stack 0.2.0rc2__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.
Files changed (392) hide show
  1. aegis/__init__.py +5 -0
  2. aegis/__main__.py +51 -0
  3. aegis/cli/__init__.py +6 -0
  4. aegis/cli/callbacks.py +114 -0
  5. aegis/cli/interactive.py +611 -0
  6. aegis/cli/utils.py +70 -0
  7. aegis/cli/validators.py +34 -0
  8. aegis/commands/__init__.py +6 -0
  9. aegis/commands/add.py +353 -0
  10. aegis/commands/add_service.py +332 -0
  11. aegis/commands/components.py +35 -0
  12. aegis/commands/init.py +370 -0
  13. aegis/commands/remove.py +227 -0
  14. aegis/commands/services.py +52 -0
  15. aegis/commands/update.py +252 -0
  16. aegis/commands/version.py +12 -0
  17. aegis/config/__init__.py +1 -0
  18. aegis/config/shared_files.py +136 -0
  19. aegis/core/CLAUDE.md +377 -0
  20. aegis/core/__init__.py +6 -0
  21. aegis/core/component_files.py +228 -0
  22. aegis/core/component_utils.py +220 -0
  23. aegis/core/components.py +127 -0
  24. aegis/core/copier_manager.py +315 -0
  25. aegis/core/copier_updater.py +475 -0
  26. aegis/core/dependency_resolver.py +119 -0
  27. aegis/core/manual_updater.py +554 -0
  28. aegis/core/post_gen_tasks.py +547 -0
  29. aegis/core/service_resolver.py +261 -0
  30. aegis/core/services.py +157 -0
  31. aegis/core/template_generator.py +266 -0
  32. aegis/core/version_compatibility.py +259 -0
  33. aegis/templates/CLAUDE.md +591 -0
  34. aegis/templates/cookiecutter-aegis-project/cookiecutter.json +39 -0
  35. aegis/templates/cookiecutter-aegis-project/hooks/post_gen_project.py +214 -0
  36. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.dockerignore +71 -0
  37. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2 +130 -0
  38. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.gitignore +131 -0
  39. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Dockerfile +53 -0
  40. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Makefile +236 -0
  41. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/README.md.j2 +196 -0
  42. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/alembic.ini.j2 +111 -0
  43. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/env.py.j2 +91 -0
  44. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/script.py.mako +25 -0
  45. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/versions/001_initial_auth.py.j2 +51 -0
  46. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/__init__.py +5 -0
  47. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/__init__.py +6 -0
  48. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai.py.j2 +700 -0
  49. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai_rendering.py +361 -0
  50. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/auth.py.j2 +253 -0
  51. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/health.py.j2 +419 -0
  52. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/load_test.py.j2 +656 -0
  53. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/main.py.j2 +65 -0
  54. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/marko_terminal_renderer.py +489 -0
  55. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/tasks.py.j2 +328 -0
  56. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/{% if cookiecutter.include_scheduler == /"yes/" %}tasks.py{% endif %}" +340 -0
  57. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/__init__.py +0 -0
  58. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/__init__.py +0 -0
  59. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/__init__.py +8 -0
  60. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/router.py +329 -0
  61. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/__init__.py +1 -0
  62. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/router.py +64 -0
  63. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/deps.py +58 -0
  64. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/health.py +163 -0
  65. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/models.py.j2 +280 -0
  66. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/routing.py.j2 +32 -0
  67. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/scheduler.py.j2 +121 -0
  68. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/worker.py.j2 +478 -0
  69. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/hooks.py +144 -0
  70. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/main.py +31 -0
  71. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/__init__.py +1 -0
  72. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/cors.py +20 -0
  73. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/__init__.py +1 -0
  74. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/cleanup.py +14 -0
  75. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/__init__.py +1 -0
  76. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2 +418 -0
  77. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/database_init.py.j2 +83 -0
  78. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/__init__.py +5 -0
  79. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/__init__.py +27 -0
  80. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/table.py +78 -0
  81. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/text.py +142 -0
  82. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/__init__.py.j2 +47 -0
  83. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
  84. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
  85. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/base_card.py +256 -0
  86. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
  87. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
  88. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/database_card.py +420 -0
  89. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
  90. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
  91. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
  92. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
  93. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/services_card.py +233 -0
  94. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
  95. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/main.py.j2 +653 -0
  96. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/theme.py +48 -0
  97. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/__init__.py +1 -0
  98. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/main.py.j2 +156 -0
  99. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/CLAUDE.md.j2 +213 -0
  100. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/__init__.py +6 -0
  101. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/constants.py.j2 +30 -0
  102. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/pools.py +97 -0
  103. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/__init__.py +1 -0
  104. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/load_test.py +55 -0
  105. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/media.py +49 -0
  106. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/system.py +44 -0
  107. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/registry.py +139 -0
  108. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/__init__.py +120 -0
  109. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/load_tasks.py +507 -0
  110. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/simple_system_tasks.py +33 -0
  111. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/system_tasks.py +281 -0
  112. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/config.py.j2 +178 -0
  113. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/constants.py +58 -0
  114. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/db.py.j2 +176 -0
  115. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/log.py +92 -0
  116. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/security.py +62 -0
  117. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/__init__.py +1 -0
  118. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/webserver.py +40 -0
  119. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/{% if cookiecutter.include_scheduler == /"yes/" %}scheduler.py{% endif %}" +21 -0
  120. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/__init__.py +0 -0
  121. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/main.py +62 -0
  122. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/__init__.py +1 -0
  123. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/user.py +44 -0
  124. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/py.typed +0 -0
  125. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/__init__.py +1 -0
  126. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/__init__.py +8 -0
  127. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/config.py +130 -0
  128. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/conversation.py +213 -0
  129. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/health.py +96 -0
  130. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/models.py +229 -0
  131. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/providers.py +370 -0
  132. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/service.py +388 -0
  133. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/__init__.py +1 -0
  134. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/auth_service.py +41 -0
  135. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/health.py +164 -0
  136. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/user_service.py +83 -0
  137. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/middleware_inspector.py.j2 +223 -0
  138. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/models.py.j2 +70 -0
  139. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/route_inspector.py.j2 +155 -0
  140. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test.py +679 -0
  141. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test_models.py +266 -0
  142. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/__init__.py.j2 +21 -0
  143. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/models.py.j2 +119 -0
  144. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/scheduled_task_manager.py.j2 +273 -0
  145. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/task_monitor.py.j2 +189 -0
  146. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/__init__.py +15 -0
  147. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/models.py +26 -0
  148. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/__init__.py +52 -0
  149. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/alerts.py +94 -0
  150. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/backup.py.j2 +119 -0
  151. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2 +1333 -0
  152. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/models.py +243 -0
  153. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/ui.py +52 -0
  154. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-dark.png +0 -0
  155. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-square-backup.png +0 -0
  156. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto.png +0 -0
  157. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.dockerignore +71 -0
  158. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.env.example.j2 +64 -0
  159. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.gitignore +131 -0
  160. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Dockerfile +53 -0
  161. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Makefile +211 -0
  162. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/README.md.j2 +172 -0
  163. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/docker-compose.yml.j2 +78 -0
  164. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/mkdocs.yml.j2 +62 -0
  165. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/pyproject.toml.j2 +120 -0
  166. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/uv.lock +1673 -0
  167. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docker-compose.yml.j2 +200 -0
  168. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/api.md +191 -0
  169. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md +0 -0
  170. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md.j2 +621 -0
  171. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/development.md +215 -0
  172. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/health.md +240 -0
  173. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/javascripts/mermaid-config.js +62 -0
  174. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/stylesheets/mermaid.css +95 -0
  175. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/mkdocs.yml.j2 +62 -0
  176. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/pyproject.toml.j2 +131 -0
  177. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh +87 -0
  178. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh.j2 +93 -0
  179. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/gen_docs.py +16 -0
  180. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/__init__.py +1 -0
  181. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_auth_endpoints.py.j2 +307 -0
  182. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_health_endpoints.py.j2 +262 -0
  183. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_scheduler_endpoints.py.j2 +214 -0
  184. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_worker_endpoints.py.j2 +165 -0
  185. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_ai_rendering.py +427 -0
  186. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_conversation_memory.py +465 -0
  187. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/components/test_scheduler.py +43 -0
  188. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/conftest.py.j2 +195 -0
  189. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/__init__.py +1 -0
  190. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/__init__.py +1 -0
  191. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/conftest.py +78 -0
  192. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_health.py +157 -0
  193. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_models.py +164 -0
  194. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_service.py +198 -0
  195. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_auth_integration.py.j2 +528 -0
  196. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_component_integration.py.j2 +387 -0
  197. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_conversation_persistence.py +342 -0
  198. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_health_logic.py.j2 +663 -0
  199. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_models.py +619 -0
  200. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_service.py +603 -0
  201. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_middleware_inspector.py.j2 +248 -0
  202. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_scheduled_task_manager.py.j2 +292 -0
  203. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_system_service.py +98 -0
  204. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_worker_health_registration.py.j2 +257 -0
  205. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/test_core.py +49 -0
  206. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/uv.lock +1673 -0
  207. aegis/templates/copier-aegis-project/{{ project_slug }}/.copier-answers.yml.jinja +21 -0
  208. aegis/templates/copier-aegis-project/{{ project_slug }}/.dockerignore +71 -0
  209. aegis/templates/copier-aegis-project/{{ project_slug }}/.env.example.jinja +130 -0
  210. aegis/templates/copier-aegis-project/{{ project_slug }}/.gitignore +131 -0
  211. aegis/templates/copier-aegis-project/{{ project_slug }}/Dockerfile +53 -0
  212. aegis/templates/copier-aegis-project/{{ project_slug }}/Makefile.jinja +236 -0
  213. aegis/templates/copier-aegis-project/{{ project_slug }}/README.md.jinja +196 -0
  214. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/alembic.ini.jinja +111 -0
  215. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/env.py.jinja +91 -0
  216. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/script.py.mako +25 -0
  217. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/versions/001_initial_auth.py.jinja +51 -0
  218. aegis/templates/copier-aegis-project/{{ project_slug }}/app/__init__.py.jinja +5 -0
  219. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/__init__.py.jinja +6 -0
  220. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai.py.jinja +700 -0
  221. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai_rendering.py +360 -0
  222. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/auth.py.jinja +253 -0
  223. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/health.py.jinja +419 -0
  224. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/load_test.py.jinja +656 -0
  225. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/main.py.jinja +65 -0
  226. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/marko_terminal_renderer.py +489 -0
  227. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/tasks.py.jinja +328 -0
  228. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/__init__.py +0 -0
  229. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/__init__.py +0 -0
  230. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/__init__.py +8 -0
  231. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/router.py +329 -0
  232. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/__init__.py +1 -0
  233. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/router.py +64 -0
  234. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/deps.py +58 -0
  235. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/health.py.jinja +163 -0
  236. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/models.py.jinja +280 -0
  237. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/routing.py.jinja +32 -0
  238. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/scheduler.py.jinja +121 -0
  239. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/worker.py.jinja +478 -0
  240. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/hooks.py +144 -0
  241. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/main.py +31 -0
  242. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/__init__.py +1 -0
  243. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/cors.py +20 -0
  244. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/__init__.py +1 -0
  245. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/cleanup.py +14 -0
  246. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/__init__.py +1 -0
  247. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/component_health.py.jinja +418 -0
  248. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/database_init.py.jinja +83 -0
  249. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/__init__.py +5 -0
  250. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/__init__.py +27 -0
  251. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/table.py +78 -0
  252. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/text.py +142 -0
  253. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/__init__.py.jinja +47 -0
  254. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
  255. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
  256. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/base_card.py +256 -0
  257. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
  258. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
  259. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/database_card.py +420 -0
  260. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
  261. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
  262. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
  263. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
  264. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/services_card.py +233 -0
  265. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
  266. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/main.py.jinja +653 -0
  267. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/theme.py +48 -0
  268. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/__init__.py +1 -0
  269. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/main.py.jinja +156 -0
  270. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/CLAUDE.md.jinja +213 -0
  271. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/__init__.py +6 -0
  272. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/constants.py.jinja +30 -0
  273. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/pools.py +97 -0
  274. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/__init__.py +1 -0
  275. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/load_test.py +55 -0
  276. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/media.py +49 -0
  277. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/system.py +44 -0
  278. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/registry.py +139 -0
  279. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/__init__.py +120 -0
  280. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/load_tasks.py +507 -0
  281. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/simple_system_tasks.py +33 -0
  282. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/system_tasks.py +281 -0
  283. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/config.py.jinja +178 -0
  284. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/constants.py +58 -0
  285. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/db.py.jinja +176 -0
  286. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/log.py +92 -0
  287. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/security.py +62 -0
  288. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/__init__.py +1 -0
  289. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/scheduler.py.jinja +21 -0
  290. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/webserver.py +39 -0
  291. aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/__init__.py +0 -0
  292. aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/main.py +61 -0
  293. aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/__init__.py +1 -0
  294. aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/user.py +44 -0
  295. aegis/templates/copier-aegis-project/{{ project_slug }}/app/py.typed +0 -0
  296. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/__init__.py +1 -0
  297. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/__init__.py +8 -0
  298. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/config.py +130 -0
  299. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/conversation.py +213 -0
  300. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/health.py +96 -0
  301. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/models.py +229 -0
  302. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/providers.py.jinja +370 -0
  303. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/service.py +387 -0
  304. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/__init__.py +1 -0
  305. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/auth_service.py +40 -0
  306. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/health.py +162 -0
  307. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/user_service.py +82 -0
  308. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/middleware_inspector.py.jinja +223 -0
  309. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/models.py.jinja +70 -0
  310. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/route_inspector.py.jinja +155 -0
  311. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test.py +678 -0
  312. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test_models.py +265 -0
  313. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/__init__.py.jinja +21 -0
  314. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/models.py.jinja +119 -0
  315. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/scheduled_task_manager.py.jinja +273 -0
  316. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/task_monitor.py.jinja +189 -0
  317. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/__init__.py +15 -0
  318. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/models.py +26 -0
  319. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/__init__.py +52 -0
  320. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/alerts.py +94 -0
  321. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/backup.py.jinja +119 -0
  322. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/health.py.jinja +1333 -0
  323. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/models.py +243 -0
  324. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/ui.py +52 -0
  325. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57223!aegis-manifesto.png +0 -0
  326. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57224!aegis-manifesto-dark.png +0 -0
  327. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57225!aegis-manifesto-square-backup.png +0 -0
  328. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57533!aegis-manifesto.png +0 -0
  329. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57534!aegis-manifesto-dark.png +0 -0
  330. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57538!aegis-manifesto-square-backup.png +0 -0
  331. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57897!aegis-manifesto.png +0 -0
  332. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57898!aegis-manifesto-dark.png +0 -0
  333. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57904!aegis-manifesto-square-backup.png +0 -0
  334. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58315!aegis-manifesto.png +0 -0
  335. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58316!aegis-manifesto-dark.png +0 -0
  336. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58324!aegis-manifesto-square-backup.png +0 -0
  337. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58837!aegis-manifesto.png +0 -0
  338. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58838!aegis-manifesto-dark.png +0 -0
  339. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58849!aegis-manifesto-square-backup.png +0 -0
  340. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-dark.png +0 -0
  341. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-square-backup.png +0 -0
  342. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto.png +0 -0
  343. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/.env.example.jinja +64 -0
  344. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/README.md.jinja +172 -0
  345. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/docker-compose.yml.jinja +78 -0
  346. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/mkdocs.yml.jinja +62 -0
  347. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/pyproject.toml.jinja +120 -0
  348. aegis/templates/copier-aegis-project/{{ project_slug }}/docker-compose.yml.jinja +200 -0
  349. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/api.md.jinja +191 -0
  350. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md +0 -0
  351. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md.jinja +621 -0
  352. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/development.md.jinja +215 -0
  353. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/health.md.jinja +240 -0
  354. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/javascripts/mermaid-config.js +62 -0
  355. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/stylesheets/mermaid.css +95 -0
  356. aegis/templates/copier-aegis-project/{{ project_slug }}/mkdocs.yml.jinja +62 -0
  357. aegis/templates/copier-aegis-project/{{ project_slug }}/pyproject.toml.jinja +131 -0
  358. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh +87 -0
  359. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh.jinja +93 -0
  360. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/gen_docs.py +16 -0
  361. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/__init__.py +1 -0
  362. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_auth_endpoints.py.jinja +307 -0
  363. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_health_endpoints.py.jinja +262 -0
  364. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_scheduler_endpoints.py.jinja +214 -0
  365. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_worker_endpoints.py.jinja +165 -0
  366. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_ai_rendering.py +427 -0
  367. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_conversation_memory.py +465 -0
  368. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/components/test_scheduler.py +43 -0
  369. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/conftest.py.jinja +195 -0
  370. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/__init__.py +1 -0
  371. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/__init__.py +1 -0
  372. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/conftest.py +78 -0
  373. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_health.py +157 -0
  374. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_models.py +164 -0
  375. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_service.py +198 -0
  376. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_auth_integration.py.jinja +528 -0
  377. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_component_integration.py.jinja +387 -0
  378. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_conversation_persistence.py +342 -0
  379. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_health_logic.py.jinja +663 -0
  380. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_models.py +619 -0
  381. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_service.py +603 -0
  382. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_middleware_inspector.py.jinja +248 -0
  383. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_scheduled_task_manager.py.jinja +292 -0
  384. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_system_service.py +98 -0
  385. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_worker_health_registration.py.jinja +257 -0
  386. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/test_core.py +49 -0
  387. aegis/templates/copier-aegis-project/{{ project_slug }}/uv.lock +1673 -0
  388. aegis_stack-0.2.0rc2.dist-info/METADATA +165 -0
  389. aegis_stack-0.2.0rc2.dist-info/RECORD +392 -0
  390. aegis_stack-0.2.0rc2.dist-info/WHEEL +4 -0
  391. aegis_stack-0.2.0rc2.dist-info/entry_points.txt +3 -0
  392. aegis_stack-0.2.0rc2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,322 @@
1
+ """
2
+ Stunning Redis/Cache Component Card
3
+
4
+ Modern, visually striking card component that displays real Redis metrics
5
+ from Redis INFO command, including memory usage, cache hit rates, operations
6
+ per second, connection statistics, and comprehensive performance data.
7
+ """
8
+
9
+ import flet as ft
10
+ from app.components.frontend.controls import (
11
+ LabelText,
12
+ MetricText,
13
+ PrimaryText,
14
+ SecondaryText,
15
+ TitleText,
16
+ )
17
+ from app.services.system.models import ComponentStatus, ComponentStatusType
18
+
19
+ from .card_utils import create_responsive_3_section_layout
20
+
21
+
22
+ class RedisCard:
23
+ """
24
+ A visually stunning, wide component card for displaying real Redis metrics.
25
+
26
+ Features:
27
+ - Modern Material Design 3 styling with circular gauge indicators
28
+ - Three-section layout (badge, real-time metrics, performance stats)
29
+ - Real Redis INFO command data: hit rates, memory usage, ops/sec
30
+ - Comprehensive statistics: uptime, keys, connections, evictions
31
+ - Health-aware coloring based on cache performance thresholds
32
+ - Graceful fallback for unavailable metrics
33
+ """
34
+
35
+ def __init__(self, component_data: ComponentStatus) -> None:
36
+ """
37
+ Initialize the Redis card with component data.
38
+
39
+ Args:
40
+ component_data: ComponentStatus containing Redis health and metrics
41
+ """
42
+ self.component_data = component_data
43
+ self._card_container: ft.Container | None = None
44
+
45
+ def _get_status_colors(self) -> tuple[str, str, str]:
46
+ """
47
+ Get status-aware colors for the card.
48
+
49
+ Returns:
50
+ Tuple of (primary_color, background_color, border_color)
51
+ """
52
+ status = self.component_data.status
53
+
54
+ if status == ComponentStatusType.HEALTHY:
55
+ return (ft.Colors.GREEN, ft.Colors.SURFACE, ft.Colors.GREEN)
56
+ elif status == ComponentStatusType.INFO:
57
+ return (ft.Colors.BLUE, ft.Colors.SURFACE, ft.Colors.BLUE)
58
+ elif status == ComponentStatusType.WARNING:
59
+ return (ft.Colors.ORANGE, ft.Colors.SURFACE, ft.Colors.ORANGE)
60
+ else: # UNHEALTHY
61
+ return (ft.Colors.RED, ft.Colors.SURFACE, ft.Colors.RED)
62
+
63
+ def _create_metric_gauge(
64
+ self, label: str, value: float, unit: str, color: str
65
+ ) -> ft.Container:
66
+ """Create a circular gauge-style metric indicator."""
67
+ # Format value appropriately based on size
68
+ if value >= 1000:
69
+ formatted_value = f"{value / 1000:.1f}k"
70
+ elif value >= 1000000:
71
+ formatted_value = f"{value / 1000000:.1f}M"
72
+ else:
73
+ formatted_value = (
74
+ f"{value:.1f}" if isinstance(value, float) else str(int(value))
75
+ )
76
+
77
+ return ft.Container(
78
+ content=ft.Column(
79
+ [
80
+ LabelText(label),
81
+ ft.Container(
82
+ content=ft.Column(
83
+ [
84
+ MetricText(formatted_value),
85
+ LabelText(unit),
86
+ ],
87
+ alignment=ft.MainAxisAlignment.CENTER,
88
+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
89
+ spacing=0,
90
+ ),
91
+ width=60,
92
+ height=60,
93
+ bgcolor=ft.Colors.with_opacity(0.1, color),
94
+ border=ft.border.all(2, color),
95
+ border_radius=30,
96
+ padding=ft.padding.all(4),
97
+ ),
98
+ ],
99
+ alignment=ft.MainAxisAlignment.CENTER,
100
+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
101
+ spacing=4,
102
+ ),
103
+ padding=ft.padding.all(8),
104
+ )
105
+
106
+ def _create_technology_badge(self) -> ft.Container:
107
+ """Create the Redis technology badge section."""
108
+ primary_color, _, _ = self._get_status_colors()
109
+
110
+ return ft.Container(
111
+ content=ft.Column(
112
+ [
113
+ ft.Container(
114
+ content=ft.Text("🗄️", size=32),
115
+ padding=ft.padding.all(8),
116
+ bgcolor=primary_color,
117
+ border_radius=12,
118
+ margin=ft.margin.only(bottom=8),
119
+ ),
120
+ TitleText("Redis"),
121
+ SecondaryText("Cache + Pub/Sub"),
122
+ ft.Container(
123
+ content=LabelText(
124
+ "CACHE",
125
+ color=ft.Colors.WHITE,
126
+ ),
127
+ padding=ft.padding.symmetric(horizontal=8, vertical=2),
128
+ bgcolor=ft.Colors.RED,
129
+ border_radius=8,
130
+ margin=ft.margin.only(top=4),
131
+ ),
132
+ ],
133
+ alignment=ft.MainAxisAlignment.CENTER,
134
+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
135
+ spacing=4,
136
+ ),
137
+ padding=ft.padding.all(16),
138
+ width=160, # Expanded badge width to 160px
139
+ )
140
+
141
+ def _create_metrics_section(self) -> ft.Container:
142
+ """Create the Redis metrics section with memory and connection stats."""
143
+ # Extract real Redis metrics from component metadata
144
+ metadata = self.component_data.metadata or {}
145
+
146
+ # Calculate hit rate with proper color coding
147
+ hit_rate = metadata.get("hit_rate_percent", 0)
148
+ hit_rate_color = (
149
+ ft.Colors.GREEN
150
+ if hit_rate >= 90
151
+ else ft.Colors.ORANGE
152
+ if hit_rate >= 70
153
+ else ft.Colors.RED
154
+ )
155
+
156
+ # Calculate memory usage percentage if maxmemory is set
157
+ used_memory = metadata.get("used_memory", 0)
158
+ max_memory = metadata.get("maxmemory", 0)
159
+
160
+ if max_memory > 0:
161
+ memory_percent = (used_memory / max_memory) * 100
162
+ memory_value = memory_percent
163
+ memory_unit = "%"
164
+ # Set color based on memory usage percentage
165
+ if memory_percent >= 90:
166
+ memory_color = ft.Colors.RED
167
+ elif memory_percent >= 70:
168
+ memory_color = ft.Colors.ORANGE
169
+ else:
170
+ memory_color = ft.Colors.BLUE
171
+ else:
172
+ # Show absolute memory usage in MB
173
+ memory_value = used_memory / (1024 * 1024) if used_memory > 0 else 0
174
+ memory_unit = "MB"
175
+ memory_color = ft.Colors.BLUE
176
+
177
+ # Get operations per second
178
+ ops_per_sec = metadata.get("instantaneous_ops_per_sec", 0)
179
+
180
+ redis_metrics = {
181
+ "hit_ratio": {
182
+ "value": hit_rate,
183
+ "unit": "%",
184
+ "color": hit_rate_color,
185
+ },
186
+ "memory": {
187
+ "value": memory_value,
188
+ "unit": memory_unit,
189
+ "color": memory_color,
190
+ },
191
+ "ops_sec": {
192
+ "value": ops_per_sec,
193
+ "unit": "/sec",
194
+ "color": ft.Colors.PURPLE,
195
+ },
196
+ }
197
+
198
+ metrics_controls = []
199
+ for metric_key, data in redis_metrics.items():
200
+ label = metric_key.replace("_", " ").replace("ops sec", "Ops").title()
201
+ metrics_controls.append(
202
+ self._create_metric_gauge(
203
+ label,
204
+ data["value"],
205
+ data["unit"],
206
+ data["color"],
207
+ )
208
+ )
209
+
210
+ return ft.Column(
211
+ [
212
+ PrimaryText("Cache Metrics"),
213
+ ft.Divider(height=1, color=ft.Colors.GREY_300),
214
+ ft.Row(
215
+ metrics_controls, spacing=15, alignment=ft.MainAxisAlignment.CENTER
216
+ ),
217
+ ],
218
+ spacing=8,
219
+ )
220
+
221
+ def _create_performance_section(self) -> ft.Container:
222
+ """Create the Redis performance and statistics section."""
223
+
224
+ # Extract real Redis performance stats from metadata
225
+ metadata = self.component_data.metadata or {}
226
+
227
+ # Format uptime from seconds to human readable
228
+ uptime_seconds = metadata.get("uptime_in_seconds", 0)
229
+ uptime_days = uptime_seconds // 86400
230
+ uptime_hours = (uptime_seconds % 86400) // 3600
231
+ uptime_str = (
232
+ f"{uptime_days}d {uptime_hours}h" if uptime_days > 0 else f"{uptime_hours}h"
233
+ )
234
+
235
+ # Format numbers with commas
236
+ def format_number(num: int | float) -> str:
237
+ if isinstance(num, float):
238
+ return f"{num:,.1f}"
239
+ return f"{num:,}"
240
+
241
+ performance_stats = {
242
+ "Uptime": uptime_str,
243
+ "Commands/sec": format_number(metadata.get("instantaneous_ops_per_sec", 0)),
244
+ "Total Keys": format_number(metadata.get("total_keys", 0)),
245
+ "Memory Peak": metadata.get("used_memory_peak_human", "unknown"),
246
+ "Connected Clients": format_number(metadata.get("connected_clients", 0)),
247
+ "Evicted Keys": format_number(metadata.get("evicted_keys", 0)),
248
+ "Fragmentation": f"{metadata.get('mem_fragmentation_ratio', 1.0):.2f}",
249
+ }
250
+
251
+ perf_content = [
252
+ PrimaryText("Performance"),
253
+ ft.Divider(height=1, color=ft.Colors.GREY_300),
254
+ ]
255
+
256
+ for stat_name, stat_value in performance_stats.items():
257
+ perf_content.append(
258
+ ft.Row(
259
+ [
260
+ SecondaryText(f"{stat_name}:"),
261
+ LabelText(stat_value),
262
+ ],
263
+ alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
264
+ )
265
+ )
266
+
267
+ # Add status info
268
+ perf_content.extend(
269
+ [
270
+ ft.Divider(height=1, color=ft.Colors.GREY_300),
271
+ ft.Row(
272
+ [
273
+ SecondaryText("Status:"),
274
+ LabelText(
275
+ self.component_data.status.value.title(),
276
+ color=self._get_status_colors()[0],
277
+ ),
278
+ ],
279
+ alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
280
+ ),
281
+ ]
282
+ )
283
+
284
+ # Wrap in a scrollable container to handle overflow
285
+ return ft.Container(
286
+ content=ft.Column(
287
+ [
288
+ ft.Container(
289
+ content=ft.Column(
290
+ perf_content,
291
+ spacing=6,
292
+ scroll=ft.ScrollMode.AUTO,
293
+ ),
294
+ height=240, # Increased height to show more stats
295
+ )
296
+ ]
297
+ ),
298
+ padding=ft.padding.only(right=4), # Add padding for scrollbar space
299
+ )
300
+
301
+ def build(self) -> ft.Container:
302
+ """Build and return the complete Redis card with responsive layout."""
303
+ primary_color, background_color, border_color = self._get_status_colors()
304
+
305
+ # Use shared responsive 3-section layout prioritizing middle section
306
+ content = create_responsive_3_section_layout(
307
+ left_content=self._create_technology_badge(),
308
+ middle_content=self._create_metrics_section(),
309
+ right_content=self._create_performance_section(),
310
+ )
311
+
312
+ self._card_container = ft.Container(
313
+ content=content,
314
+ bgcolor=ft.Colors.SURFACE,
315
+ border=ft.border.all(1, border_color),
316
+ border_radius=16,
317
+ padding=0,
318
+ width=None, # Let ResponsiveRow handle the width
319
+ height=280,
320
+ )
321
+
322
+ return self._card_container
@@ -0,0 +1,352 @@
1
+ """
2
+ Stunning Scheduler Component Card
3
+
4
+ Modern, visually striking card component that displays scheduled jobs,
5
+ job statistics, and scheduling information using shared utility functions.
6
+ """
7
+
8
+ from datetime import UTC
9
+
10
+ import flet as ft
11
+ from app.components.frontend.controls import PrimaryText
12
+ from app.services.system.models import ComponentStatus
13
+
14
+ from .card_utils import (
15
+ create_hover_handler,
16
+ create_responsive_3_section_layout,
17
+ create_standard_card_container,
18
+ create_stats_row,
19
+ create_tech_badge,
20
+ get_status_colors,
21
+ )
22
+
23
+
24
+ class SchedulerCard:
25
+ """
26
+ Visually stunning, wide component card for displaying Scheduler/APScheduler metrics.
27
+
28
+ Features:
29
+ - Modern Material Design 3 styling
30
+ - Three-section layout (badge, jobs, stats)
31
+ - Scheduled job indicators with next run times
32
+ - Job statistics and scheduler status
33
+ - Status-aware coloring and hover effects
34
+ - 800px width for optimal content spacing
35
+ """
36
+
37
+ def __init__(self, component_data: ComponentStatus) -> None:
38
+ """
39
+ Initialize the Scheduler card with component data.
40
+
41
+ Args:
42
+ component_data: ComponentStatus containing scheduler health and metrics
43
+ """
44
+ self.component_data = component_data
45
+ self._card_container: ft.Container | None = None
46
+
47
+ def _format_next_run_time(self, iso_time_str: str) -> str:
48
+ """Format ISO datetime string to human readable relative time."""
49
+ if not iso_time_str:
50
+ return "Unknown"
51
+
52
+ try:
53
+ from datetime import datetime
54
+
55
+ # Handle both timezone-aware and naive datetimes
56
+ if iso_time_str.endswith("Z"):
57
+ next_run = datetime.fromisoformat(iso_time_str.replace("Z", "+00:00"))
58
+ elif "+" in iso_time_str or iso_time_str.endswith("00:00"):
59
+ next_run = datetime.fromisoformat(iso_time_str)
60
+ else:
61
+ # Assume UTC if no timezone info
62
+ next_run = datetime.fromisoformat(iso_time_str).replace(tzinfo=UTC)
63
+
64
+ now = datetime.now(UTC)
65
+
66
+ # Make sure both datetimes are timezone-aware
67
+ if next_run.tzinfo is None:
68
+ next_run = next_run.replace(tzinfo=UTC)
69
+
70
+ delta = next_run - now
71
+ total_seconds = delta.total_seconds()
72
+
73
+ if total_seconds < 0:
74
+ return "Past due"
75
+ elif total_seconds < 60:
76
+ return f"in {int(total_seconds)}s"
77
+ elif total_seconds < 3600:
78
+ minutes = int(total_seconds / 60)
79
+ return f"in {minutes}m"
80
+ elif total_seconds < 86400:
81
+ hours = total_seconds / 3600
82
+ if hours < 2:
83
+ return f"in {hours:.1f}h"
84
+ else:
85
+ return f"in {int(hours)}h"
86
+ else:
87
+ days = int(total_seconds / 86400)
88
+ return f"in {days}d"
89
+ except Exception:
90
+ return "Unknown"
91
+
92
+ def _format_schedule_human_readable(self, schedule: str) -> str:
93
+ """Convert schedule format to human readable."""
94
+ if not schedule or "Unknown" in schedule:
95
+ return "Unknown schedule"
96
+
97
+ # Handle common cron patterns
98
+ if "hour=2, minute=0, second=0" in schedule:
99
+ return "Daily at 2:00 AM UTC"
100
+ elif "hour=" in schedule and "minute=" in schedule:
101
+ # Extract hour and minute from the schedule string
102
+ try:
103
+ import re
104
+
105
+ hour_match = re.search(r"hour=([0-9]+)", schedule)
106
+ minute_match = re.search(r"minute=([0-9]+)", schedule)
107
+ if hour_match and minute_match:
108
+ hour = int(hour_match.group(1))
109
+ minute = int(minute_match.group(1))
110
+ time_str = f"{hour:02d}:{minute:02d}"
111
+ return f"Daily at {time_str} UTC"
112
+ except Exception:
113
+ pass
114
+
115
+ # Fallback to original schedule
116
+ return schedule
117
+
118
+ def _handle_job_hover_simple(self, e: ft.ControlEvent) -> None:
119
+ """Simple hover handler that works with event source."""
120
+ container = e.control
121
+ buttons = container.content.controls[0].controls[2] # Access the buttons row
122
+
123
+ if e.data == "true": # Mouse enter
124
+ container.border = ft.border.all(1, ft.Colors.GREY_400)
125
+ buttons.opacity = 1.0
126
+ else: # Mouse leave
127
+ container.border = ft.border.all(1, ft.Colors.TRANSPARENT)
128
+ buttons.opacity = 0.0
129
+ container.update()
130
+
131
+ def _create_technology_badge(self) -> ft.Container:
132
+ """Create the Scheduler technology badge section."""
133
+ primary_color, _, _ = get_status_colors(self.component_data)
134
+
135
+ return create_tech_badge(
136
+ title="Scheduler",
137
+ subtitle="APScheduler",
138
+ icon="⏰",
139
+ badge_text="JOBS",
140
+ badge_color=ft.Colors.TEAL,
141
+ primary_color=primary_color,
142
+ width=160,
143
+ )
144
+
145
+ def _create_jobs_section(self) -> ft.Container:
146
+ """Create the scheduled jobs section with job list."""
147
+ # Get real scheduled jobs from component metadata
148
+ upcoming_tasks = []
149
+ if (
150
+ self.component_data.metadata
151
+ and "upcoming_tasks" in self.component_data.metadata
152
+ ):
153
+ upcoming_tasks = self.component_data.metadata["upcoming_tasks"]
154
+
155
+ job_list_items = []
156
+ for task in upcoming_tasks:
157
+ # Format next run time
158
+ next_run_display = self._format_next_run_time(task.get("next_run", ""))
159
+ schedule = task.get("schedule", "Unknown schedule")
160
+
161
+ job_list_items.append(
162
+ ft.Container(
163
+ content=ft.Column(
164
+ [
165
+ ft.Row(
166
+ [
167
+ ft.Icon(
168
+ ft.Icons.SCHEDULE, size=16, color=ft.Colors.GREY
169
+ ),
170
+ ft.Text(
171
+ task.get("name", task.get("id", "Unknown")),
172
+ size=15,
173
+ weight=ft.FontWeight.W_500,
174
+ color=ft.Colors.ON_SURFACE,
175
+ expand=True,
176
+ ),
177
+ ft.Row(
178
+ [
179
+ ft.IconButton(
180
+ icon=ft.Icons.PAUSE_CIRCLE_OUTLINE,
181
+ icon_size=16,
182
+ icon_color=ft.Colors.GREY,
183
+ tooltip="Pause job",
184
+ on_click=lambda _: None,
185
+ style=ft.ButtonStyle(
186
+ padding=ft.padding.all(2),
187
+ ),
188
+ ),
189
+ ft.IconButton(
190
+ icon=ft.Icons.DELETE_OUTLINE,
191
+ icon_size=16,
192
+ icon_color=ft.Colors.GREY,
193
+ tooltip="Delete job",
194
+ on_click=lambda _: None,
195
+ style=ft.ButtonStyle(
196
+ padding=ft.padding.all(2),
197
+ ),
198
+ ),
199
+ ],
200
+ spacing=0,
201
+ opacity=0.0, # Hidden by default
202
+ ),
203
+ ],
204
+ spacing=8,
205
+ alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
206
+ ),
207
+ ft.Row(
208
+ [
209
+ ft.Text(
210
+ next_run_display,
211
+ size=12,
212
+ color=ft.Colors.GREY,
213
+ ),
214
+ ft.Text("|", size=12, color=ft.Colors.GREY),
215
+ ft.Text(
216
+ self._format_schedule_human_readable(schedule),
217
+ size=12,
218
+ color=ft.Colors.GREY,
219
+ ),
220
+ ],
221
+ spacing=6,
222
+ ),
223
+ ],
224
+ spacing=6,
225
+ ),
226
+ padding=ft.padding.symmetric(vertical=2, horizontal=8),
227
+ border=ft.border.all(1, ft.Colors.TRANSPARENT),
228
+ border_radius=8,
229
+ on_hover=self._handle_job_hover_simple,
230
+ )
231
+ )
232
+
233
+ # Add placeholder if no jobs
234
+ if not job_list_items:
235
+ job_list_items.append(
236
+ ft.Container(
237
+ content=ft.Row(
238
+ [
239
+ ft.Icon(
240
+ ft.Icons.SCHEDULE_OUTLINED,
241
+ size=20,
242
+ color=ft.Colors.GREY,
243
+ ),
244
+ ft.Text("No active jobs", color=ft.Colors.GREY),
245
+ ],
246
+ spacing=8,
247
+ alignment=ft.MainAxisAlignment.CENTER,
248
+ ),
249
+ padding=ft.padding.all(20),
250
+ bgcolor=ft.Colors.with_opacity(0.05, ft.Colors.GREY),
251
+ border_radius=12,
252
+ )
253
+ )
254
+
255
+ return ft.Container(
256
+ content=ft.Container(
257
+ content=ft.Column(
258
+ job_list_items,
259
+ spacing=2,
260
+ scroll=ft.ScrollMode.AUTO,
261
+ ),
262
+ height=250, # Fixed height to force scrolling
263
+ padding=ft.padding.all(
264
+ 0
265
+ ), # Remove any default padding from inner container
266
+ ),
267
+ width=400, # Section width
268
+ padding=ft.padding.only(left=12, right=12, bottom=12, top=0),
269
+ alignment=ft.alignment.top_left,
270
+ )
271
+
272
+ def _create_stats_section(self) -> ft.Container:
273
+ """Create the scheduler statistics section."""
274
+ # Get real scheduler stats from component metadata
275
+ metadata = self.component_data.metadata or {}
276
+
277
+ total_tasks = str(metadata.get("total_tasks", 0))
278
+ active_tasks = str(metadata.get("active_tasks", 0))
279
+ paused_tasks = str(metadata.get("paused_tasks", 0))
280
+
281
+ # Get next job info
282
+ upcoming_tasks = metadata.get("upcoming_tasks", [])
283
+ next_job = "None"
284
+ if upcoming_tasks:
285
+ next_job = self._format_next_run_time(upcoming_tasks[0].get("next_run", ""))
286
+
287
+ scheduler_stats = {
288
+ "Total Tasks": total_tasks,
289
+ "Active Tasks": active_tasks,
290
+ "Paused Tasks": paused_tasks,
291
+ "Next Task": next_job,
292
+ }
293
+
294
+ stats_content = [
295
+ PrimaryText("Task Statistics"),
296
+ ft.Divider(height=1, color=ft.Colors.GREY_300),
297
+ ]
298
+
299
+ # Add all stats using the utility function
300
+ for stat_name, stat_value in scheduler_stats.items():
301
+ stats_content.append(create_stats_row(stat_name, stat_value))
302
+
303
+ # Add scheduler status
304
+ stats_content.extend(
305
+ [
306
+ ft.Divider(height=1, color=ft.Colors.GREY_300),
307
+ create_stats_row(
308
+ "Status",
309
+ self.component_data.status.value.title(),
310
+ get_status_colors(self.component_data)[0],
311
+ ),
312
+ ]
313
+ )
314
+
315
+ return ft.Container(
316
+ content=ft.Column(
317
+ stats_content,
318
+ spacing=8,
319
+ alignment=ft.MainAxisAlignment.START,
320
+ ),
321
+ padding=ft.padding.all(16),
322
+ width=240, # Stats section width
323
+ alignment=ft.alignment.top_left,
324
+ )
325
+
326
+ def build(self) -> ft.Container:
327
+ """Build and return the complete Scheduler card with responsive layout."""
328
+ primary_color, background_color, border_color = get_status_colors(
329
+ self.component_data
330
+ )
331
+
332
+ # Use shared responsive 3-section layout prioritizing middle section
333
+ content = create_responsive_3_section_layout(
334
+ left_content=self._create_technology_badge(),
335
+ middle_content=self._create_jobs_section(),
336
+ right_content=self._create_stats_section(),
337
+ )
338
+
339
+ self._card_container = create_standard_card_container(
340
+ content=content,
341
+ primary_color=primary_color,
342
+ border_color=border_color,
343
+ width=None, # Let ResponsiveRow handle the width
344
+ hover_handler=create_hover_handler(
345
+ None
346
+ ), # Will set after container creation
347
+ )
348
+
349
+ # Set the hover handler with the actual container
350
+ # Hover effects disabled
351
+
352
+ return self._card_container