openadmin-dev 0.1.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 (37) hide show
  1. openadmin/fastapi/__init__.py +11 -0
  2. openadmin/fastapi/admin_page.py +526 -0
  3. openadmin/fastapi/admin_panel.py +89 -0
  4. openadmin/fastapi/types/__init__.py +36 -0
  5. openadmin/fastapi/types/action.py +20 -0
  6. openadmin/fastapi/types/area_chart.py +19 -0
  7. openadmin/fastapi/types/bar_chart.py +19 -0
  8. openadmin/fastapi/types/form.py +20 -0
  9. openadmin/fastapi/types/line_chart.py +19 -0
  10. openadmin/fastapi/types/markdown.py +19 -0
  11. openadmin/fastapi/types/page_protocol.py +12 -0
  12. openadmin/fastapi/types/pie_chart.py +19 -0
  13. openadmin/fastapi/types/section.py +15 -0
  14. openadmin/fastapi/types/stat.py +19 -0
  15. openadmin/fastapi/types/table.py +19 -0
  16. openadmin/fastapi/utils.py +167 -0
  17. openadmin/spec/__init__.py +41 -0
  18. openadmin/spec/components/__init__.py +35 -0
  19. openadmin/spec/components/action.py +22 -0
  20. openadmin/spec/components/area_chart.py +19 -0
  21. openadmin/spec/components/bar_chart.py +19 -0
  22. openadmin/spec/components/form.py +22 -0
  23. openadmin/spec/components/http_methods.py +7 -0
  24. openadmin/spec/components/line_chart.py +19 -0
  25. openadmin/spec/components/markdown.py +19 -0
  26. openadmin/spec/components/pie_chart.py +19 -0
  27. openadmin/spec/components/property.py +17 -0
  28. openadmin/spec/components/property_type.py +18 -0
  29. openadmin/spec/components/stat.py +19 -0
  30. openadmin/spec/components/table.py +21 -0
  31. openadmin/spec/page.py +15 -0
  32. openadmin/spec/section.py +15 -0
  33. openadmin/spec/spec.py +16 -0
  34. openadmin_dev-0.1.0.dist-info/METADATA +304 -0
  35. openadmin_dev-0.1.0.dist-info/RECORD +37 -0
  36. openadmin_dev-0.1.0.dist-info/WHEEL +4 -0
  37. openadmin_dev-0.1.0.dist-info/licenses/LICENSE +661 -0
openadmin/spec/spec.py ADDED
@@ -0,0 +1,16 @@
1
+ # SPDX-FileCopyrightText: 2026 OpenAdmin
2
+ #
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ from typing import List
6
+
7
+ from pydantic import BaseModel
8
+
9
+ from .section import Section
10
+
11
+
12
+ class Spec(BaseModel):
13
+ version: str
14
+ name: str
15
+ description: str | None = None
16
+ sections: List[Section]
@@ -0,0 +1,304 @@
1
+ Metadata-Version: 2.4
2
+ Name: openadmin-dev
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Project-URL: Homepage, https://github.com/openadmin-team/openadmin-py
6
+ Project-URL: Issues, https://github.com/openadmin-team/openadmin-py/issues
7
+ Author-email: Mykyta Kasianenko <mykytakasianenko@gmail.com>
8
+ License-Expression: AGPL-3.0-or-later
9
+ License-File: LICENSE
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.14
13
+ Requires-Dist: fastapi[standard]>=0.136.3
14
+ Description-Content-Type: text/markdown
15
+
16
+ <div align="center">
17
+ <img src="docs/assets/logo.png" alt="OpenAdmin" width="180" />
18
+
19
+ <h1>openadmin-py</h1>
20
+
21
+ <p>Build admin panels as FastAPI routes — stats, tables, charts, and actions, all in pure Python.</p>
22
+
23
+ [![License: AGPL-3.0-or-later](https://img.shields.io/badge/license-AGPL--3.0--or--later-green?style=flat-square)](LICENSE)
24
+ [![Python](https://img.shields.io/badge/python-3.14%2B-blue?style=flat-square)](https://python.org)
25
+ [![FastAPI](https://img.shields.io/badge/built%20on-FastAPI-009688?style=flat-square)](https://fastapi.tiangolo.com)
26
+ </div>
27
+
28
+ ---
29
+
30
+ **OpenAdmin** is a FastAPI-native library for building admin dashboards. Define pages with typed decorators — no frontend code, no templates, no configuration files. Every page is just a router; every widget is just an endpoint.
31
+
32
+ ## Features
33
+
34
+ - **Stats** — display single values: counts, totals, booleans, percentages
35
+ - **Tables** — paginated, searchable data grids with per-row actions
36
+ - **Charts** — area, bar, line, and pie charts with labeled series
37
+ - **Actions** — trigger HTTP calls (GET / POST / PUT / PATCH / DELETE) from the UI
38
+ - **Forms** — structured forms that POST/PUT/PATCH/DELETE to your own endpoints
39
+ - **Markdown** — render rich text content on any page
40
+ - **FastAPI-native** — full dependency injection, OpenAPI docs, and router composition out of the box
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install openadmin-py
46
+ # or with uv
47
+ uv add openadmin-py
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ ```python
53
+ from fastapi import FastAPI
54
+ from openadmin.fastapi import AdminPanel, AdminPage
55
+ from openadmin.types import Stat, Table
56
+
57
+ # Create an admin panel (a FastAPI sub-application)
58
+ admin = AdminPanel()
59
+
60
+ # Define a page
61
+ page = AdminPage("Dashboard")
62
+
63
+ @page.stat("Total Users")
64
+ async def total_users() -> Stat:
65
+ return Stat(value=1_024)
66
+
67
+ @page.table("Recent Users")
68
+ async def recent_users() -> Table:
69
+ return Table(data=[
70
+ {"id": 1, "name": "Alice", "role": "admin"},
71
+ {"id": 2, "name": "Bob", "role": "viewer"},
72
+ ])
73
+
74
+ # Register the page and mount the panel
75
+ admin.include_page(page)
76
+
77
+ app = FastAPI()
78
+ app.mount("/admin", admin)
79
+ ```
80
+
81
+ ## Usage
82
+
83
+ ### Stats
84
+
85
+ Display a single numeric or boolean value.
86
+
87
+ ```python
88
+ from openadmin.types import Stat
89
+
90
+ @page.stat("Active Sessions")
91
+ async def active_sessions() -> Stat:
92
+ return Stat(value=42)
93
+ ```
94
+
95
+ ### Tables
96
+
97
+ Paginated tables with optional search. Use the built-in dependency types to receive pagination and search parameters automatically.
98
+
99
+ ```python
100
+ from openadmin.fastapi import PaginationParamsDep, SearchQueryDep
101
+ from openadmin.types import Table
102
+
103
+ @page.table("Users")
104
+ async def users_table(
105
+ pagination: PaginationParamsDep,
106
+ search: SearchQueryDep,
107
+ ) -> Table:
108
+ # pagination.page, pagination.per_page
109
+ # search is str | None
110
+ return Table(data=[...])
111
+ ```
112
+
113
+ #### Row Actions
114
+
115
+ Attach per-row action buttons by including `__actions__` in each row:
116
+
117
+ ```python
118
+ from openadmin.types import Action, Table, TableRow
119
+
120
+ @page.table("Users")
121
+ async def users_table() -> Table:
122
+ return Table(data=[
123
+ TableRow(
124
+ id=1,
125
+ name="Alice",
126
+ __actions__=[
127
+ Action(color="danger", method="DELETE", url="/users/1", body=None),
128
+ Action(color="info", method="POST", url="/users/1/reset", body=None),
129
+ ],
130
+ )
131
+ ])
132
+ ```
133
+
134
+ ### Charts
135
+
136
+ All chart types share the same structure: a `data` list of dicts and a `config` that maps series keys to display labels and colors.
137
+
138
+ ```python
139
+ from openadmin.types import BarChart, PieChart
140
+
141
+ @page.bar_chart("Sales by Region", "Total sales per region this quarter")
142
+ async def sales_chart() -> BarChart:
143
+ return BarChart(
144
+ data=[
145
+ {"region": "North", "sales": 120},
146
+ {"region": "South", "sales": 95},
147
+ ],
148
+ config={"sales": {"label": "Sales", "color": "#6366f1"}},
149
+ )
150
+
151
+ @page.pie_chart("User Roles", "Breakdown of user roles")
152
+ async def roles_chart() -> PieChart:
153
+ return PieChart(
154
+ data=[
155
+ {"segment": "Admin", "count": 5},
156
+ {"segment": "Editor", "count": 20},
157
+ {"segment": "Viewer", "count": 75},
158
+ ],
159
+ config={"count": {"label": "Users", "color": "#10b981"}},
160
+ )
161
+ ```
162
+
163
+ ### Actions
164
+
165
+ Expose buttons that trigger HTTP requests — useful for one-off operations like cache clearing or triggering background jobs.
166
+
167
+ ```python
168
+ @page.action_post("Clear Cache")
169
+ async def clear_cache():
170
+ # your logic here
171
+ return {"status": "cleared"}
172
+ ```
173
+
174
+ ### Forms
175
+
176
+ Forms collect user input and submit it to your endpoint. Mark a form hidden if it should not appear in the page navigation.
177
+
178
+ ```python
179
+ from pydantic import BaseModel
180
+
181
+ class InvitePayload(BaseModel):
182
+ email: str
183
+ role: str
184
+
185
+ @page.form_post("Invite User", "Send an invitation email to a new user")
186
+ async def invite_user(payload: InvitePayload):
187
+ # send invite...
188
+ return {"invited": payload.email}
189
+ ```
190
+
191
+ ### Markdown
192
+
193
+ Render markdown text directly on a page — useful for instructions, changelogs, or documentation sections.
194
+
195
+ ```python
196
+ @page.markdown("Release Notes")
197
+ async def release_notes() -> str:
198
+ return "## v1.2.0\n- Added dark mode\n- Fixed pagination bug"
199
+ ```
200
+
201
+ ## Full Example
202
+
203
+ A real-world page querying a SQLAlchemy database:
204
+
205
+ ```python
206
+ from sqlalchemy import func, select
207
+ from openadmin.fastapi import AdminPage, PaginationParamsDep, SearchQueryDep
208
+ from openadmin.types import Stat, Table, BarChart
209
+
210
+ page = AdminPage("Library")
211
+
212
+ @page.stat("Total Books")
213
+ async def total_books(session: AsyncSessionDep) -> Stat:
214
+ result = await session.execute(select(func.count()).select_from(Book))
215
+ return Stat(value=result.scalar_one())
216
+
217
+ @page.table("Books")
218
+ async def books_table(
219
+ session: AsyncSessionDep,
220
+ pagination: PaginationParamsDep,
221
+ search: SearchQueryDep,
222
+ ) -> Table:
223
+ stmt = select(Book).offset(pagination.page * pagination.per_page).limit(pagination.per_page)
224
+ books = (await session.execute(stmt)).scalars().all()
225
+ return Table(data=[{"title": b.title, "year": b.published_year} for b in books])
226
+
227
+ @page.bar_chart("Books per Genre", "Number of books in each genre")
228
+ async def books_per_genre(session: AsyncSessionDep) -> BarChart:
229
+ rows = (await session.execute(
230
+ select(Genre.name, func.count().label("books"))
231
+ .join(BookToGenre).group_by(Genre.id)
232
+ )).all()
233
+ return BarChart(
234
+ data=[{"genre": name, "books": count} for name, count in rows],
235
+ config={"books": {"label": "Books", "color": "#6366f1"}},
236
+ )
237
+ ```
238
+
239
+ See the [`examples/`](examples/) directory for a complete runnable application.
240
+
241
+ ```bash
242
+ make dev/run
243
+ # → http://127.0.0.1:8000/docs
244
+ ```
245
+
246
+ ## API Reference
247
+
248
+ ### `AdminPanel`
249
+
250
+ A `FastAPI` subclass. Use `include_page(page)` to register an `AdminPage`.
251
+
252
+ | Method | Description |
253
+ |---|---|
254
+ | `include_page(page, tags=None)` | Mount an `AdminPage` onto the panel |
255
+
256
+ ### `AdminPage`
257
+
258
+ An `APIRouter` subclass. Each decorator creates a typed GET (or POST/PUT/etc.) endpoint under the page's prefix.
259
+
260
+ | Decorator | HTTP | Response type |
261
+ |---|---|---|
262
+ | `@page.stat(name)` | GET | `Stat` |
263
+ | `@page.table(name)` | GET | `Table` |
264
+ | `@page.markdown(name)` | GET | `str` |
265
+ | `@page.area_chart(name, description)` | GET | `AreaChart` |
266
+ | `@page.bar_chart(name, description)` | GET | `BarChart` |
267
+ | `@page.line_chart(name, description)` | GET | `LineChart` |
268
+ | `@page.pie_chart(name, description)` | GET | `PieChart` |
269
+ | `@page.action_get/post/put/patch/delete(name)` | * | any |
270
+ | `@page.form_post/put/patch/delete(name, description)` | * | any |
271
+
272
+ ### Dependencies
273
+
274
+ | Name | Type | Description |
275
+ |---|---|---|
276
+ | `PaginationParamsDep` | `PaginationParams` | `page` and `per_page` query params |
277
+ | `SearchQueryDep` | `str \| None` | `search` query param |
278
+
279
+ ### Types
280
+
281
+ | Type | Fields |
282
+ |---|---|
283
+ | `Stat` | `value: str \| bool \| int \| float` |
284
+ | `Table` | `data: list[TableRow \| dict]` |
285
+ | `TableRow` | any fields + `__actions__: list[Action]` |
286
+ | `Action` | `color`, `method`, `url`, `body` |
287
+ | `AreaChart / BarChart / LineChart / PieChart` | `data: list[dict]`, `config: dict` |
288
+
289
+ ## Development
290
+
291
+ ```bash
292
+ # Run the example app
293
+ make dev/run
294
+
295
+ # Run all checks (format, lint, types, tests, security)
296
+ make check
297
+
298
+ # Auto-fix formatting and lint issues
299
+ make fix
300
+ ```
301
+
302
+ ## License
303
+
304
+ [AGPL-3.0-or-later](LICENSE) — © 2026 OpenAdmin
@@ -0,0 +1,37 @@
1
+ openadmin/fastapi/__init__.py,sha256=RiqYntBCCLt9snCMc3D-h3Y-1mTrqc9lHNmkFIjw9FI,209
2
+ openadmin/fastapi/admin_page.py,sha256=kwEBjxB9iso6mtJmm41goEY-a8RTuF6fcDMzEq0Fk1E,15478
3
+ openadmin/fastapi/admin_panel.py,sha256=LzpEjIL17Xxf1rXq4e6W2pzG9SErdlFB8NjQxEj7lys,2609
4
+ openadmin/fastapi/utils.py,sha256=eleWv0kWZgndSJI2vFTR3C4vlNabL0Nxz1QnuxP6XhA,5180
5
+ openadmin/fastapi/types/__init__.py,sha256=VQRRr668R0npyVWYvDSKXoAx4JHbutynHUqOdgEX34E,754
6
+ openadmin/fastapi/types/action.py,sha256=MzFtamoavThIXrFkujBJMntZkJGv4JT5IrqOt_Blaxg,432
7
+ openadmin/fastapi/types/area_chart.py,sha256=aoLAR6oWSI3vw14zGxRKlnlrnxJLqDDYI8BCk8oOAM8,415
8
+ openadmin/fastapi/types/bar_chart.py,sha256=Gl9n93moh7sfFPf4CvZLmjLEuWjy5AdQaOWFi06ACPU,414
9
+ openadmin/fastapi/types/form.py,sha256=0Ww-7vrFJP9c5J1_oGbug3fDzrWXOp7M3pYC9LRuhAo,429
10
+ openadmin/fastapi/types/line_chart.py,sha256=OWYhEXHPFob-SPnu7shz6Zqf5x1NlqEUt__YW1Gc4jc,415
11
+ openadmin/fastapi/types/markdown.py,sha256=2g8uX_n2Shx-6r8ukehv9LMA4iD6i7ynuph65gRgFaQ,414
12
+ openadmin/fastapi/types/page_protocol.py,sha256=zMio8SamTFO6_Dxp_8_xnErTC6VEfryVtW_5jOM6oDw,265
13
+ openadmin/fastapi/types/pie_chart.py,sha256=MYp1mg7YlEgAA4RiPaz0g-0ynlSRflPc4nZz85soG2M,414
14
+ openadmin/fastapi/types/section.py,sha256=oWo5caUkLrdmvXnc3pdCC3cwLk8iMT4slrH5GnxI0E8,296
15
+ openadmin/fastapi/types/stat.py,sha256=fWit9vyIEkVzkf3IxkV73lAtttxgPfWOMfsVQDP44q8,410
16
+ openadmin/fastapi/types/table.py,sha256=JzYkV8zTa5rMmMh-tnmVm8Vl_LJefFXPsT0tJvTMjJo,411
17
+ openadmin/spec/__init__.py,sha256=9ZZnQz67JN7TyZhoI43G7aNKkOidXFX5Lxjkr41lYQ8,660
18
+ openadmin/spec/page.py,sha256=XCiQM_37lTQjjMQjWhrWSHof-pD_j69HZu5n27iHfcA,279
19
+ openadmin/spec/section.py,sha256=9qZny4rGqHeMpl2VApF8vqLuR-oMsc2kE1DmQdO62BI,261
20
+ openadmin/spec/spec.py,sha256=9wMyVDj4yMUkEdI-5f3FAYLDqXJ_FddME9padayB5Fs,294
21
+ openadmin/spec/components/__init__.py,sha256=jmm-FRcjJ_aDq_TU4mX4YJSUe1FTo9gmNPuF6GrRIfo,741
22
+ openadmin/spec/components/action.py,sha256=a4YAHS8kHKKHItP4_he2_rZq3z3gYf6U0ZmGAgPcJRE,522
23
+ openadmin/spec/components/area_chart.py,sha256=PlQmnTBSlsGESrW_LcBsxRnbfjfWroTbKEgNcVsVoR4,411
24
+ openadmin/spec/components/bar_chart.py,sha256=HccaLgfUsXZ5LASBBhVYp9atVbT6-Ek5fsM_gyRwPLg,409
25
+ openadmin/spec/components/form.py,sha256=683FleQsJPGQbJzWBn2fS3W94SGSxmd_6MpYTxGPTC4,517
26
+ openadmin/spec/components/http_methods.py,sha256=gRcLLnMCHvXYyq_8T4aVmjxy_kfOAc7x8SLc3MJmAl0,192
27
+ openadmin/spec/components/line_chart.py,sha256=Ebqk1Q-lgY2839iAumV5_9ZTdw5TKIOSP3kvg9xUKCs,411
28
+ openadmin/spec/components/markdown.py,sha256=u3zH8CGxR4udXFWNY7iqy1Cek44kGY28mO78ta3j8AU,414
29
+ openadmin/spec/components/pie_chart.py,sha256=lQC2Ot7UT4typoa3D11NCQ4HzCXrWQubWtXGamXi6kI,409
30
+ openadmin/spec/components/property.py,sha256=svoPxt3miGmK_ySFPFJmGdfZIAYBxaK37VsYwUjctbI,492
31
+ openadmin/spec/components/property_type.py,sha256=m1eJ7H5YIP4dhaa899Qc9BCRkZOwP_DELl6vPZB31HY,306
32
+ openadmin/spec/components/stat.py,sha256=sXQttzMJulS68foVeJ2Y0W4-UH_FSa4nOnxiLfQSGGE,400
33
+ openadmin/spec/components/table.py,sha256=8dJFnKNj-yOE85mFWQVdoku5tTWA4UZHwV8zId0z6j8,500
34
+ openadmin_dev-0.1.0.dist-info/METADATA,sha256=6CrL_e_a7mLGLklzQuF1cByvumS6nAN8krZkNBTWvQc,8892
35
+ openadmin_dev-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
36
+ openadmin_dev-0.1.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
37
+ openadmin_dev-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any