quantum-framework 0.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/admin.py +131 -0
  4. quantum/cli/check.py +223 -0
  5. quantum/cli/commands/__init__.py +15 -0
  6. quantum/cli/commands/build.py +414 -0
  7. quantum/cli/commands/dev.py +185 -0
  8. quantum/cli/commands/docs.py +329 -0
  9. quantum/cli/commands/lint.py +523 -0
  10. quantum/cli/commands/migrate.py +548 -0
  11. quantum/cli/commands/new.py +622 -0
  12. quantum/cli/commands/serve.py +190 -0
  13. quantum/cli/commands/test.py +193 -0
  14. quantum/cli/deploy.py +810 -0
  15. quantum/cli/hot_reload.py +951 -0
  16. quantum/cli/jobs.py +356 -0
  17. quantum/cli/mq.py +582 -0
  18. quantum/cli/pkg.py +390 -0
  19. quantum/cli/runner.py +565 -0
  20. quantum/cli/server_process.py +159 -0
  21. quantum/cli/utils.py +334 -0
  22. quantum/compiler/__init__.py +30 -0
  23. quantum/compiler/base_generator.py +367 -0
  24. quantum/compiler/cli.py +295 -0
  25. quantum/compiler/expression_transformer.py +444 -0
  26. quantum/compiler/javascript/__init__.py +10 -0
  27. quantum/compiler/javascript/generator.py +659 -0
  28. quantum/compiler/optimizer.py +270 -0
  29. quantum/compiler/python/__init__.py +10 -0
  30. quantum/compiler/python/generator.py +883 -0
  31. quantum/compiler/python/runtime.py +863 -0
  32. quantum/compiler/transpiler.py +330 -0
  33. quantum/core/__init__.py +3 -0
  34. quantum/core/ast_nodes.py +2619 -0
  35. quantum/core/config_env.py +54 -0
  36. quantum/core/docs_links.py +8 -0
  37. quantum/core/expression_diagnostics.py +87 -0
  38. quantum/core/expression_stdlib.py +208 -0
  39. quantum/core/expressions.py +677 -0
  40. quantum/core/features/agents/src/__init__.py +30 -0
  41. quantum/core/features/agents/src/ast_node.py +540 -0
  42. quantum/core/features/conditionals/src/__init__.py +8 -0
  43. quantum/core/features/conditionals/src/ast_node.py +68 -0
  44. quantum/core/features/data_import/src/__init__.py +0 -0
  45. quantum/core/features/data_import/src/ast_node.py +292 -0
  46. quantum/core/features/data_import/src/runtime.py +533 -0
  47. quantum/core/features/dump/src/__init__.py +12 -0
  48. quantum/core/features/dump/src/ast_node.py +106 -0
  49. quantum/core/features/dump/src/parser.py +61 -0
  50. quantum/core/features/dump/src/runtime.py +246 -0
  51. quantum/core/features/functions/src/__init__.py +8 -0
  52. quantum/core/features/functions/src/ast_node.py +149 -0
  53. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  54. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  55. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  56. quantum/core/features/invocation/src/__init__.py +0 -0
  57. quantum/core/features/invocation/src/ast_node.py +147 -0
  58. quantum/core/features/invocation/src/runtime.py +332 -0
  59. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  60. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  61. quantum/core/features/knowledge_base/src/parser.py +82 -0
  62. quantum/core/features/logging/src/__init__.py +12 -0
  63. quantum/core/features/logging/src/ast_node.py +111 -0
  64. quantum/core/features/logging/src/parser.py +50 -0
  65. quantum/core/features/logging/src/runtime.py +190 -0
  66. quantum/core/features/loops/src/__init__.py +8 -0
  67. quantum/core/features/loops/src/ast_node.py +60 -0
  68. quantum/core/features/query/src/__init__.py +0 -0
  69. quantum/core/features/query/src/query_validators.py +20 -0
  70. quantum/core/features/state_management/src/__init__.py +11 -0
  71. quantum/core/features/state_management/src/ast_node.py +121 -0
  72. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  73. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  74. quantum/core/features/terminal_engine/src/parser.py +361 -0
  75. quantum/core/features/testing_engine/src/__init__.py +41 -0
  76. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  77. quantum/core/features/testing_engine/src/parser.py +604 -0
  78. quantum/core/features/theming/src/__init__.py +48 -0
  79. quantum/core/features/theming/src/ast_node.py +137 -0
  80. quantum/core/features/theming/src/presets.py +405 -0
  81. quantum/core/features/ui_engine/src/__init__.py +20 -0
  82. quantum/core/features/ui_engine/src/ast_nodes.py +1913 -0
  83. quantum/core/features/ui_engine/src/parser.py +1192 -0
  84. quantum/core/features/websocket/src/__init__.py +24 -0
  85. quantum/core/features/websocket/src/ast_node.py +247 -0
  86. quantum/core/guards.py +53 -0
  87. quantum/core/html_compat.py +299 -0
  88. quantum/core/parser.py +1551 -0
  89. quantum/core/parser_registry.py +213 -0
  90. quantum/core/parsers/__init__.py +76 -0
  91. quantum/core/parsers/ai/__init__.py +12 -0
  92. quantum/core/parsers/ai/agent_parser.py +106 -0
  93. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  94. quantum/core/parsers/ai/llm_parser.py +78 -0
  95. quantum/core/parsers/ai/team_parser.py +88 -0
  96. quantum/core/parsers/base.py +322 -0
  97. quantum/core/parsers/composition/__init__.py +10 -0
  98. quantum/core/parsers/composition/import_parser.py +50 -0
  99. quantum/core/parsers/composition/slot_parser.py +48 -0
  100. quantum/core/parsers/control_flow/__init__.py +11 -0
  101. quantum/core/parsers/control_flow/if_parser.py +71 -0
  102. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  103. quantum/core/parsers/control_flow/set_parser.py +93 -0
  104. quantum/core/parsers/data/__init__.py +12 -0
  105. quantum/core/parsers/data/data_parser.py +229 -0
  106. quantum/core/parsers/data/invoke_parser.py +133 -0
  107. quantum/core/parsers/data/query_parser.py +213 -0
  108. quantum/core/parsers/data/transaction_parser.py +86 -0
  109. quantum/core/parsers/events/__init__.py +9 -0
  110. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  111. quantum/core/parsers/forms/__init__.py +11 -0
  112. quantum/core/parsers/forms/action_parser.py +79 -0
  113. quantum/core/parsers/forms/flash_parser.py +34 -0
  114. quantum/core/parsers/forms/redirect_parser.py +33 -0
  115. quantum/core/parsers/functions/__init__.py +11 -0
  116. quantum/core/parsers/functions/function_parser.py +98 -0
  117. quantum/core/parsers/functions/param_parser.py +73 -0
  118. quantum/core/parsers/functions/return_parser.py +31 -0
  119. quantum/core/parsers/html/__init__.py +10 -0
  120. quantum/core/parsers/html/component_call_parser.py +130 -0
  121. quantum/core/parsers/html/html_parser.py +115 -0
  122. quantum/core/parsers/jobs/__init__.py +11 -0
  123. quantum/core/parsers/jobs/job_parser.py +71 -0
  124. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  125. quantum/core/parsers/jobs/thread_parser.py +57 -0
  126. quantum/core/parsers/messaging/__init__.py +17 -0
  127. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  128. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_parser.py +114 -0
  130. quantum/core/parsers/messaging/queue_parser.py +61 -0
  131. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  132. quantum/core/parsers/routing/__init__.py +9 -0
  133. quantum/core/parsers/routing/route_parser.py +41 -0
  134. quantum/core/parsers/scripting/__init__.py +12 -0
  135. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  136. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  137. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  138. quantum/core/parsers/scripting/python_parser.py +49 -0
  139. quantum/core/parsers/services/__init__.py +12 -0
  140. quantum/core/parsers/services/dump_parser.py +52 -0
  141. quantum/core/parsers/services/file_parser.py +48 -0
  142. quantum/core/parsers/services/log_parser.py +46 -0
  143. quantum/core/parsers/services/mail_parser.py +65 -0
  144. quantum/core/tiers.py +164 -0
  145. quantum/core/xml_lines.py +81 -0
  146. quantum/packages/__init__.py +28 -0
  147. quantum/packages/manager.py +413 -0
  148. quantum/packages/manifest.py +351 -0
  149. quantum/packages/registry.py +399 -0
  150. quantum/packages/resolver.py +336 -0
  151. quantum/plugins/__init__.py +33 -0
  152. quantum/plugins/hooks.py +329 -0
  153. quantum/plugins/loader.py +479 -0
  154. quantum/plugins/manifest.py +336 -0
  155. quantum/plugins/registry.py +371 -0
  156. quantum/runtime/__init__.py +25 -0
  157. quantum/runtime/action_handler.py +532 -0
  158. quantum/runtime/adapters/__init__.py +88 -0
  159. quantum/runtime/adapters/memory_adapter.py +690 -0
  160. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  161. quantum/runtime/adapters/redis_adapter.py +582 -0
  162. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  163. quantum/runtime/agent_service.py +1133 -0
  164. quantum/runtime/ast_cache.py +506 -0
  165. quantum/runtime/auth_service.py +267 -0
  166. quantum/runtime/component.py +1048 -0
  167. quantum/runtime/component_composer.py +133 -0
  168. quantum/runtime/component_resolver.py +174 -0
  169. quantum/runtime/conditions.py +54 -0
  170. quantum/runtime/database_service.py +595 -0
  171. quantum/runtime/db_schema.py +100 -0
  172. quantum/runtime/dev_panel.py +178 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +283 -0
  176. quantum/runtime/executor_registry.py +178 -0
  177. quantum/runtime/executors/__init__.py +71 -0
  178. quantum/runtime/executors/ai/__init__.py +12 -0
  179. quantum/runtime/executors/ai/agent_executor.py +217 -0
  180. quantum/runtime/executors/ai/knowledge_executor.py +114 -0
  181. quantum/runtime/executors/ai/llm_executor.py +153 -0
  182. quantum/runtime/executors/ai/team_executor.py +171 -0
  183. quantum/runtime/executors/base.py +264 -0
  184. quantum/runtime/executors/control_flow/__init__.py +12 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +98 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +308 -0
  187. quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
  188. quantum/runtime/executors/control_flow/set_executor.py +464 -0
  189. quantum/runtime/executors/data/__init__.py +12 -0
  190. quantum/runtime/executors/data/data_executor.py +156 -0
  191. quantum/runtime/executors/data/invoke_executor.py +228 -0
  192. quantum/runtime/executors/data/query_executor.py +290 -0
  193. quantum/runtime/executors/data/transaction_executor.py +91 -0
  194. quantum/runtime/executors/jobs/__init__.py +11 -0
  195. quantum/runtime/executors/jobs/job_executor.py +190 -0
  196. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  197. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  198. quantum/runtime/executors/messaging/__init__.py +17 -0
  199. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  200. quantum/runtime/executors/messaging/message_executor.py +174 -0
  201. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  202. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  203. quantum/runtime/executors/scripting/__init__.py +11 -0
  204. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  205. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  206. quantum/runtime/executors/scripting/python_executor.py +249 -0
  207. quantum/runtime/executors/services/__init__.py +12 -0
  208. quantum/runtime/executors/services/dump_executor.py +72 -0
  209. quantum/runtime/executors/services/file_executor.py +89 -0
  210. quantum/runtime/executors/services/log_executor.py +77 -0
  211. quantum/runtime/executors/services/mail_executor.py +81 -0
  212. quantum/runtime/expression_cache.py +498 -0
  213. quantum/runtime/file_upload_service.py +326 -0
  214. quantum/runtime/function_registry.py +118 -0
  215. quantum/runtime/game_builder.py +166 -0
  216. quantum/runtime/game_code_generator.py +2371 -0
  217. quantum/runtime/game_templates.py +2006 -0
  218. quantum/runtime/godot_code_generator.py +4681 -0
  219. quantum/runtime/godot_templates.py +1449 -0
  220. quantum/runtime/job_executor.py +1599 -0
  221. quantum/runtime/knowledge_service.py +546 -0
  222. quantum/runtime/llm_cache.py +100 -0
  223. quantum/runtime/llm_providers.py +704 -0
  224. quantum/runtime/llm_service.py +287 -0
  225. quantum/runtime/local_server.py +28 -0
  226. quantum/runtime/logging_setup.py +141 -0
  227. quantum/runtime/message_broker.py +364 -0
  228. quantum/runtime/message_queue_service.py +571 -0
  229. quantum/runtime/param_validation.py +218 -0
  230. quantum/runtime/pypy_compat.py +317 -0
  231. quantum/runtime/python_bridge.py +698 -0
  232. quantum/runtime/query_validators.py +304 -0
  233. quantum/runtime/renderer.py +734 -0
  234. quantum/runtime/service_container.py +470 -0
  235. quantum/runtime/table_params.py +163 -0
  236. quantum/runtime/terminal_builder.py +76 -0
  237. quantum/runtime/terminal_code_generator.py +607 -0
  238. quantum/runtime/terminal_templates.py +243 -0
  239. quantum/runtime/testing_builder.py +77 -0
  240. quantum/runtime/testing_code_generator.py +833 -0
  241. quantum/runtime/testing_templates.py +85 -0
  242. quantum/runtime/ui_builder.py +222 -0
  243. quantum/runtime/ui_console.py +405 -0
  244. quantum/runtime/ui_desktop.py +56 -0
  245. quantum/runtime/ui_form_rules.py +193 -0
  246. quantum/runtime/ui_html_adapter.py +2567 -0
  247. quantum/runtime/ui_html_templates.py +2106 -0
  248. quantum/runtime/ui_mobile_adapter.py +1832 -0
  249. quantum/runtime/ui_mobile_templates.py +1003 -0
  250. quantum/runtime/ui_page_renderer.py +392 -0
  251. quantum/runtime/ui_pager.py +73 -0
  252. quantum/runtime/ui_table_edit.py +139 -0
  253. quantum/runtime/ui_textual_adapter.py +1866 -0
  254. quantum/runtime/ui_textual_templates.py +45 -0
  255. quantum/runtime/ui_tokens.py +465 -0
  256. quantum/runtime/ui_validator.py +365 -0
  257. quantum/runtime/ui_view.py +221 -0
  258. quantum/runtime/validators.py +256 -0
  259. quantum/runtime/web_server.py +2131 -0
  260. quantum/runtime/websocket_adapter.py +501 -0
  261. quantum/runtime/websocket_service.py +585 -0
  262. quantum/runtime/websocket_transport.py +289 -0
  263. quantum/runtime/wsgi.py +101 -0
  264. quantum/services.py +109 -0
  265. quantum/utils/__init__.py +1 -0
  266. quantum_admin/__init__.py +4 -0
  267. quantum_admin/assets/quantum-admin-connectors.js +123 -0
  268. quantum_admin/assets/quantum-admin.css +2058 -0
  269. quantum_admin/assets/quantum-source-highlight.js +106 -0
  270. quantum_admin/backend/__init__.py +4 -0
  271. quantum_admin/backend/audit_service.py +346 -0
  272. quantum_admin/backend/auth_service.py +394 -0
  273. quantum_admin/backend/component_discovery.py +766 -0
  274. quantum_admin/backend/config_generator.py +586 -0
  275. quantum_admin/backend/connector_service.py +1016 -0
  276. quantum_admin/backend/crud.py +557 -0
  277. quantum_admin/backend/database.py +226 -0
  278. quantum_admin/backend/db_setup_service.py +193 -0
  279. quantum_admin/backend/deploy_service.py +698 -0
  280. quantum_admin/backend/docker_service.py +422 -0
  281. quantum_admin/backend/environment_service.py +418 -0
  282. quantum_admin/backend/git_service.py +370 -0
  283. quantum_admin/backend/health_service.py +390 -0
  284. quantum_admin/backend/job_service.py +347 -0
  285. quantum_admin/backend/log_service.py +331 -0
  286. quantum_admin/backend/main.py +14426 -0
  287. quantum_admin/backend/models.py +986 -0
  288. quantum_admin/backend/pipeline_service.py +812 -0
  289. quantum_admin/backend/resource_manager.py +1118 -0
  290. quantum_admin/backend/schemas.py +419 -0
  291. quantum_admin/backend/secret_manager.py +277 -0
  292. quantum_admin/backend/settings_service.py +488 -0
  293. quantum_admin/backend/template_service.py +710 -0
  294. quantum_admin/backend/test_execution_service.py +364 -0
  295. quantum_admin/backend/test_generator.py +782 -0
  296. quantum_admin/backend/webhook_service.py +454 -0
  297. quantum_admin/backend/websocket_manager.py +360 -0
  298. quantum_admin/components/admin/_layout/AdminShell.q +121 -0
  299. quantum_admin/components/admin/agents.q +107 -0
  300. quantum_admin/components/admin/app/[name].q +558 -0
  301. quantum_admin/components/admin/applications.q +160 -0
  302. quantum_admin/components/admin/component/[...path].q +205 -0
  303. quantum_admin/components/admin/components.q +59 -0
  304. quantum_admin/components/admin/connectors.q +283 -0
  305. quantum_admin/components/admin/dashboard.q +86 -0
  306. quantum_admin/components/admin/database.q +95 -0
  307. quantum_admin/components/admin/features.q +69 -0
  308. quantum_admin/components/admin/index.q +12 -0
  309. quantum_admin/components/admin/jobs.q +75 -0
  310. quantum_admin/components/admin/login.q +65 -0
  311. quantum_admin/components/admin/logout.q +11 -0
  312. quantum_admin/components/admin/settings.q +174 -0
  313. quantum_admin/components/admin/source.q +45 -0
  314. quantum_admin/components/admin/tests.q +73 -0
  315. quantum_admin/run.py +13 -0
  316. quantum_admin/services/__init__.py +16 -0
  317. quantum_admin/services/_base.py +84 -0
  318. quantum_admin/services/_component_test_generator.py +205 -0
  319. quantum_admin/services/_yaml_editor.py +82 -0
  320. quantum_admin/services/apps.py +330 -0
  321. quantum_admin/services/auth.py +61 -0
  322. quantum_admin/services/components.py +210 -0
  323. quantum_admin/services/connectors.py +164 -0
  324. quantum_admin/services/projects.py +189 -0
  325. quantum_admin/services/repository.py +223 -0
  326. quantum_admin/services/settings.py +150 -0
  327. quantum_admin/services/yaml_import.py +118 -0
  328. quantum_framework-0.18.0.dist-info/METADATA +281 -0
  329. quantum_framework-0.18.0.dist-info/RECORD +333 -0
  330. quantum_framework-0.18.0.dist-info/WHEEL +5 -0
  331. quantum_framework-0.18.0.dist-info/entry_points.txt +2 -0
  332. quantum_framework-0.18.0.dist-info/licenses/LICENSE +21 -0
  333. quantum_framework-0.18.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,710 @@
1
+ """
2
+ Template Service for Quantum Admin
3
+ Provides project scaffolding from templates
4
+ """
5
+
6
+ import os
7
+ import shutil
8
+ import logging
9
+ from pathlib import Path
10
+ from typing import Dict, Any, List, Optional
11
+ from dataclasses import dataclass, field
12
+ from datetime import datetime
13
+ import yaml
14
+ import json
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ @dataclass
20
+ class TemplateInfo:
21
+ """Information about a project template"""
22
+ id: str
23
+ name: str
24
+ description: str
25
+ icon: str = "📁" # folder icon
26
+ category: str = "general"
27
+ features: List[str] = field(default_factory=list)
28
+ options: List[Dict[str, Any]] = field(default_factory=list)
29
+ files: List[str] = field(default_factory=list)
30
+
31
+ def to_dict(self) -> Dict[str, Any]:
32
+ return {
33
+ "id": self.id,
34
+ "name": self.name,
35
+ "description": self.description,
36
+ "icon": self.icon,
37
+ "category": self.category,
38
+ "features": self.features,
39
+ "options": self.options,
40
+ "files": self.files
41
+ }
42
+
43
+
44
+ # Built-in templates
45
+ TEMPLATES = {
46
+ "blank": TemplateInfo(
47
+ id="blank",
48
+ name="Blank Project",
49
+ description="Empty project with minimal configuration",
50
+ icon="📄",
51
+ category="starter",
52
+ features=["Minimal setup", "Ready for customization"],
53
+ options=[]
54
+ ),
55
+ "blog": TemplateInfo(
56
+ id="blog",
57
+ name="Blog",
58
+ description="Full-featured blog with posts, comments, and admin",
59
+ icon="📝",
60
+ category="content",
61
+ features=["Posts management", "Comments", "Categories/Tags", "User authentication", "Admin panel"],
62
+ options=[
63
+ {"name": "include_auth", "label": "Include Authentication", "type": "boolean", "default": True},
64
+ {"name": "include_comments", "label": "Enable Comments", "type": "boolean", "default": True},
65
+ {"name": "sample_data", "label": "Include Sample Posts", "type": "boolean", "default": True}
66
+ ]
67
+ ),
68
+ "api": TemplateInfo(
69
+ id="api",
70
+ name="REST API",
71
+ description="RESTful API with CRUD operations and authentication",
72
+ icon="🔌",
73
+ category="backend",
74
+ features=["RESTful endpoints", "JWT authentication", "Database integration", "Swagger docs"],
75
+ options=[
76
+ {"name": "include_auth", "label": "Include Authentication", "type": "boolean", "default": True},
77
+ {"name": "include_swagger", "label": "Generate Swagger Docs", "type": "boolean", "default": True},
78
+ {"name": "database", "label": "Database Type", "type": "select", "default": "postgres",
79
+ "choices": ["postgres", "mysql", "sqlite"]}
80
+ ]
81
+ ),
82
+ "dashboard": TemplateInfo(
83
+ id="dashboard",
84
+ name="Dashboard",
85
+ description="Admin dashboard with charts, tables, and data visualization",
86
+ icon="📊",
87
+ category="admin",
88
+ features=["Charts & Graphs", "Data tables", "Real-time updates", "User management"],
89
+ options=[
90
+ {"name": "include_auth", "label": "Include Authentication", "type": "boolean", "default": True},
91
+ {"name": "include_charts", "label": "Include Chart Components", "type": "boolean", "default": True},
92
+ {"name": "sample_data", "label": "Include Sample Data", "type": "boolean", "default": True}
93
+ ]
94
+ ),
95
+ "landing": TemplateInfo(
96
+ id="landing",
97
+ name="Landing Page",
98
+ description="Marketing landing page with sections and contact form",
99
+ icon="🏠",
100
+ category="marketing",
101
+ features=["Hero section", "Features grid", "Testimonials", "Contact form", "Responsive design"],
102
+ options=[
103
+ {"name": "include_contact", "label": "Include Contact Form", "type": "boolean", "default": True},
104
+ {"name": "include_newsletter", "label": "Include Newsletter Signup", "type": "boolean", "default": False}
105
+ ]
106
+ )
107
+ }
108
+
109
+
110
+ class TemplateService:
111
+ """Service for managing and scaffolding project templates"""
112
+
113
+ def __init__(self, templates_dir: Path = None):
114
+ if templates_dir:
115
+ self.templates_dir = templates_dir
116
+ else:
117
+ # Default to quantum_admin/templates
118
+ self.templates_dir = Path(__file__).parent.parent / "templates"
119
+
120
+ def list_templates(self) -> List[TemplateInfo]:
121
+ """List all available templates"""
122
+ templates = list(TEMPLATES.values())
123
+
124
+ # Also check for custom templates in templates directory
125
+ if self.templates_dir.exists():
126
+ for template_dir in self.templates_dir.iterdir():
127
+ if template_dir.is_dir() and template_dir.name not in TEMPLATES:
128
+ manifest_path = template_dir / "template.yaml"
129
+ if manifest_path.exists():
130
+ try:
131
+ with open(manifest_path) as f:
132
+ manifest = yaml.safe_load(f)
133
+ templates.append(TemplateInfo(
134
+ id=template_dir.name,
135
+ name=manifest.get("name", template_dir.name),
136
+ description=manifest.get("description", ""),
137
+ icon=manifest.get("icon", "📁"),
138
+ category=manifest.get("category", "custom"),
139
+ features=manifest.get("features", []),
140
+ options=manifest.get("options", [])
141
+ ))
142
+ except Exception as e:
143
+ logger.warning(f"Failed to load template manifest {manifest_path}: {e}")
144
+
145
+ return templates
146
+
147
+ def get_template(self, template_id: str) -> Optional[TemplateInfo]:
148
+ """Get a specific template by ID"""
149
+ if template_id in TEMPLATES:
150
+ return TEMPLATES[template_id]
151
+
152
+ # Check custom templates
153
+ template_dir = self.templates_dir / template_id
154
+ if template_dir.exists():
155
+ manifest_path = template_dir / "template.yaml"
156
+ if manifest_path.exists():
157
+ with open(manifest_path) as f:
158
+ manifest = yaml.safe_load(f)
159
+ return TemplateInfo(
160
+ id=template_id,
161
+ name=manifest.get("name", template_id),
162
+ description=manifest.get("description", ""),
163
+ icon=manifest.get("icon", "📁"),
164
+ category=manifest.get("category", "custom"),
165
+ features=manifest.get("features", []),
166
+ options=manifest.get("options", [])
167
+ )
168
+
169
+ return None
170
+
171
+ def scaffold_project(
172
+ self,
173
+ template_id: str,
174
+ project_name: str,
175
+ target_dir: Path,
176
+ options: Dict[str, Any] = None
177
+ ) -> Dict[str, Any]:
178
+ """
179
+ Create a new project from a template.
180
+
181
+ Args:
182
+ template_id: ID of the template to use
183
+ project_name: Name of the new project
184
+ target_dir: Directory where the project will be created
185
+ options: Template-specific options
186
+
187
+ Returns:
188
+ Dict with created files and status
189
+ """
190
+ options = options or {}
191
+ template = self.get_template(template_id)
192
+
193
+ if not template:
194
+ raise ValueError(f"Template not found: {template_id}")
195
+
196
+ project_dir = target_dir / project_name
197
+ if project_dir.exists():
198
+ raise ValueError(f"Project directory already exists: {project_dir}")
199
+
200
+ project_dir.mkdir(parents=True)
201
+ created_files = []
202
+
203
+ try:
204
+ # Create base structure
205
+ (project_dir / "components").mkdir()
206
+ (project_dir / "migrations").mkdir(exist_ok=True)
207
+
208
+ # Create quantum.config.yaml
209
+ config = self._generate_config(template_id, project_name, options)
210
+ config_path = project_dir / "quantum.config.yaml"
211
+ with open(config_path, 'w') as f:
212
+ yaml.dump(config, f, default_flow_style=False)
213
+ created_files.append("quantum.config.yaml")
214
+
215
+ # Generate template-specific files
216
+ if template_id == "blank":
217
+ created_files.extend(self._scaffold_blank(project_dir, project_name, options))
218
+ elif template_id == "blog":
219
+ created_files.extend(self._scaffold_blog(project_dir, project_name, options))
220
+ elif template_id == "api":
221
+ created_files.extend(self._scaffold_api(project_dir, project_name, options))
222
+ elif template_id == "dashboard":
223
+ created_files.extend(self._scaffold_dashboard(project_dir, project_name, options))
224
+ elif template_id == "landing":
225
+ created_files.extend(self._scaffold_landing(project_dir, project_name, options))
226
+ else:
227
+ # Copy from custom template directory
228
+ template_src = self.templates_dir / template_id
229
+ if template_src.exists():
230
+ created_files.extend(self._copy_template_files(template_src, project_dir, project_name, options))
231
+
232
+ return {
233
+ "success": True,
234
+ "project_dir": str(project_dir),
235
+ "files_created": created_files,
236
+ "message": f"Project '{project_name}' created successfully"
237
+ }
238
+
239
+ except Exception as e:
240
+ # Cleanup on failure
241
+ if project_dir.exists():
242
+ shutil.rmtree(project_dir)
243
+ raise
244
+
245
+ def _generate_config(self, template_id: str, project_name: str, options: Dict[str, Any]) -> Dict:
246
+ """Generate quantum.config.yaml content"""
247
+ config = {
248
+ "name": project_name,
249
+ "version": "1.0.0",
250
+ "template": template_id,
251
+ "created_at": datetime.utcnow().isoformat(),
252
+ "server": {
253
+ "port": 8080,
254
+ "host": "0.0.0.0"
255
+ }
256
+ }
257
+
258
+ # Add database config if needed
259
+ if options.get("database") or template_id in ("blog", "api", "dashboard"):
260
+ db_type = options.get("database", "sqlite")
261
+ config["database"] = {
262
+ "type": db_type,
263
+ "name": project_name if db_type == "sqlite" else f"{project_name}_db"
264
+ }
265
+
266
+ # Add auth config if needed
267
+ if options.get("include_auth", True) and template_id != "landing":
268
+ config["auth"] = {
269
+ "enabled": True,
270
+ "type": "session",
271
+ "session_timeout": 3600
272
+ }
273
+
274
+ return config
275
+
276
+ def _scaffold_blank(self, project_dir: Path, name: str, options: Dict) -> List[str]:
277
+ """Scaffold a blank project"""
278
+ files = []
279
+
280
+ # Create index.q
281
+ index_content = f'''<q:component name="index">
282
+ <q:head>
283
+ <title>{name}</title>
284
+ </q:head>
285
+
286
+ <q:body>
287
+ <div class="container">
288
+ <h1>Welcome to {name}</h1>
289
+ <p>Your Quantum project is ready!</p>
290
+ </div>
291
+ </q:body>
292
+ </q:component>
293
+ '''
294
+ with open(project_dir / "components" / "index.q", 'w') as f:
295
+ f.write(index_content)
296
+ files.append("components/index.q")
297
+
298
+ return files
299
+
300
+ def _scaffold_blog(self, project_dir: Path, name: str, options: Dict) -> List[str]:
301
+ """Scaffold a blog project"""
302
+ files = []
303
+ include_auth = options.get("include_auth", True)
304
+ include_comments = options.get("include_comments", True)
305
+
306
+ # Index page
307
+ index_content = f'''<q:component name="index">
308
+ <q:head>
309
+ <title>{name} - Blog</title>
310
+ <link rel="stylesheet" href="/static/blog.css" />
311
+ </q:head>
312
+
313
+ <q:query name="posts" datasource="default">
314
+ SELECT id, title, slug, excerpt, created_at, author
315
+ FROM posts
316
+ WHERE published = true
317
+ ORDER BY created_at DESC
318
+ LIMIT 10
319
+ </q:query>
320
+
321
+ <q:body>
322
+ <header class="blog-header">
323
+ <h1>{name}</h1>
324
+ <nav>
325
+ <a href="/">Home</a>
326
+ <a href="/about">About</a>
327
+ {"<a href='/admin'>Admin</a>" if include_auth else ""}
328
+ </nav>
329
+ </header>
330
+
331
+ <main class="blog-posts">
332
+ <q:loop items="{{posts}}" item="post">
333
+ <article class="post-card">
334
+ <h2><a href="/post/{{post.slug}}">{{post.title}}</a></h2>
335
+ <p class="post-meta">By {{post.author}} on {{post.created_at}}</p>
336
+ <p>{{post.excerpt}}</p>
337
+ <a href="/post/{{post.slug}}" class="read-more">Read more</a>
338
+ </article>
339
+ </q:loop>
340
+ </main>
341
+ </q:body>
342
+ </q:component>
343
+ '''
344
+ with open(project_dir / "components" / "index.q", 'w') as f:
345
+ f.write(index_content)
346
+ files.append("components/index.q")
347
+
348
+ # Post detail page
349
+ post_content = f'''<q:component name="post">
350
+ <q:set name="slug" value="{{url.params.slug}}" />
351
+
352
+ <q:query name="post" datasource="default">
353
+ SELECT id, title, content, created_at, author
354
+ FROM posts
355
+ WHERE slug = :slug AND published = true
356
+ </q:query>
357
+
358
+ {"<q:query name='comments' datasource='default'>SELECT * FROM comments WHERE post_id = :post.id ORDER BY created_at</q:query>" if include_comments else ""}
359
+
360
+ <q:head>
361
+ <title>{{post.title}} - {name}</title>
362
+ </q:head>
363
+
364
+ <q:body>
365
+ <article class="post-full">
366
+ <h1>{{post.title}}</h1>
367
+ <p class="post-meta">By {{post.author}} on {{post.created_at}}</p>
368
+ <div class="post-content">
369
+ {{post.content}}
370
+ </div>
371
+ </article>
372
+
373
+ {"<section class='comments'><h3>Comments</h3><q:loop items='{comments}' item='comment'><div class='comment'><p>{comment.content}</p><small>By {comment.author}</small></div></q:loop></section>" if include_comments else ""}
374
+
375
+ <a href="/" class="back-link">Back to posts</a>
376
+ </q:body>
377
+ </q:component>
378
+ '''
379
+ with open(project_dir / "components" / "post.q", 'w') as f:
380
+ f.write(post_content)
381
+ files.append("components/post.q")
382
+
383
+ # Migration for posts table
384
+ migration_content = '''-- Migration: Create posts table
385
+ -- Version: V001
386
+
387
+ CREATE TABLE IF NOT EXISTS posts (
388
+ id SERIAL PRIMARY KEY,
389
+ title VARCHAR(255) NOT NULL,
390
+ slug VARCHAR(255) NOT NULL UNIQUE,
391
+ excerpt TEXT,
392
+ content TEXT,
393
+ author VARCHAR(100),
394
+ published BOOLEAN DEFAULT false,
395
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
396
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
397
+ );
398
+
399
+ CREATE INDEX idx_posts_slug ON posts(slug);
400
+ CREATE INDEX idx_posts_published ON posts(published);
401
+ '''
402
+ with open(project_dir / "migrations" / "V001_create_posts.sql", 'w') as f:
403
+ f.write(migration_content)
404
+ files.append("migrations/V001_create_posts.sql")
405
+
406
+ if include_comments:
407
+ comments_migration = '''-- Migration: Create comments table
408
+ -- Version: V002
409
+
410
+ CREATE TABLE IF NOT EXISTS comments (
411
+ id SERIAL PRIMARY KEY,
412
+ post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE,
413
+ author VARCHAR(100),
414
+ email VARCHAR(255),
415
+ content TEXT NOT NULL,
416
+ approved BOOLEAN DEFAULT false,
417
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
418
+ );
419
+
420
+ CREATE INDEX idx_comments_post ON comments(post_id);
421
+ '''
422
+ with open(project_dir / "migrations" / "V002_create_comments.sql", 'w') as f:
423
+ f.write(comments_migration)
424
+ files.append("migrations/V002_create_comments.sql")
425
+
426
+ return files
427
+
428
+ def _scaffold_api(self, project_dir: Path, name: str, options: Dict) -> List[str]:
429
+ """Scaffold a REST API project"""
430
+ files = []
431
+ include_auth = options.get("include_auth", True)
432
+
433
+ # API index with endpoints listing
434
+ index_content = f'''<q:component name="api-index">
435
+ <q:head>
436
+ <title>{name} API</title>
437
+ </q:head>
438
+
439
+ <q:body>
440
+ <h1>{name} API</h1>
441
+ <p>Welcome to the {name} REST API</p>
442
+
443
+ <h2>Available Endpoints</h2>
444
+ <ul>
445
+ <li>GET /api/items - List all items</li>
446
+ <li>GET /api/items/:id - Get item by ID</li>
447
+ <li>POST /api/items - Create new item</li>
448
+ <li>PUT /api/items/:id - Update item</li>
449
+ <li>DELETE /api/items/:id - Delete item</li>
450
+ {"<li>POST /api/auth/login - Authenticate user</li>" if include_auth else ""}
451
+ </ul>
452
+ </q:body>
453
+ </q:component>
454
+ '''
455
+ with open(project_dir / "components" / "index.q", 'w') as f:
456
+ f.write(index_content)
457
+ files.append("components/index.q")
458
+
459
+ # Items API component
460
+ items_content = '''<q:component name="items-api">
461
+ <!-- List items -->
462
+ <q:function name="list" method="GET" path="/api/items">
463
+ <q:query name="items" datasource="default">
464
+ SELECT * FROM items ORDER BY created_at DESC
465
+ </q:query>
466
+ <q:return format="json">{{items}}</q:return>
467
+ </q:function>
468
+
469
+ <!-- Get single item -->
470
+ <q:function name="get" method="GET" path="/api/items/:id">
471
+ <q:query name="item" datasource="default">
472
+ SELECT * FROM items WHERE id = :id
473
+ </q:query>
474
+ <q:if condition="{{!item}}">
475
+ <q:return format="json" status="404">{"error": "Item not found"}</q:return>
476
+ </q:if>
477
+ <q:return format="json">{{item}}</q:return>
478
+ </q:function>
479
+
480
+ <!-- Create item -->
481
+ <q:function name="create" method="POST" path="/api/items">
482
+ <q:query name="result" datasource="default">
483
+ INSERT INTO items (name, description)
484
+ VALUES (:body.name, :body.description)
485
+ RETURNING *
486
+ </q:query>
487
+ <q:return format="json" status="201">{{result}}</q:return>
488
+ </q:function>
489
+
490
+ <!-- Update item -->
491
+ <q:function name="update" method="PUT" path="/api/items/:id">
492
+ <q:query name="result" datasource="default">
493
+ UPDATE items
494
+ SET name = :body.name, description = :body.description, updated_at = NOW()
495
+ WHERE id = :id
496
+ RETURNING *
497
+ </q:query>
498
+ <q:return format="json">{{result}}</q:return>
499
+ </q:function>
500
+
501
+ <!-- Delete item -->
502
+ <q:function name="delete" method="DELETE" path="/api/items/:id">
503
+ <q:query datasource="default">
504
+ DELETE FROM items WHERE id = :id
505
+ </q:query>
506
+ <q:return format="json">{"message": "Item deleted"}</q:return>
507
+ </q:function>
508
+ </q:component>
509
+ '''
510
+ with open(project_dir / "components" / "items.q", 'w') as f:
511
+ f.write(items_content)
512
+ files.append("components/items.q")
513
+
514
+ # Items migration
515
+ migration_content = '''-- Migration: Create items table
516
+ -- Version: V001
517
+
518
+ CREATE TABLE IF NOT EXISTS items (
519
+ id SERIAL PRIMARY KEY,
520
+ name VARCHAR(255) NOT NULL,
521
+ description TEXT,
522
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
523
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
524
+ );
525
+ '''
526
+ with open(project_dir / "migrations" / "V001_create_items.sql", 'w') as f:
527
+ f.write(migration_content)
528
+ files.append("migrations/V001_create_items.sql")
529
+
530
+ return files
531
+
532
+ def _scaffold_dashboard(self, project_dir: Path, name: str, options: Dict) -> List[str]:
533
+ """Scaffold a dashboard project"""
534
+ files = []
535
+
536
+ # Dashboard index
537
+ index_content = f'''<q:component name="dashboard">
538
+ <q:head>
539
+ <title>{name} - Dashboard</title>
540
+ <link rel="stylesheet" href="/static/dashboard.css" />
541
+ </q:head>
542
+
543
+ <q:query name="stats" datasource="default">
544
+ SELECT
545
+ (SELECT COUNT(*) FROM users) as total_users,
546
+ (SELECT COUNT(*) FROM orders) as total_orders,
547
+ (SELECT SUM(amount) FROM orders) as total_revenue
548
+ </q:query>
549
+
550
+ <q:body>
551
+ <div class="dashboard-container">
552
+ <header class="dashboard-header">
553
+ <h1>{name}</h1>
554
+ <nav class="dashboard-nav">
555
+ <a href="/dashboard">Overview</a>
556
+ <a href="/dashboard/users">Users</a>
557
+ <a href="/dashboard/orders">Orders</a>
558
+ <a href="/dashboard/settings">Settings</a>
559
+ </nav>
560
+ </header>
561
+
562
+ <main class="dashboard-main">
563
+ <div class="stats-grid">
564
+ <div class="stat-card">
565
+ <h3>Total Users</h3>
566
+ <p class="stat-value">{{stats.total_users}}</p>
567
+ </div>
568
+ <div class="stat-card">
569
+ <h3>Total Orders</h3>
570
+ <p class="stat-value">{{stats.total_orders}}</p>
571
+ </div>
572
+ <div class="stat-card">
573
+ <h3>Revenue</h3>
574
+ <p class="stat-value">${{stats.total_revenue}}</p>
575
+ </div>
576
+ </div>
577
+
578
+ <section class="recent-activity">
579
+ <h2>Recent Activity</h2>
580
+ <q:query name="recent" datasource="default">
581
+ SELECT * FROM activity_log ORDER BY created_at DESC LIMIT 10
582
+ </q:query>
583
+ <table class="data-table">
584
+ <thead>
585
+ <tr><th>Action</th><th>User</th><th>Time</th></tr>
586
+ </thead>
587
+ <tbody>
588
+ <q:loop items="{{recent}}" item="log">
589
+ <tr>
590
+ <td>{{log.action}}</td>
591
+ <td>{{log.user_name}}</td>
592
+ <td>{{log.created_at}}</td>
593
+ </tr>
594
+ </q:loop>
595
+ </tbody>
596
+ </table>
597
+ </section>
598
+ </main>
599
+ </div>
600
+ </q:body>
601
+ </q:component>
602
+ '''
603
+ with open(project_dir / "components" / "index.q", 'w') as f:
604
+ f.write(index_content)
605
+ files.append("components/index.q")
606
+
607
+ return files
608
+
609
+ def _scaffold_landing(self, project_dir: Path, name: str, options: Dict) -> List[str]:
610
+ """Scaffold a landing page project"""
611
+ files = []
612
+ include_contact = options.get("include_contact", True)
613
+
614
+ # Landing page
615
+ landing_content = f'''<q:component name="landing">
616
+ <q:head>
617
+ <title>{name}</title>
618
+ <link rel="stylesheet" href="/static/landing.css" />
619
+ </q:head>
620
+
621
+ <q:body>
622
+ <!-- Hero Section -->
623
+ <section class="hero">
624
+ <div class="hero-content">
625
+ <h1>{name}</h1>
626
+ <p class="hero-subtitle">Your amazing product description goes here</p>
627
+ <a href="#features" class="cta-button">Get Started</a>
628
+ </div>
629
+ </section>
630
+
631
+ <!-- Features Section -->
632
+ <section id="features" class="features">
633
+ <h2>Features</h2>
634
+ <div class="features-grid">
635
+ <div class="feature-card">
636
+ <h3>Feature One</h3>
637
+ <p>Description of your first amazing feature</p>
638
+ </div>
639
+ <div class="feature-card">
640
+ <h3>Feature Two</h3>
641
+ <p>Description of your second amazing feature</p>
642
+ </div>
643
+ <div class="feature-card">
644
+ <h3>Feature Three</h3>
645
+ <p>Description of your third amazing feature</p>
646
+ </div>
647
+ </div>
648
+ </section>
649
+
650
+ <!-- Testimonials -->
651
+ <section class="testimonials">
652
+ <h2>What People Say</h2>
653
+ <div class="testimonials-grid">
654
+ <blockquote>
655
+ <p>"This product changed my life!"</p>
656
+ <cite>- Happy Customer</cite>
657
+ </blockquote>
658
+ </div>
659
+ </section>
660
+
661
+ {"<!-- Contact Form --><section id='contact' class='contact'><h2>Contact Us</h2><form class='contact-form' action='/api/contact' method='POST'><input type='text' name='name' placeholder='Your Name' required /><input type='email' name='email' placeholder='Your Email' required /><textarea name='message' placeholder='Your Message' required></textarea><button type='submit'>Send Message</button></form></section>" if include_contact else ""}
662
+
663
+ <!-- Footer -->
664
+ <footer class="footer">
665
+ <p>&copy; 2024 {name}. All rights reserved.</p>
666
+ </footer>
667
+ </q:body>
668
+ </q:component>
669
+ '''
670
+ with open(project_dir / "components" / "index.q", 'w') as f:
671
+ f.write(landing_content)
672
+ files.append("components/index.q")
673
+
674
+ return files
675
+
676
+ def _copy_template_files(
677
+ self,
678
+ template_src: Path,
679
+ project_dir: Path,
680
+ name: str,
681
+ options: Dict
682
+ ) -> List[str]:
683
+ """Copy files from a custom template directory"""
684
+ files = []
685
+
686
+ for src_file in template_src.rglob("*"):
687
+ if src_file.is_file() and src_file.name != "template.yaml":
688
+ rel_path = src_file.relative_to(template_src)
689
+ dest_file = project_dir / rel_path
690
+ dest_file.parent.mkdir(parents=True, exist_ok=True)
691
+
692
+ # Read and process content (replace placeholders)
693
+ try:
694
+ content = src_file.read_text()
695
+ content = content.replace("{{PROJECT_NAME}}", name)
696
+ for key, value in options.items():
697
+ content = content.replace(f"{{{{OPTIONS.{key}}}}}", str(value))
698
+ dest_file.write_text(content)
699
+ except Exception:
700
+ # Binary file, just copy
701
+ shutil.copy2(src_file, dest_file)
702
+
703
+ files.append(str(rel_path))
704
+
705
+ return files
706
+
707
+
708
+ def get_template_service() -> TemplateService:
709
+ """Get template service instance"""
710
+ return TemplateService()