overflask 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. overflask-0.1.0/LICENSE +21 -0
  2. overflask-0.1.0/PKG-INFO +220 -0
  3. overflask-0.1.0/README.md +159 -0
  4. overflask-0.1.0/pyproject.toml +107 -0
  5. overflask-0.1.0/setup.cfg +4 -0
  6. overflask-0.1.0/src/overflask/__init__.py +23 -0
  7. overflask-0.1.0/src/overflask/analytics/__init__.py +1 -0
  8. overflask-0.1.0/src/overflask/analytics/analytics.py +72 -0
  9. overflask-0.1.0/src/overflask/analytics/commands.py +30 -0
  10. overflask-0.1.0/src/overflask/analytics/es_index_templates.py +51 -0
  11. overflask-0.1.0/src/overflask/analytics/events.py +43 -0
  12. overflask-0.1.0/src/overflask/analytics/flusher.py +75 -0
  13. overflask-0.1.0/src/overflask/analytics/purge.py +49 -0
  14. overflask-0.1.0/src/overflask/cli/__init__.py +0 -0
  15. overflask-0.1.0/src/overflask/cli/cli.py +269 -0
  16. overflask-0.1.0/src/overflask/commands/__init__.py +0 -0
  17. overflask-0.1.0/src/overflask/commands/commands.py +194 -0
  18. overflask-0.1.0/src/overflask/core/__init__.py +0 -0
  19. overflask-0.1.0/src/overflask/core/app.py +28 -0
  20. overflask-0.1.0/src/overflask/core/config.py +93 -0
  21. overflask-0.1.0/src/overflask/core/discovery.py +74 -0
  22. overflask-0.1.0/src/overflask/db/__init__.py +0 -0
  23. overflask-0.1.0/src/overflask/db/model_base.py +124 -0
  24. overflask-0.1.0/src/overflask/deploy/__init__.py +0 -0
  25. overflask-0.1.0/src/overflask/deploy/gunicorn.py +33 -0
  26. overflask-0.1.0/src/overflask/es/__init__.py +0 -0
  27. overflask-0.1.0/src/overflask/es/elastic.py +21 -0
  28. overflask-0.1.0/src/overflask/redis/__init__.py +0 -0
  29. overflask-0.1.0/src/overflask/redis/cache.py +173 -0
  30. overflask-0.1.0/src/overflask/redis/redis.py +22 -0
  31. overflask-0.1.0/src/overflask/tasks/__init__.py +0 -0
  32. overflask-0.1.0/src/overflask/tasks/callable_data.py +14 -0
  33. overflask-0.1.0/src/overflask/tasks/callable_registry.py +9 -0
  34. overflask-0.1.0/src/overflask/tasks/callable_wrapper.py +67 -0
  35. overflask-0.1.0/src/overflask/tasks/commands.py +37 -0
  36. overflask-0.1.0/src/overflask/tasks/es_index_templates.py +96 -0
  37. overflask-0.1.0/src/overflask/tasks/kibana.py +286 -0
  38. overflask-0.1.0/src/overflask/tasks/metrics.py +71 -0
  39. overflask-0.1.0/src/overflask/tasks/scheduler.py +101 -0
  40. overflask-0.1.0/src/overflask/tasks/task_store.py +214 -0
  41. overflask-0.1.0/src/overflask/tasks/tasks.py +46 -0
  42. overflask-0.1.0/src/overflask/tasks/validator.py +109 -0
  43. overflask-0.1.0/src/overflask/tasks/worker.py +61 -0
  44. overflask-0.1.0/src/overflask/templates/README.md.jinja +65 -0
  45. overflask-0.1.0/src/overflask/templates/component/__init__.py.jinja +1 -0
  46. overflask-0.1.0/src/overflask/templates/component/cli.py.jinja +14 -0
  47. overflask-0.1.0/src/overflask/templates/component/models.py.jinja +15 -0
  48. overflask-0.1.0/src/overflask/templates/component/services.py.jinja +4 -0
  49. overflask-0.1.0/src/overflask/templates/component/tasks.py.jinja +15 -0
  50. overflask-0.1.0/src/overflask/templates/component/tests.py.jinja +19 -0
  51. overflask-0.1.0/src/overflask/templates/component/views.py.jinja +16 -0
  52. overflask-0.1.0/src/overflask/templates/compose.yaml.jinja +94 -0
  53. overflask-0.1.0/src/overflask/templates/conftest.py.jinja +262 -0
  54. overflask-0.1.0/src/overflask/templates/dockerignore +29 -0
  55. overflask-0.1.0/src/overflask/templates/docs/README-overflask.md +582 -0
  56. overflask-0.1.0/src/overflask/templates/docs/gunicorn.md +87 -0
  57. overflask-0.1.0/src/overflask/templates/docs/traefik.md +81 -0
  58. overflask-0.1.0/src/overflask/templates/gitignore +33 -0
  59. overflask-0.1.0/src/overflask/templates/manage.py.jinja +85 -0
  60. overflask-0.1.0/src/overflask/templates/ops/env.template +47 -0
  61. overflask-0.1.0/src/overflask/templates/ops/migrations/alembic.ini.jinja +40 -0
  62. overflask-0.1.0/src/overflask/templates/ops/migrations/env.py.jinja +68 -0
  63. overflask-0.1.0/src/overflask/templates/ops/migrations/script.py.mako +24 -0
  64. overflask-0.1.0/src/overflask/templates/ops/setup-traefik.sh +338 -0
  65. overflask-0.1.0/src/overflask/templates/pytest.ini.jinja +9 -0
  66. overflask-0.1.0/src/overflask/templates/requirements.pip.jinja +15 -0
  67. overflask-0.1.0/src/overflask/templates/settings.py.jinja +92 -0
  68. overflask-0.1.0/src/overflask/testing/__init__.py +0 -0
  69. overflask-0.1.0/src/overflask/testing/testing.py +115 -0
  70. overflask-0.1.0/src/overflask.egg-info/PKG-INFO +220 -0
  71. overflask-0.1.0/src/overflask.egg-info/SOURCES.txt +77 -0
  72. overflask-0.1.0/src/overflask.egg-info/dependency_links.txt +1 -0
  73. overflask-0.1.0/src/overflask.egg-info/entry_points.txt +2 -0
  74. overflask-0.1.0/src/overflask.egg-info/requires.txt +14 -0
  75. overflask-0.1.0/src/overflask.egg-info/top_level.txt +1 -0
  76. overflask-0.1.0/tests/test_cli.py +1048 -0
  77. overflask-0.1.0/tests/test_model_base.py +210 -0
  78. overflask-0.1.0/tests/test_tasks.py +817 -0
  79. overflask-0.1.0/tests/test_testing.py +223 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rklaus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.4
2
+ Name: overflask
3
+ Version: 0.1.0
4
+ Summary: Flask scaffolding CLI and runtime library — component architecture, SQLAlchemy, Redis caching, async tasks, and Elasticsearch analytics
5
+ Author: rklaus
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 rklaus
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://gitlab.com/overflask/overflask
28
+ Project-URL: Source, https://gitlab.com/overflask/overflask
29
+ Project-URL: Tracker, https://gitlab.com/overflask/overflask/-/issues
30
+ Keywords: flask,scaffolding,cli,sqlalchemy,redis,elasticsearch,tasks,analytics
31
+ Classifier: Development Status :: 4 - Beta
32
+ Classifier: Environment :: Web Environment
33
+ Classifier: Framework :: Flask
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.10
39
+ Classifier: Programming Language :: Python :: 3.11
40
+ Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
42
+ Classifier: Topic :: Software Development :: Code Generators
43
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
44
+ Requires-Python: >=3.10
45
+ Description-Content-Type: text/markdown
46
+ License-File: LICENSE
47
+ Requires-Dist: click>=8.0
48
+ Requires-Dist: flask>=3.0
49
+ Requires-Dist: python-dotenv>=1.0
50
+ Requires-Dist: sqlalchemy>=2.0
51
+ Requires-Dist: redis>=5.0
52
+ Requires-Dist: elasticsearch>=8.0
53
+ Requires-Dist: croniter>=3.0
54
+ Provides-Extra: dev
55
+ Requires-Dist: pytest>=7.0; extra == "dev"
56
+ Requires-Dist: pytest-tmp-files>=0.0.2; extra == "dev"
57
+ Requires-Dist: ruff>=0.8; extra == "dev"
58
+ Requires-Dist: build>=1.0; extra == "dev"
59
+ Requires-Dist: twine>=5.0; extra == "dev"
60
+ Dynamic: license-file
61
+
62
+ # overflask
63
+
64
+ A CLI tool that scaffolds Flask projects with component-based architecture, and a runtime library that adds SQLAlchemy models, Redis caching, async/recurring tasks, and Elasticsearch analytics on top of Flask.
65
+
66
+ ## Install
67
+
68
+ ```bash
69
+ pip install overflask @ git+https://gitlab.com/hamdarsi/overflask.git
70
+ ```
71
+
72
+ ## Create a project
73
+
74
+ ```bash
75
+ overflask create myapp
76
+ ```
77
+
78
+ The CLI prompts for an initial component name and Postgres/Redis connection details, generates the project, pre-fills `.env`, and optionally starts the database containers.
79
+
80
+ ```
81
+ myapp/
82
+ ├── manage.py # project CLI
83
+ ├── settings.py # configuration
84
+ ├── requirements.pip # dependencies
85
+ ├── compose.yaml # Docker Compose
86
+ ├── conftest.py # pytest fixtures
87
+ ├── ops/
88
+ │ ├── env.template
89
+ │ ├── migrations/ # Alembic
90
+ │ └── setup-traefik.sh
91
+ ├── docs/
92
+ └── components/
93
+ └── myapp/
94
+ ├── models.py
95
+ ├── views.py
96
+ ├── services.py
97
+ ├── cli.py
98
+ ├── tasks.py
99
+ └── tests.py
100
+ ```
101
+
102
+ ## Add a component
103
+
104
+ ```bash
105
+ python manage.py component add auth
106
+ ```
107
+
108
+ Creates `components/auth/` with the same structure and registers it in `settings.COMPONENTS`.
109
+
110
+ ## Runtime library
111
+
112
+ Generated projects import directly from `overflask`:
113
+
114
+ ### Models
115
+
116
+ ```python
117
+ from overflask import ModelBase
118
+ from sqlalchemy.orm import Mapped, mapped_column
119
+
120
+ class User(ModelBase):
121
+ # __tablename__ auto-set to "auth_users"
122
+ id: Mapped[int] = mapped_column(primary_key=True)
123
+ email: Mapped[str] = mapped_column(String(255), unique=True)
124
+ ```
125
+
126
+ Table names are derived automatically as `{component}_{plural_snake_case_model_name}`.
127
+
128
+ ### Redis cache
129
+
130
+ ```python
131
+ from overflask import cached
132
+
133
+ @cached(ttl=300)
134
+ def get_user(user_id: int):
135
+ ...
136
+ ```
137
+
138
+ Backed by Redis db 15 with stampede prevention via locking.
139
+
140
+ ### Async tasks
141
+
142
+ ```python
143
+ from overflask import async_task
144
+
145
+ @async_task
146
+ def send_welcome_email(user_id: int, email: str) -> None:
147
+ ...
148
+
149
+ # Enqueue immediately or with a delay
150
+ send_welcome_email.queue(user_id=42, email="alice@example.com")
151
+ send_welcome_email.queue(timedelta(minutes=5), user_id=42, email="alice@example.com")
152
+ ```
153
+
154
+ ### Recurring tasks
155
+
156
+ ```python
157
+ from overflask import recurring_task
158
+
159
+ @recurring_task("0 9 * * MON-FRI")
160
+ def daily_report() -> None:
161
+ ...
162
+ ```
163
+
164
+ Tasks are stored in Elasticsearch and dispatched via Redis. Run the supporting processes:
165
+
166
+ ```bash
167
+ python manage.py tasks scheduler # polls ES, dispatches due tasks
168
+ python manage.py tasks worker # executes tasks from Redis queue
169
+ ```
170
+
171
+ ### Analytics
172
+
173
+ ```python
174
+ from overflask.analytics import Analytics
175
+
176
+ Analytics.record("user.registered", area="auth", plan="free")
177
+ Analytics.record("purchase.completed", area="billing", amount=99)
178
+ ```
179
+
180
+ Events are buffered in-process and bulk-flushed to Elasticsearch in the background. Each `area` gets its own monthly index (`myapp_analytics_auth-2026.03`).
181
+
182
+ ## Database migrations
183
+
184
+ ```bash
185
+ python manage.py db migrate -m "add users table"
186
+ python manage.py db upgrade
187
+ python manage.py db downgrade 001
188
+ ```
189
+
190
+ ## Testing
191
+
192
+ ```bash
193
+ pytest # all tests
194
+ pytest -m unit # unit tests only (no DB)
195
+ pytest -m integration
196
+ pytest -n auto # parallel
197
+ ```
198
+
199
+ Integration tests run against a real PostgreSQL instance using a cloned template database — fast isolation without `create_all()` per test. Redis is always replaced with fakeredis.
200
+
201
+ ## Deployment
202
+
203
+ The generated project ships with a Gunicorn config and Traefik integration for HTTPS:
204
+
205
+ ```bash
206
+ bash ops/setup-traefik.sh # one-time Traefik setup per server
207
+ docker compose up -d
208
+ docker compose exec web python manage.py db upgrade
209
+ ```
210
+
211
+ See `docs/traefik.md` and `docs/gunicorn.md` in the generated project for details.
212
+
213
+ ## Development
214
+
215
+ ```bash
216
+ pip install -e ".[dev]"
217
+ pytest
218
+ ruff check src/ tests/
219
+ ruff format src/ tests/
220
+ ```
@@ -0,0 +1,159 @@
1
+ # overflask
2
+
3
+ A CLI tool that scaffolds Flask projects with component-based architecture, and a runtime library that adds SQLAlchemy models, Redis caching, async/recurring tasks, and Elasticsearch analytics on top of Flask.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install overflask @ git+https://gitlab.com/hamdarsi/overflask.git
9
+ ```
10
+
11
+ ## Create a project
12
+
13
+ ```bash
14
+ overflask create myapp
15
+ ```
16
+
17
+ The CLI prompts for an initial component name and Postgres/Redis connection details, generates the project, pre-fills `.env`, and optionally starts the database containers.
18
+
19
+ ```
20
+ myapp/
21
+ ├── manage.py # project CLI
22
+ ├── settings.py # configuration
23
+ ├── requirements.pip # dependencies
24
+ ├── compose.yaml # Docker Compose
25
+ ├── conftest.py # pytest fixtures
26
+ ├── ops/
27
+ │ ├── env.template
28
+ │ ├── migrations/ # Alembic
29
+ │ └── setup-traefik.sh
30
+ ├── docs/
31
+ └── components/
32
+ └── myapp/
33
+ ├── models.py
34
+ ├── views.py
35
+ ├── services.py
36
+ ├── cli.py
37
+ ├── tasks.py
38
+ └── tests.py
39
+ ```
40
+
41
+ ## Add a component
42
+
43
+ ```bash
44
+ python manage.py component add auth
45
+ ```
46
+
47
+ Creates `components/auth/` with the same structure and registers it in `settings.COMPONENTS`.
48
+
49
+ ## Runtime library
50
+
51
+ Generated projects import directly from `overflask`:
52
+
53
+ ### Models
54
+
55
+ ```python
56
+ from overflask import ModelBase
57
+ from sqlalchemy.orm import Mapped, mapped_column
58
+
59
+ class User(ModelBase):
60
+ # __tablename__ auto-set to "auth_users"
61
+ id: Mapped[int] = mapped_column(primary_key=True)
62
+ email: Mapped[str] = mapped_column(String(255), unique=True)
63
+ ```
64
+
65
+ Table names are derived automatically as `{component}_{plural_snake_case_model_name}`.
66
+
67
+ ### Redis cache
68
+
69
+ ```python
70
+ from overflask import cached
71
+
72
+ @cached(ttl=300)
73
+ def get_user(user_id: int):
74
+ ...
75
+ ```
76
+
77
+ Backed by Redis db 15 with stampede prevention via locking.
78
+
79
+ ### Async tasks
80
+
81
+ ```python
82
+ from overflask import async_task
83
+
84
+ @async_task
85
+ def send_welcome_email(user_id: int, email: str) -> None:
86
+ ...
87
+
88
+ # Enqueue immediately or with a delay
89
+ send_welcome_email.queue(user_id=42, email="alice@example.com")
90
+ send_welcome_email.queue(timedelta(minutes=5), user_id=42, email="alice@example.com")
91
+ ```
92
+
93
+ ### Recurring tasks
94
+
95
+ ```python
96
+ from overflask import recurring_task
97
+
98
+ @recurring_task("0 9 * * MON-FRI")
99
+ def daily_report() -> None:
100
+ ...
101
+ ```
102
+
103
+ Tasks are stored in Elasticsearch and dispatched via Redis. Run the supporting processes:
104
+
105
+ ```bash
106
+ python manage.py tasks scheduler # polls ES, dispatches due tasks
107
+ python manage.py tasks worker # executes tasks from Redis queue
108
+ ```
109
+
110
+ ### Analytics
111
+
112
+ ```python
113
+ from overflask.analytics import Analytics
114
+
115
+ Analytics.record("user.registered", area="auth", plan="free")
116
+ Analytics.record("purchase.completed", area="billing", amount=99)
117
+ ```
118
+
119
+ Events are buffered in-process and bulk-flushed to Elasticsearch in the background. Each `area` gets its own monthly index (`myapp_analytics_auth-2026.03`).
120
+
121
+ ## Database migrations
122
+
123
+ ```bash
124
+ python manage.py db migrate -m "add users table"
125
+ python manage.py db upgrade
126
+ python manage.py db downgrade 001
127
+ ```
128
+
129
+ ## Testing
130
+
131
+ ```bash
132
+ pytest # all tests
133
+ pytest -m unit # unit tests only (no DB)
134
+ pytest -m integration
135
+ pytest -n auto # parallel
136
+ ```
137
+
138
+ Integration tests run against a real PostgreSQL instance using a cloned template database — fast isolation without `create_all()` per test. Redis is always replaced with fakeredis.
139
+
140
+ ## Deployment
141
+
142
+ The generated project ships with a Gunicorn config and Traefik integration for HTTPS:
143
+
144
+ ```bash
145
+ bash ops/setup-traefik.sh # one-time Traefik setup per server
146
+ docker compose up -d
147
+ docker compose exec web python manage.py db upgrade
148
+ ```
149
+
150
+ See `docs/traefik.md` and `docs/gunicorn.md` in the generated project for details.
151
+
152
+ ## Development
153
+
154
+ ```bash
155
+ pip install -e ".[dev]"
156
+ pytest
157
+ ruff check src/ tests/
158
+ ruff format src/ tests/
159
+ ```
@@ -0,0 +1,107 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "overflask"
7
+ version = "0.1.0"
8
+ description = "Flask scaffolding CLI and runtime library — component architecture, SQLAlchemy, Redis caching, async tasks, and Elasticsearch analytics"
9
+ readme = "README.md"
10
+ license = { file = "LICENSE" }
11
+ authors = [{ name = "rklaus" }]
12
+ requires-python = ">=3.10"
13
+ keywords = ["flask", "scaffolding", "cli", "sqlalchemy", "redis", "elasticsearch", "tasks", "analytics"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Web Environment",
17
+ "Framework :: Flask",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
26
+ "Topic :: Software Development :: Code Generators",
27
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
28
+ ]
29
+
30
+ dependencies = [
31
+ "click>=8.0",
32
+ "flask>=3.0",
33
+ "python-dotenv>=1.0",
34
+ "sqlalchemy>=2.0",
35
+ "redis>=5.0",
36
+ "elasticsearch>=8.0",
37
+ "croniter>=3.0",
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://gitlab.com/overflask/overflask"
42
+ Source = "https://gitlab.com/overflask/overflask"
43
+ Tracker = "https://gitlab.com/overflask/overflask/-/issues"
44
+
45
+ [project.optional-dependencies]
46
+ dev = [
47
+ "pytest>=7.0",
48
+ "pytest-tmp-files>=0.0.2",
49
+ "ruff>=0.8",
50
+ "build>=1.0",
51
+ "twine>=5.0",
52
+ ]
53
+
54
+ [project.scripts]
55
+ overflask = "overflask.cli.cli:main"
56
+
57
+ [tool.setuptools.packages.find]
58
+ where = ["src"]
59
+
60
+ [tool.setuptools.package-data]
61
+ overflask = ["templates/**/*"]
62
+
63
+ [tool.pytest.ini_options]
64
+ testpaths = ["tests"]
65
+ markers = [
66
+ "fixture_file: load JSON fixture files into db_session",
67
+ ]
68
+
69
+ [tool.pyright]
70
+ extraPaths = ["src"]
71
+ exclude = ["build", "dist", "*.egg-info"]
72
+
73
+ [tool.ruff]
74
+ line-length = 120
75
+ target-version = "py310"
76
+
77
+ [tool.ruff.lint]
78
+ select = [
79
+ "E", # pycodestyle errors
80
+ "W", # pycodestyle warnings
81
+ "F", # pyflakes
82
+ "I", # isort (import sorting)
83
+ "N", # pep8-naming
84
+ "UP", # pyupgrade
85
+ "B", # flake8-bugbear
86
+ "C4", # flake8-comprehensions
87
+ "SIM", # flake8-simplify
88
+ ]
89
+
90
+ ignore = [
91
+ "TC001", # Move application imports to TYPE_CHECKING
92
+ "TC003", # Move standard library imports to TYPE_CHECKING
93
+ ]
94
+
95
+ [tool.ruff.lint.per-file-ignores]
96
+ "tests/**/*.py" = [
97
+ "N806", # Non-lowercase variables
98
+ "F841", # Unused variables (test fixtures)
99
+ "SIM117", # Nested with statements
100
+ "B023", # Function uses loop variable
101
+ "F401", # Unused imports
102
+ "E402", # Module import not at top
103
+ ]
104
+
105
+ [tool.ruff.format]
106
+ quote-style = "single"
107
+ docstring-code-format = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,23 @@
1
+ """overflask - A CLI tool to scaffold Flask projects with component-based architecture."""
2
+
3
+ from overflask.analytics.analytics import Analytics
4
+ from overflask.core.app import OverFlask
5
+ from overflask.core.config import Config
6
+ from overflask.db.model_base import ModelBase
7
+ from overflask.redis.cache import CachePopulationTimeoutError, cached
8
+ from overflask.redis.redis import get_redis
9
+ from overflask.tasks.tasks import async_task, delete, recurring_task
10
+
11
+ __version__ = '0.1.0'
12
+ __all__ = [
13
+ 'Analytics',
14
+ 'CachePopulationTimeoutError',
15
+ 'Config',
16
+ 'ModelBase',
17
+ 'OverFlask',
18
+ 'async_task',
19
+ 'cached',
20
+ 'delete',
21
+ 'get_redis',
22
+ 'recurring_task',
23
+ ]
@@ -0,0 +1 @@
1
+ """Analytics package for overflask."""
@@ -0,0 +1,72 @@
1
+ """Analytics static class for recording events."""
2
+
3
+ import re
4
+ from datetime import datetime, timezone
5
+ from typing import Any
6
+
7
+ from flask import g, request
8
+
9
+ from overflask.analytics.events import AnalyticsEvents
10
+ from overflask.analytics.flusher import AnalyticsFlusher
11
+ from overflask.core.config import Config
12
+
13
+
14
+ def _normalize_area(area: str) -> str:
15
+ """Lowercase and replace non-alphanumeric characters with underscores."""
16
+ return re.sub(r"[^a-z0-9]+", "_", area.lower()).strip("_")
17
+
18
+
19
+ class Analytics:
20
+ """Record analytics events. Thread-safe; buffered in-process."""
21
+
22
+ @staticmethod
23
+ def record(event: str, *, area: str, **data: Any) -> None:
24
+ """Record an analytics event.
25
+
26
+ Args:
27
+ event: Event name (e.g. "user.registered").
28
+ area: Required domain/component label (e.g. "auth"). Determines the ES index.
29
+ **data: Arbitrary key-value pairs stored under ``data`` in the event envelope.
30
+ """
31
+ doc = Analytics._build_doc(event, area, data)
32
+ AnalyticsEvents.queue(doc)
33
+ AnalyticsFlusher.ensure()
34
+
35
+ @staticmethod
36
+ def _build_doc(event: str, area: str, data: dict) -> dict:
37
+ """Build the full event envelope with optional request context enrichment."""
38
+ doc: dict[str, Any] = {
39
+ "event": event,
40
+ "timestamp": datetime.now(tz=timezone.utc).isoformat(),
41
+ "project": Config.project_name(),
42
+ "environment": Config.analytics_environment(),
43
+ "area": _normalize_area(area),
44
+ "data": data,
45
+ }
46
+
47
+ # Auto-enrich with Flask request context if available.
48
+ try:
49
+ ctx: dict[str, str] = {
50
+ "http_method": request.method,
51
+ "http_path": request.path,
52
+ }
53
+ request_id = getattr(g, "request_id", None) or request.headers.get("X-Request-ID")
54
+ if request_id:
55
+ ctx["request_id"] = request_id
56
+ user_id = getattr(g, "user_id", None)
57
+ if user_id is not None:
58
+ ctx["user_id"] = str(user_id)
59
+ doc["context"] = ctx
60
+ except RuntimeError:
61
+ # No Flask request context — omit context entirely.
62
+ pass
63
+
64
+ return doc
65
+
66
+ @staticmethod
67
+ def flush_now() -> int:
68
+ """Drain the buffer and bulk-write to ES immediately.
69
+
70
+ Returns the number of events flushed.
71
+ """
72
+ return AnalyticsFlusher.flush_now()
@@ -0,0 +1,30 @@
1
+ """Click commands for the analytics system."""
2
+
3
+ import click
4
+ from flask import Flask
5
+
6
+ from overflask.analytics.es_index_templates import AnalyticsEsIndexTemplates
7
+ from overflask.analytics.purge import AnalyticsPurge
8
+
9
+
10
+ @click.group()
11
+ def analytics():
12
+ """Analytics management commands."""
13
+
14
+
15
+ @analytics.command("provision")
16
+ @click.pass_obj
17
+ def provision_cmd(app: Flask) -> None:
18
+ """Create or update the Elasticsearch index template for analytics indices."""
19
+ with app.app_context():
20
+ AnalyticsEsIndexTemplates.ensure()
21
+ click.echo("Analytics ES index template provisioned.")
22
+
23
+
24
+ @analytics.command("purge")
25
+ @click.pass_obj
26
+ def purge_cmd(app: Flask) -> None:
27
+ """Delete analytics indices older than the configured retention period."""
28
+ with app.app_context():
29
+ AnalyticsPurge.purge_old()
30
+ click.echo("Analytics purge complete.")
@@ -0,0 +1,51 @@
1
+ """Elasticsearch index templates and naming helpers for the analytics system."""
2
+
3
+ from datetime import datetime
4
+
5
+ from overflask.core.config import Config
6
+ from overflask.es.elastic import get_es_client
7
+
8
+
9
+ class AnalyticsEsIndexTemplates:
10
+ """Manages ES index templates and index naming for the analytics system."""
11
+
12
+ @staticmethod
13
+ def analytics_index(project: str, area: str, dt: datetime) -> str:
14
+ """Return the monthly analytics index name for the given project, area, and datetime."""
15
+ return f"{project}_analytics_{area}-{dt:%Y.%m}"
16
+
17
+ @staticmethod
18
+ def ensure() -> None:
19
+ """Create or update the ES index template for all analytics index patterns.
20
+
21
+ Idempotent — safe to call on every scheduler and worker startup.
22
+ """
23
+ es = get_es_client()
24
+ project = Config.project_name()
25
+
26
+ properties = {
27
+ "event": {"type": "keyword"},
28
+ "timestamp": {"type": "date"},
29
+ "project": {"type": "keyword"},
30
+ "environment": {"type": "keyword"},
31
+ "area": {"type": "keyword"},
32
+ "context": {
33
+ "type": "object",
34
+ "properties": {
35
+ "request_id": {"type": "keyword"},
36
+ "http_method": {"type": "keyword"},
37
+ "http_path": {"type": "keyword"},
38
+ "user_id": {"type": "keyword"},
39
+ },
40
+ },
41
+ "data": {
42
+ "type": "object",
43
+ "dynamic": True,
44
+ },
45
+ }
46
+
47
+ es.indices.put_index_template(
48
+ name=f"{project}-analytics",
49
+ index_patterns=[f"{project}_analytics_*-*"],
50
+ template={"mappings": {"properties": properties}},
51
+ )