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.
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ frontend/node_modules/
9
+ storage/
10
+ *.db
11
+ *.sqlite3
12
+ .DS_Store
13
+ .claude/worktrees/
@@ -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"]