createsonline 0.1.26__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.
- createsonline/__init__.py +46 -0
- createsonline/admin/__init__.py +7 -0
- createsonline/admin/content.py +526 -0
- createsonline/admin/crud.py +805 -0
- createsonline/admin/field_builder.py +559 -0
- createsonline/admin/integration.py +482 -0
- createsonline/admin/interface.py +2562 -0
- createsonline/admin/model_creator.py +513 -0
- createsonline/admin/model_manager.py +388 -0
- createsonline/admin/modern_dashboard.py +498 -0
- createsonline/admin/permissions.py +264 -0
- createsonline/admin/user_forms.py +594 -0
- createsonline/ai/__init__.py +202 -0
- createsonline/ai/fields.py +1226 -0
- createsonline/ai/orm.py +325 -0
- createsonline/ai/services.py +1244 -0
- createsonline/app.py +506 -0
- createsonline/auth/__init__.py +8 -0
- createsonline/auth/management.py +228 -0
- createsonline/auth/models.py +552 -0
- createsonline/cli/__init__.py +5 -0
- createsonline/cli/commands/__init__.py +122 -0
- createsonline/cli/commands/database.py +416 -0
- createsonline/cli/commands/info.py +173 -0
- createsonline/cli/commands/initdb.py +218 -0
- createsonline/cli/commands/project.py +545 -0
- createsonline/cli/commands/serve.py +173 -0
- createsonline/cli/commands/shell.py +93 -0
- createsonline/cli/commands/users.py +148 -0
- createsonline/cli/main.py +2041 -0
- createsonline/cli/manage.py +274 -0
- createsonline/config/__init__.py +9 -0
- createsonline/config/app.py +2577 -0
- createsonline/config/database.py +179 -0
- createsonline/config/docs.py +384 -0
- createsonline/config/errors.py +160 -0
- createsonline/config/orm.py +43 -0
- createsonline/config/request.py +93 -0
- createsonline/config/settings.py +176 -0
- createsonline/data/__init__.py +23 -0
- createsonline/data/dataframe.py +925 -0
- createsonline/data/io.py +453 -0
- createsonline/data/series.py +557 -0
- createsonline/database/__init__.py +60 -0
- createsonline/database/abstraction.py +440 -0
- createsonline/database/assistant.py +585 -0
- createsonline/database/fields.py +442 -0
- createsonline/database/migrations.py +132 -0
- createsonline/database/models.py +604 -0
- createsonline/database.py +438 -0
- createsonline/http/__init__.py +28 -0
- createsonline/http/client.py +535 -0
- createsonline/ml/__init__.py +55 -0
- createsonline/ml/classification.py +552 -0
- createsonline/ml/clustering.py +680 -0
- createsonline/ml/metrics.py +542 -0
- createsonline/ml/neural.py +560 -0
- createsonline/ml/preprocessing.py +784 -0
- createsonline/ml/regression.py +501 -0
- createsonline/performance/__init__.py +19 -0
- createsonline/performance/cache.py +444 -0
- createsonline/performance/compression.py +335 -0
- createsonline/performance/core.py +419 -0
- createsonline/project_init.py +789 -0
- createsonline/routing.py +528 -0
- createsonline/security/__init__.py +34 -0
- createsonline/security/core.py +811 -0
- createsonline/security/encryption.py +349 -0
- createsonline/server.py +295 -0
- createsonline/static/css/admin.css +263 -0
- createsonline/static/css/common.css +358 -0
- createsonline/static/css/dashboard.css +89 -0
- createsonline/static/favicon.ico +0 -0
- createsonline/static/icons/icon-128x128.png +0 -0
- createsonline/static/icons/icon-128x128.webp +0 -0
- createsonline/static/icons/icon-16x16.png +0 -0
- createsonline/static/icons/icon-16x16.webp +0 -0
- createsonline/static/icons/icon-180x180.png +0 -0
- createsonline/static/icons/icon-180x180.webp +0 -0
- createsonline/static/icons/icon-192x192.png +0 -0
- createsonline/static/icons/icon-192x192.webp +0 -0
- createsonline/static/icons/icon-256x256.png +0 -0
- createsonline/static/icons/icon-256x256.webp +0 -0
- createsonline/static/icons/icon-32x32.png +0 -0
- createsonline/static/icons/icon-32x32.webp +0 -0
- createsonline/static/icons/icon-384x384.png +0 -0
- createsonline/static/icons/icon-384x384.webp +0 -0
- createsonline/static/icons/icon-48x48.png +0 -0
- createsonline/static/icons/icon-48x48.webp +0 -0
- createsonline/static/icons/icon-512x512.png +0 -0
- createsonline/static/icons/icon-512x512.webp +0 -0
- createsonline/static/icons/icon-64x64.png +0 -0
- createsonline/static/icons/icon-64x64.webp +0 -0
- createsonline/static/image/android-chrome-192x192.png +0 -0
- createsonline/static/image/android-chrome-512x512.png +0 -0
- createsonline/static/image/apple-touch-icon.png +0 -0
- createsonline/static/image/favicon-16x16.png +0 -0
- createsonline/static/image/favicon-32x32.png +0 -0
- createsonline/static/image/favicon.ico +0 -0
- createsonline/static/image/favicon.svg +17 -0
- createsonline/static/image/icon-128x128.png +0 -0
- createsonline/static/image/icon-128x128.webp +0 -0
- createsonline/static/image/icon-16x16.png +0 -0
- createsonline/static/image/icon-16x16.webp +0 -0
- createsonline/static/image/icon-180x180.png +0 -0
- createsonline/static/image/icon-180x180.webp +0 -0
- createsonline/static/image/icon-192x192.png +0 -0
- createsonline/static/image/icon-192x192.webp +0 -0
- createsonline/static/image/icon-256x256.png +0 -0
- createsonline/static/image/icon-256x256.webp +0 -0
- createsonline/static/image/icon-32x32.png +0 -0
- createsonline/static/image/icon-32x32.webp +0 -0
- createsonline/static/image/icon-384x384.png +0 -0
- createsonline/static/image/icon-384x384.webp +0 -0
- createsonline/static/image/icon-48x48.png +0 -0
- createsonline/static/image/icon-48x48.webp +0 -0
- createsonline/static/image/icon-512x512.png +0 -0
- createsonline/static/image/icon-512x512.webp +0 -0
- createsonline/static/image/icon-64x64.png +0 -0
- createsonline/static/image/icon-64x64.webp +0 -0
- createsonline/static/image/logo-header-h100.png +0 -0
- createsonline/static/image/logo-header-h100.webp +0 -0
- createsonline/static/image/logo-header-h200@2x.png +0 -0
- createsonline/static/image/logo-header-h200@2x.webp +0 -0
- createsonline/static/image/logo.png +0 -0
- createsonline/static/js/admin.js +274 -0
- createsonline/static/site.webmanifest +35 -0
- createsonline/static/templates/admin/base.html +87 -0
- createsonline/static/templates/admin/dashboard.html +217 -0
- createsonline/static/templates/admin/model_form.html +270 -0
- createsonline/static/templates/admin/model_list.html +202 -0
- createsonline/static/test_script.js +15 -0
- createsonline/static/test_styles.css +59 -0
- createsonline/static_files.py +365 -0
- createsonline/templates/404.html +100 -0
- createsonline/templates/admin_login.html +169 -0
- createsonline/templates/base.html +102 -0
- createsonline/templates/index.html +151 -0
- createsonline/templates.py +205 -0
- createsonline/testing.py +322 -0
- createsonline/utils.py +448 -0
- createsonline/validation/__init__.py +49 -0
- createsonline/validation/fields.py +598 -0
- createsonline/validation/models.py +504 -0
- createsonline/validation/validators.py +561 -0
- createsonline/views.py +184 -0
- createsonline-0.1.26.dist-info/METADATA +46 -0
- createsonline-0.1.26.dist-info/RECORD +152 -0
- createsonline-0.1.26.dist-info/WHEEL +5 -0
- createsonline-0.1.26.dist-info/entry_points.txt +2 -0
- createsonline-0.1.26.dist-info/licenses/LICENSE +21 -0
- createsonline-0.1.26.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{% block title %}CREATESONLINE Framework{% endblock %}</title>
|
|
7
|
+
|
|
8
|
+
<!-- Favicons -->
|
|
9
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
10
|
+
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
|
11
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32x32.png">
|
|
12
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/icons/icon-16x16.png">
|
|
13
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-180x180.png">
|
|
14
|
+
<link rel="manifest" href="/site.webmanifest">
|
|
15
|
+
|
|
16
|
+
{% block extra_head %}{% endblock %}
|
|
17
|
+
|
|
18
|
+
<style>
|
|
19
|
+
* {
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
body {
|
|
26
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
27
|
+
background: linear-gradient(135deg, #000000 0%, #1a1a1a 50%, #000000 100%);
|
|
28
|
+
min-height: 100vh;
|
|
29
|
+
display: flex;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
align-items: center;
|
|
32
|
+
padding: 20px;
|
|
33
|
+
position: relative;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
body::before {
|
|
38
|
+
content: '';
|
|
39
|
+
position: absolute;
|
|
40
|
+
top: -50%;
|
|
41
|
+
left: -50%;
|
|
42
|
+
width: 200%;
|
|
43
|
+
height: 200%;
|
|
44
|
+
background: repeating-linear-gradient(
|
|
45
|
+
0deg,
|
|
46
|
+
transparent,
|
|
47
|
+
transparent 2px,
|
|
48
|
+
rgba(255, 255, 255, 0.03) 2px,
|
|
49
|
+
rgba(255, 255, 255, 0.03) 4px
|
|
50
|
+
);
|
|
51
|
+
animation: scan 8s linear infinite;
|
|
52
|
+
pointer-events: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@keyframes scan {
|
|
56
|
+
0% { transform: translateY(0); }
|
|
57
|
+
100% { transform: translateY(50px); }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.container {
|
|
61
|
+
background: rgba(255, 255, 255, 0.98);
|
|
62
|
+
backdrop-filter: blur(10px);
|
|
63
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
64
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
|
65
|
+
padding: 80px 60px;
|
|
66
|
+
max-width: 1000px;
|
|
67
|
+
width: 100%;
|
|
68
|
+
animation: slideUp 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
|
69
|
+
position: relative;
|
|
70
|
+
z-index: 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@keyframes slideUp {
|
|
74
|
+
from {
|
|
75
|
+
opacity: 0;
|
|
76
|
+
transform: translateY(30px);
|
|
77
|
+
}
|
|
78
|
+
to {
|
|
79
|
+
opacity: 1;
|
|
80
|
+
transform: translateY(0);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.logo {
|
|
85
|
+
display: block;
|
|
86
|
+
margin: 0 auto 50px;
|
|
87
|
+
max-width: 280px;
|
|
88
|
+
width: 100%;
|
|
89
|
+
height: auto;
|
|
90
|
+
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
{% block extra_style %}{% endblock %}
|
|
94
|
+
</style>
|
|
95
|
+
</head>
|
|
96
|
+
<body>
|
|
97
|
+
<div class="container">
|
|
98
|
+
<img src="/logo-header-h200@2x.png" alt="CREATESONLINE" class="logo">
|
|
99
|
+
{% block content %}{% endblock %}
|
|
100
|
+
</div>
|
|
101
|
+
</body>
|
|
102
|
+
</html>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}Welcome - CREATESONLINE{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block extra_style %}
|
|
6
|
+
h1 {
|
|
7
|
+
font-size: 2.8em;
|
|
8
|
+
color: #000000;
|
|
9
|
+
margin-bottom: 15px;
|
|
10
|
+
text-align: center;
|
|
11
|
+
font-weight: 300;
|
|
12
|
+
letter-spacing: -1px;
|
|
13
|
+
position: relative;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
h1::after {
|
|
17
|
+
content: '';
|
|
18
|
+
display: block;
|
|
19
|
+
width: 60px;
|
|
20
|
+
height: 3px;
|
|
21
|
+
background: #000000;
|
|
22
|
+
margin: 20px auto 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.subtitle {
|
|
26
|
+
text-align: center;
|
|
27
|
+
color: #666666;
|
|
28
|
+
font-size: 1.1em;
|
|
29
|
+
margin-bottom: 50px;
|
|
30
|
+
font-weight: 300;
|
|
31
|
+
line-height: 1.6;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.links {
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: repeat(2, 1fr);
|
|
37
|
+
gap: 25px;
|
|
38
|
+
margin: 50px 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.link-card {
|
|
42
|
+
background: #000000;
|
|
43
|
+
padding: 40px 30px;
|
|
44
|
+
text-decoration: none;
|
|
45
|
+
color: white;
|
|
46
|
+
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
|
47
|
+
border: 2px solid #000000;
|
|
48
|
+
position: relative;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.link-card::before {
|
|
53
|
+
content: '';
|
|
54
|
+
position: absolute;
|
|
55
|
+
top: 0;
|
|
56
|
+
left: -100%;
|
|
57
|
+
width: 100%;
|
|
58
|
+
height: 100%;
|
|
59
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
|
|
60
|
+
transition: left 0.5s;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.link-card:hover::before {
|
|
64
|
+
left: 100%;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.link-card:hover {
|
|
68
|
+
background: #ffffff;
|
|
69
|
+
color: #000000;
|
|
70
|
+
border: 2px solid #000000;
|
|
71
|
+
transform: translateY(-8px);
|
|
72
|
+
box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.3);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.link-card h3 {
|
|
76
|
+
margin-bottom: 12px;
|
|
77
|
+
font-size: 1.3em;
|
|
78
|
+
font-weight: 600;
|
|
79
|
+
letter-spacing: -0.5px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.link-card p {
|
|
83
|
+
opacity: 0.85;
|
|
84
|
+
font-size: 0.95em;
|
|
85
|
+
font-weight: 300;
|
|
86
|
+
line-height: 1.6;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.link-card:hover p {
|
|
90
|
+
opacity: 0.7;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.version {
|
|
94
|
+
text-align: center;
|
|
95
|
+
color: #999999;
|
|
96
|
+
margin-top: 60px;
|
|
97
|
+
font-size: 0.85em;
|
|
98
|
+
font-weight: 300;
|
|
99
|
+
padding-top: 40px;
|
|
100
|
+
border-top: 1px solid #e0e0e0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.version p {
|
|
104
|
+
margin: 5px 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@media (max-width: 768px) {
|
|
108
|
+
.links {
|
|
109
|
+
grid-template-columns: 1fr;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.container {
|
|
113
|
+
padding: 50px 30px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
h1 {
|
|
117
|
+
font-size: 2em;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
{% endblock %}
|
|
121
|
+
|
|
122
|
+
{% block content %}
|
|
123
|
+
<h1>AI-Native Web Framework</h1>
|
|
124
|
+
|
|
125
|
+
<div class="links">
|
|
126
|
+
<a href="https://createsonline.com/docs" class="link-card">
|
|
127
|
+
<h3>Documentation</h3>
|
|
128
|
+
<p>Complete guides and API reference</p>
|
|
129
|
+
</a>
|
|
130
|
+
|
|
131
|
+
<a href="https://createsonline.com/guide" class="link-card">
|
|
132
|
+
<h3>Quick Start</h3>
|
|
133
|
+
<p>Get started in 5 minutes</p>
|
|
134
|
+
</a>
|
|
135
|
+
|
|
136
|
+
<a href="https://createsonline.com/examples" class="link-card">
|
|
137
|
+
<h3>Examples</h3>
|
|
138
|
+
<p>Real-world code examples</p>
|
|
139
|
+
</a>
|
|
140
|
+
|
|
141
|
+
<a href="https://github.com/meahmedh/createsonline" class="link-card">
|
|
142
|
+
<h3>GitHub</h3>
|
|
143
|
+
<p>View source and contribute</p>
|
|
144
|
+
</a>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<div class="version">
|
|
148
|
+
<p>CREATESONLINE v0.1.23</p>
|
|
149
|
+
<p>Built by the CREATESONLINE Team</p>
|
|
150
|
+
</div>
|
|
151
|
+
{% endblock %}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# createsonline/templates.py
|
|
2
|
+
"""
|
|
3
|
+
Simple Template Rendering System
|
|
4
|
+
|
|
5
|
+
Lightweight template engine with Django-style syntax support.
|
|
6
|
+
Zero external dependencies - uses only Python stdlib.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, Any, Optional
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger("createsonline")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TemplateEngine:
|
|
18
|
+
"""
|
|
19
|
+
Simple template engine supporting:
|
|
20
|
+
- {% extends "base.html" %}
|
|
21
|
+
- {% block name %}...{% endblock %}
|
|
22
|
+
- {{ variable }}
|
|
23
|
+
- {% for item in items %}...{% endfor %}
|
|
24
|
+
- {% if condition %}...{% endif %}
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, template_dirs: list = None):
|
|
28
|
+
"""
|
|
29
|
+
Initialize template engine
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
template_dirs: List of directories to search for templates
|
|
33
|
+
"""
|
|
34
|
+
if template_dirs is None:
|
|
35
|
+
template_dirs = [Path.cwd() / "templates"]
|
|
36
|
+
|
|
37
|
+
self.template_dirs = [Path(d) for d in template_dirs]
|
|
38
|
+
self._template_cache = {}
|
|
39
|
+
|
|
40
|
+
def find_template(self, name: str) -> Optional[Path]:
|
|
41
|
+
"""Find template file in template directories"""
|
|
42
|
+
for template_dir in self.template_dirs:
|
|
43
|
+
template_path = template_dir / name
|
|
44
|
+
if template_path.exists():
|
|
45
|
+
return template_path
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
def load_template(self, name: str) -> str:
|
|
49
|
+
"""Load template content from file"""
|
|
50
|
+
if name in self._template_cache:
|
|
51
|
+
return self._template_cache[name]
|
|
52
|
+
|
|
53
|
+
template_path = self.find_template(name)
|
|
54
|
+
if not template_path:
|
|
55
|
+
raise FileNotFoundError(f"Template not found: {name}")
|
|
56
|
+
|
|
57
|
+
content = template_path.read_text(encoding='utf-8')
|
|
58
|
+
self._template_cache[name] = content
|
|
59
|
+
return content
|
|
60
|
+
|
|
61
|
+
def render(self, template_name: str, context: Dict[str, Any] = None) -> str:
|
|
62
|
+
"""
|
|
63
|
+
Render a template with context
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
template_name: Name of the template file
|
|
67
|
+
context: Dictionary of variables to pass to template
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Rendered HTML string
|
|
71
|
+
"""
|
|
72
|
+
if context is None:
|
|
73
|
+
context = {}
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
content = self.load_template(template_name)
|
|
77
|
+
|
|
78
|
+
# Handle {% extends "..." %}
|
|
79
|
+
extends_match = re.search(r'{%\s*extends\s+["\'](.+?)["\']\s*%}', content)
|
|
80
|
+
if extends_match:
|
|
81
|
+
parent_name = extends_match.group(1)
|
|
82
|
+
content = self._handle_extends(content, parent_name, context)
|
|
83
|
+
|
|
84
|
+
# Render the final content
|
|
85
|
+
return self._render_content(content, context)
|
|
86
|
+
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.error(f"Template rendering error: {e}")
|
|
89
|
+
return f"<html><body><h1>Template Error</h1><p>{str(e)}</p></body></html>"
|
|
90
|
+
|
|
91
|
+
def _handle_extends(self, child_content: str, parent_name: str, context: Dict[str, Any]) -> str:
|
|
92
|
+
"""Handle template inheritance with {% extends %}"""
|
|
93
|
+
# Load parent template
|
|
94
|
+
parent_content = self.load_template(parent_name)
|
|
95
|
+
|
|
96
|
+
# Extract blocks from child template
|
|
97
|
+
child_blocks = self._extract_blocks(child_content)
|
|
98
|
+
|
|
99
|
+
# Replace blocks in parent template
|
|
100
|
+
result = parent_content
|
|
101
|
+
for block_name, block_content in child_blocks.items():
|
|
102
|
+
# Find block in parent and replace
|
|
103
|
+
pattern = r'{%\s*block\s+' + re.escape(block_name) + r'\s*%}.*?{%\s*endblock\s*%}'
|
|
104
|
+
replacement = f'{{% block {block_name} %}}{block_content}{{% endblock %}}'
|
|
105
|
+
result = re.sub(pattern, replacement, result, flags=re.DOTALL)
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
def _extract_blocks(self, content: str) -> Dict[str, str]:
|
|
110
|
+
"""Extract all {% block name %}...{% endblock %} from template"""
|
|
111
|
+
blocks = {}
|
|
112
|
+
pattern = r'{%\s*block\s+(\w+)\s*%}(.*?){%\s*endblock\s*%}'
|
|
113
|
+
|
|
114
|
+
for match in re.finditer(pattern, content, re.DOTALL):
|
|
115
|
+
block_name = match.group(1)
|
|
116
|
+
block_content = match.group(2)
|
|
117
|
+
blocks[block_name] = block_content
|
|
118
|
+
|
|
119
|
+
return blocks
|
|
120
|
+
|
|
121
|
+
def _render_content(self, content: str, context: Dict[str, Any]) -> str:
|
|
122
|
+
"""Render template content with context"""
|
|
123
|
+
# Remove {% extends %} tag if still present
|
|
124
|
+
content = re.sub(r'{%\s*extends\s+["\'](.+?)["\']\s*%}', '', content)
|
|
125
|
+
|
|
126
|
+
# Render {% block %} tags (remove block markers, keep content)
|
|
127
|
+
content = re.sub(r'{%\s*block\s+\w+\s*%}', '', content)
|
|
128
|
+
content = re.sub(r'{%\s*endblock\s*%}', '', content)
|
|
129
|
+
|
|
130
|
+
# Render {{ variable }} tags
|
|
131
|
+
def replace_var(match):
|
|
132
|
+
var_name = match.group(1).strip()
|
|
133
|
+
return str(context.get(var_name, ''))
|
|
134
|
+
|
|
135
|
+
content = re.sub(r'{{\s*(.+?)\s*}}', replace_var, content)
|
|
136
|
+
|
|
137
|
+
return content
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# Global template engine instance
|
|
141
|
+
_template_engine = None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_template_engine() -> TemplateEngine:
|
|
145
|
+
"""Get or create the global template engine instance"""
|
|
146
|
+
global _template_engine
|
|
147
|
+
|
|
148
|
+
if _template_engine is None:
|
|
149
|
+
# Auto-discover template directories
|
|
150
|
+
template_dirs = []
|
|
151
|
+
|
|
152
|
+
# Check current working directory
|
|
153
|
+
cwd = Path.cwd()
|
|
154
|
+
if (cwd / "templates").exists():
|
|
155
|
+
template_dirs.append(cwd / "templates")
|
|
156
|
+
|
|
157
|
+
# Check createsonline package templates
|
|
158
|
+
pkg_templates = Path(__file__).parent / "templates"
|
|
159
|
+
if pkg_templates.exists():
|
|
160
|
+
template_dirs.append(pkg_templates)
|
|
161
|
+
|
|
162
|
+
_template_engine = TemplateEngine(template_dirs)
|
|
163
|
+
|
|
164
|
+
return _template_engine
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def render_template(template_name: str, context: Dict[str, Any] = None) -> str:
|
|
168
|
+
"""
|
|
169
|
+
Convenience function to render a template
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
template_name: Name of the template file (e.g., "index.html")
|
|
173
|
+
context: Dictionary of variables to pass to template
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Rendered HTML string
|
|
177
|
+
|
|
178
|
+
Example:
|
|
179
|
+
html = render_template("index.html", {"title": "Welcome"})
|
|
180
|
+
"""
|
|
181
|
+
engine = get_template_engine()
|
|
182
|
+
return engine.render(template_name, context or {})
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def render_to_response(template_name: str, context: Dict[str, Any] = None):
|
|
186
|
+
"""
|
|
187
|
+
Render template and return response tuple
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
template_name: Name of the template file
|
|
191
|
+
context: Dictionary of variables to pass to template
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Tuple of (html_string, status_code, headers)
|
|
195
|
+
"""
|
|
196
|
+
html = render_template(template_name, context)
|
|
197
|
+
return html, 200, {'Content-Type': 'text/html; charset=utf-8'}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
__all__ = [
|
|
201
|
+
'TemplateEngine',
|
|
202
|
+
'render_template',
|
|
203
|
+
'render_to_response',
|
|
204
|
+
'get_template_engine',
|
|
205
|
+
]
|