tungsten-saved-views 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.
- tungsten_saved_views-0.1.0/.gitignore +13 -0
- tungsten_saved_views-0.1.0/PKG-INFO +44 -0
- tungsten_saved_views-0.1.0/README.md +30 -0
- tungsten_saved_views-0.1.0/example.py +131 -0
- tungsten_saved_views-0.1.0/pyproject.toml +22 -0
- tungsten_saved_views-0.1.0/src/tungsten_saved_views/__init__.py +466 -0
- tungsten_saved_views-0.1.0/src/tungsten_saved_views/models.py +58 -0
- tungsten_saved_views-0.1.0/src/tungsten_saved_views/resources.py +138 -0
- tungsten_saved_views-0.1.0/src/tungsten_saved_views/service.py +173 -0
- tungsten_saved_views-0.1.0/src/tungsten_saved_views/templates/tungsten_saved_views/bar.html +176 -0
- tungsten_saved_views-0.1.0/tests/conftest.py +133 -0
- tungsten_saved_views-0.1.0/tests/test_saved_views.py +516 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tungsten-saved-views
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Saved table views for Tungsten: users save filters, search, sort and columns as one-click tabs like My Leads or Pending.
|
|
5
|
+
Project-URL: Homepage, https://tungsten.prisminfoways.com/
|
|
6
|
+
Project-URL: Documentation, https://tungsten.prisminfoways.com/docs/saved-views
|
|
7
|
+
Project-URL: Repository, https://github.com/Prism-Infoways/Tungsten
|
|
8
|
+
Author: Prism Infoways
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: admin,fastapi,filters,saved views,tables,tungsten,tungsten-admin
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: tungsten-admin>=0.1.7
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# tungsten-saved-views
|
|
16
|
+
|
|
17
|
+
Saved table views for [Tungsten](https://tungsten.prisminfoways.com/). Users save their own filters, search, sort, tab and columns as one-click views like **My leads** or **Pending**, share them with the team, and pick the view a list opens with.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install tungsten-saved-views
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from tungsten_saved_views import SavedViewsPlugin
|
|
25
|
+
|
|
26
|
+
panel.plugin(SavedViewsPlugin())
|
|
27
|
+
panel.create_tables(engine)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Every resource list gets a views bar above the table:
|
|
31
|
+
|
|
32
|
+
- **Save view** keeps the filters, search, sort, tab, page size, grouping and visible columns.
|
|
33
|
+
- One click opens a view. The current view is highlighted.
|
|
34
|
+
- **Open the list with this view** makes it the user's default.
|
|
35
|
+
- Rename, update, share and delete from the arrow next to a view, or on the **Saved views** page in the user menu.
|
|
36
|
+
- Ready views from code with `preset()`, with `{me}` and `{today}`.
|
|
37
|
+
- **My tickets** and **My leads** are added when `tungsten-tickets` or `tungsten-leads` is installed.
|
|
38
|
+
- Permissions `saved_views.share` and `saved_views.manage` for panels with roles.
|
|
39
|
+
|
|
40
|
+
Full guide: https://tungsten.prisminfoways.com/docs/saved-views
|
|
41
|
+
|
|
42
|
+
Try it: `uvicorn example:app` in this folder, then open http://127.0.0.1:8000/admin (admin@example.com / password).
|
|
43
|
+
|
|
44
|
+
Needs `tungsten-admin` 0.1.7 or newer.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# tungsten-saved-views
|
|
2
|
+
|
|
3
|
+
Saved table views for [Tungsten](https://tungsten.prisminfoways.com/). Users save their own filters, search, sort, tab and columns as one-click views like **My leads** or **Pending**, share them with the team, and pick the view a list opens with.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install tungsten-saved-views
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from tungsten_saved_views import SavedViewsPlugin
|
|
11
|
+
|
|
12
|
+
panel.plugin(SavedViewsPlugin())
|
|
13
|
+
panel.create_tables(engine)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Every resource list gets a views bar above the table:
|
|
17
|
+
|
|
18
|
+
- **Save view** keeps the filters, search, sort, tab, page size, grouping and visible columns.
|
|
19
|
+
- One click opens a view. The current view is highlighted.
|
|
20
|
+
- **Open the list with this view** makes it the user's default.
|
|
21
|
+
- Rename, update, share and delete from the arrow next to a view, or on the **Saved views** page in the user menu.
|
|
22
|
+
- Ready views from code with `preset()`, with `{me}` and `{today}`.
|
|
23
|
+
- **My tickets** and **My leads** are added when `tungsten-tickets` or `tungsten-leads` is installed.
|
|
24
|
+
- Permissions `saved_views.share` and `saved_views.manage` for panels with roles.
|
|
25
|
+
|
|
26
|
+
Full guide: https://tungsten.prisminfoways.com/docs/saved-views
|
|
27
|
+
|
|
28
|
+
Try it: `uvicorn example:app` in this folder, then open http://127.0.0.1:8000/admin (admin@example.com / password).
|
|
29
|
+
|
|
30
|
+
Needs `tungsten-admin` 0.1.7 or newer.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Try the saved views plugin.
|
|
2
|
+
|
|
3
|
+
pip install -e . -e plugins/saved-views uvicorn
|
|
4
|
+
uvicorn plugins.saved-views.example:app --reload (from plugins/saved-views: uvicorn example:app)
|
|
5
|
+
|
|
6
|
+
Open http://127.0.0.1:8000/admin and sign in with admin@example.com / password.
|
|
7
|
+
On the Orders list, pick a filter or a search, then click "Save view".
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import datetime as dt
|
|
13
|
+
import random
|
|
14
|
+
|
|
15
|
+
from fastapi import FastAPI
|
|
16
|
+
from fastapi.responses import RedirectResponse
|
|
17
|
+
from sqlalchemy import Boolean, Date, Integer, String, create_engine, select
|
|
18
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker
|
|
19
|
+
from tungsten_saved_views import SavedViewsPlugin
|
|
20
|
+
|
|
21
|
+
from tungsten import Auth, Panel, Resource, hash_password
|
|
22
|
+
from tungsten.forms import DatePicker, Select, TextInput
|
|
23
|
+
from tungsten.tables import DateFilter, ListTab, SelectFilter, TextColumn
|
|
24
|
+
|
|
25
|
+
STATUSES = {"new": "New", "pending": "Pending", "shipped": "Shipped", "cancelled": "Cancelled"}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Base(DeclarativeBase):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class User(Base):
|
|
33
|
+
__tablename__ = "users"
|
|
34
|
+
|
|
35
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
|
36
|
+
name: Mapped[str] = mapped_column(String(100))
|
|
37
|
+
email: Mapped[str] = mapped_column(String(200), unique=True)
|
|
38
|
+
password: Mapped[str] = mapped_column(String(200))
|
|
39
|
+
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Order(Base):
|
|
43
|
+
__tablename__ = "orders"
|
|
44
|
+
|
|
45
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
|
46
|
+
customer: Mapped[str] = mapped_column(String(100))
|
|
47
|
+
status: Mapped[str] = mapped_column(String(20), default="new")
|
|
48
|
+
owner_id: Mapped[str] = mapped_column(String(20), default="")
|
|
49
|
+
total: Mapped[int] = mapped_column(Integer, default=0)
|
|
50
|
+
placed_on: Mapped[dt.date] = mapped_column(Date, default=dt.date.today)
|
|
51
|
+
is_paid: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def user_options(db):
|
|
55
|
+
return {str(u.id): u.name for u in db.scalars(select(User)).all()}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class OrderResource(Resource):
|
|
59
|
+
model = Order
|
|
60
|
+
icon = "shopping-cart"
|
|
61
|
+
soft_delete_column = None
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def form(cls, form):
|
|
65
|
+
return form.columns(2).schema([
|
|
66
|
+
TextInput("customer").required(),
|
|
67
|
+
Select("status").options(STATUSES).required(),
|
|
68
|
+
Select("owner_id").label("Owner").options(user_options),
|
|
69
|
+
TextInput("total").integer(),
|
|
70
|
+
DatePicker("placed_on"),
|
|
71
|
+
])
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def table(cls, table):
|
|
75
|
+
return (
|
|
76
|
+
table.columns([
|
|
77
|
+
TextColumn("customer").searchable().sortable(),
|
|
78
|
+
TextColumn("status").badge().sortable(),
|
|
79
|
+
TextColumn("owner_id").label("Owner").state(lambda record, db: user_options(db).get(record.owner_id))
|
|
80
|
+
.toggleable(),
|
|
81
|
+
TextColumn("total").sortable().toggleable(),
|
|
82
|
+
TextColumn("placed_on").date().sortable().toggleable(),
|
|
83
|
+
])
|
|
84
|
+
.filters([
|
|
85
|
+
SelectFilter("status").options(STATUSES),
|
|
86
|
+
SelectFilter("owner_id").label("Owner").options(user_options),
|
|
87
|
+
DateFilter("placed_on"),
|
|
88
|
+
])
|
|
89
|
+
.tabs([ListTab("all").label("All").badge(),
|
|
90
|
+
ListTab("unpaid").label("Unpaid").query(lambda query, model: query.where(model.is_paid.is_(False)))])
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
engine = create_engine("sqlite:///saved_views_demo.db")
|
|
95
|
+
Base.metadata.create_all(engine)
|
|
96
|
+
Session = sessionmaker(engine, expire_on_commit=False)
|
|
97
|
+
|
|
98
|
+
views = SavedViewsPlugin()
|
|
99
|
+
# a ready view for everyone; {me} is the signed-in user
|
|
100
|
+
views.preset("orders", "My orders", "filters.owner_id.value={me}", icon="user-round", color="primary")
|
|
101
|
+
|
|
102
|
+
panel = Panel(path="/admin", session_factory=Session, secret_key="dev-only", auth=Auth(User),
|
|
103
|
+
brand_name="Tungsten Shop", spa=True)
|
|
104
|
+
panel.resources([OrderResource])
|
|
105
|
+
panel.plugin(views)
|
|
106
|
+
panel.create_tables(engine)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def seed() -> None:
|
|
110
|
+
with Session() as db:
|
|
111
|
+
if db.scalar(select(User)) is not None:
|
|
112
|
+
return
|
|
113
|
+
db.add(User(name="Asha Admin", email="admin@example.com", password=hash_password("password")))
|
|
114
|
+
db.add(User(name="Ravi Sales", email="ravi@example.com", password=hash_password("password")))
|
|
115
|
+
rnd = random.Random(7)
|
|
116
|
+
names = ["Aarav", "Diya", "Kabir", "Meera", "Rohan", "Sara", "Vihaan", "Zoya", "Ishaan", "Anaya"]
|
|
117
|
+
for i in range(40):
|
|
118
|
+
db.add(Order(customer=f"{rnd.choice(names)} {i + 1}", status=rnd.choice(list(STATUSES)),
|
|
119
|
+
owner_id=rnd.choice(["1", "2"]), total=rnd.randint(300, 9000), is_paid=rnd.random() < 0.5,
|
|
120
|
+
placed_on=dt.date.today() - dt.timedelta(days=rnd.randint(0, 60))))
|
|
121
|
+
db.commit()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
seed()
|
|
125
|
+
app = FastAPI()
|
|
126
|
+
panel.mount(app)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@app.get("/")
|
|
130
|
+
def home():
|
|
131
|
+
return RedirectResponse("/admin")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.24"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tungsten-saved-views"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Saved table views for Tungsten: users save filters, search, sort and columns as one-click tabs like My Leads or Pending."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Prism Infoways" }]
|
|
13
|
+
keywords = ["tungsten", "tungsten-admin", "saved views", "filters", "tables", "fastapi", "admin"]
|
|
14
|
+
dependencies = ["tungsten-admin>=0.1.7"]
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
Homepage = "https://tungsten.prisminfoways.com/"
|
|
18
|
+
Documentation = "https://tungsten.prisminfoways.com/docs/saved-views"
|
|
19
|
+
Repository = "https://github.com/Prism-Infoways/Tungsten"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["src/tungsten_saved_views"]
|