phlo-observatory-example 0.1.1__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,168 @@
1
+ # Environment variables
2
+ .env
3
+ .env.local
4
+ .env.*.local
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ *.so
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ /lib/
19
+ /lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+ .pytest_cache/
29
+ .coverage
30
+ htmlcov/
31
+ *.cover
32
+ .hypothesis/
33
+ .mypy_cache/
34
+ .ruff_cache/
35
+ .pytype/
36
+ *.pyc
37
+ .cache/
38
+
39
+ # Virtual environments
40
+ venv/
41
+ ENV/
42
+ env/
43
+ .venv
44
+
45
+ # Git worktrees
46
+ .worktrees/
47
+
48
+ # IDEs
49
+ .vscode/
50
+ .idea/
51
+ *.swp
52
+ *.swo
53
+ *~
54
+ .DS_Store
55
+
56
+ # Dagster
57
+ dagster/.nux/
58
+ dagster/.telemetry/
59
+ dagster/__pycache__/
60
+ dagster/history/
61
+ dagster/.logs_queue/
62
+ dagster/schedules/schedules.db
63
+ dagster/schedules/*.db
64
+ dagster/storage/
65
+ dagster/tmpfiles/
66
+ dagster_home/
67
+
68
+ # dbt
69
+ transforms/dbt/target/
70
+ transforms/dbt/dbt_packages/
71
+ transforms/dbt/logs/
72
+ transforms/dbt/profiles/profiles.yml.bak
73
+ transforms/dbt/package-lock.yml
74
+
75
+ # Data directories
76
+ volumes/
77
+ *.duckdb
78
+ *.duckdb.wal
79
+
80
+ # Great Expectations
81
+ great_expectations/uncommitted/
82
+ great_expectations/.ge_store_backend_id
83
+ great_expectations/**/.ge_store_backend_id
84
+ great_expectations/validations/
85
+
86
+ # Docker
87
+ docker-compose.override.yml
88
+
89
+ # Logs
90
+ *.log
91
+ logs/
92
+
93
+ # Temporary files
94
+ *.tmp
95
+ *.temp
96
+ .cache/
97
+
98
+ # Data files and formats
99
+ *.parquet
100
+ *.csv
101
+ *.pkl
102
+ *.pickle
103
+ *.h5
104
+ *.hdf5
105
+ *.feather
106
+
107
+ # Cloud and infrastructure
108
+ .aws/
109
+ .gcp/
110
+ .azure/
111
+ .terraform/
112
+ *.tfstate
113
+ *.tfvars
114
+
115
+ # Jupyter notebooks (if not wanted)
116
+ # *.ipynb
117
+
118
+ # OS generated files
119
+ .DS_Store
120
+ .DS_Store?
121
+ ._*
122
+ .Spotlight-V100
123
+ .Trashes
124
+ ehthumbs.db
125
+ Thumbs.db
126
+
127
+ # IDE and editor files
128
+ .vscode/
129
+ .idea/
130
+ *.swp
131
+ *.swo
132
+ *~
133
+ .project
134
+ .classpath
135
+ .settings/
136
+
137
+ # Logs and temporary files
138
+ *.log
139
+ logs/
140
+ *.tmp
141
+ *.temp
142
+ .cache/
143
+
144
+ transforms/dbt/target
145
+ specs
146
+ PLATFORM_REVIEW.md
147
+
148
+ # Temporary/working files
149
+ # context/
150
+ context/
151
+
152
+ # Beads issue tracker (local-only files)
153
+ .beads/beads.db
154
+ .beads/beads.db-*
155
+ .beads/bd.sock
156
+ .beads/bd.pipe
157
+ .beads/.exclusive-lock
158
+ .beads/daemon.*
159
+ .git/beads-worktrees/
160
+
161
+ # bv (beads viewer) local config and caches
162
+ .bv/
163
+
164
+ # phlo local config and caches
165
+ .phlo/
166
+
167
+ .claude
168
+ .ralph/
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: phlo-observatory-example
3
+ Version: 0.1.1
4
+ Summary: Example Observatory extension plugin
5
+ Author-email: Phlo Team <team@phlo.dev>
6
+ License: MIT
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: phlo-observatory>=0.1.0
9
+ Requires-Dist: phlo>=0.1.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # phlo-observatory-example
13
+
14
+ Example Observatory extension plugin for Phlo.
15
+
16
+ ## What It Shows
17
+
18
+ - Extension manifest wiring for routes, nav items, dashboard slots, and settings.
19
+ - Static frontend asset packaging from `observatory_assets/`.
20
+ - Plugin registration through `phlo.plugins.observatory`.
21
+
22
+ ## Entry Point
23
+
24
+ - `example = "phlo_observatory_example.observatory_plugin:ExampleObservatoryExtension"`
@@ -0,0 +1,13 @@
1
+ # phlo-observatory-example
2
+
3
+ Example Observatory extension plugin for Phlo.
4
+
5
+ ## What It Shows
6
+
7
+ - Extension manifest wiring for routes, nav items, dashboard slots, and settings.
8
+ - Static frontend asset packaging from `observatory_assets/`.
9
+ - Plugin registration through `phlo.plugins.observatory`.
10
+
11
+ ## Entry Point
12
+
13
+ - `example = "phlo_observatory_example.observatory_plugin:ExampleObservatoryExtension"`
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "phlo-observatory-example"
7
+ version = "0.1.1"
8
+ description = "Example Observatory extension plugin"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ authors = [
12
+ {name = "Phlo Team", email = "team@phlo.dev"},
13
+ ]
14
+ license = {text = "MIT"}
15
+ dependencies = [
16
+ "phlo>=0.1.0",
17
+ "phlo-observatory>=0.1.0",
18
+ ]
19
+
20
+ [project.entry-points."phlo.plugins.observatory"]
21
+ example = "phlo_observatory_example.observatory_plugin:ExampleObservatoryExtension"
22
+
23
+ [tool.hatch.build.targets.wheel]
24
+ packages = ["src/phlo_observatory_example"]
25
+ include = ["src/phlo_observatory_example/observatory_assets/*"]
26
+
27
+ [tool.hatch.build.targets.sdist]
28
+ include = ["src/phlo_observatory_example"]
29
+
30
+ [tool.ruff]
31
+ line-length = 100
32
+ target-version = "py311"
@@ -0,0 +1 @@
1
+ """Example Observatory extension package."""
@@ -0,0 +1 @@
1
+ Observatory assets for phlo-observatory-example.
@@ -0,0 +1,141 @@
1
+ export function registerRoutes({ createRoute, rootRoute }) {
2
+ return createRoute({
3
+ getParentRoute: () => rootRoute,
4
+ path: '/extensions/example',
5
+ component: ExamplePage,
6
+ })
7
+ }
8
+
9
+ export function registerDashboardSlot({ register }) {
10
+ register(DashboardNote)
11
+ }
12
+
13
+ export function registerHubSlot({ register }) {
14
+ register(HubNote)
15
+ }
16
+
17
+ const settingsApi = { loadSettings: null, saveSettings: null }
18
+
19
+ export function registerSettings({ register, loadSettings, saveSettings }) {
20
+ settingsApi.loadSettings = loadSettings
21
+ settingsApi.saveSettings = saveSettings
22
+ register({
23
+ id: 'example-settings',
24
+ title: 'Example Extension',
25
+ description: 'Demonstrates extension settings panels.',
26
+ component: ExampleSettings,
27
+ })
28
+ }
29
+
30
+ function ExamplePage() {
31
+ return 'Example extension route loaded.'
32
+ }
33
+
34
+ function DashboardNote() {
35
+ return 'Extension slot: dashboard.after-cards.'
36
+ }
37
+
38
+ function HubNote() {
39
+ return 'Extension slot: hub.after-stats.'
40
+ }
41
+
42
+ function ExampleSettings() {
43
+ const React = typeof globalThis !== 'undefined' ? globalThis.__phloReact : null
44
+ if (!React) return 'React is not available for extension settings.'
45
+
46
+ const { useEffect, useState } = React
47
+ const el = React.createElement
48
+ const loadSettingsFn = settingsApi.loadSettings
49
+ const saveSettingsFn = settingsApi.saveSettings
50
+ if (!loadSettingsFn || !saveSettingsFn) {
51
+ return 'Settings API is not available for this extension.'
52
+ }
53
+ const [loading, setLoading] = useState(true)
54
+ const [message, setMessage] = useState('')
55
+ const [enabled, setEnabled] = useState(false)
56
+ const [status, setStatus] = useState(null)
57
+
58
+ useEffect(() => {
59
+ let active = true
60
+ loadSettingsFn()
61
+ .then((data) => {
62
+ if (!active) return
63
+ setEnabled(Boolean(data.enabled))
64
+ setMessage(typeof data.message === 'string' ? data.message : '')
65
+ setLoading(false)
66
+ })
67
+ .catch(() => {
68
+ if (!active) return
69
+ setStatus('Failed to load settings.')
70
+ setLoading(false)
71
+ })
72
+ return () => {
73
+ active = false
74
+ }
75
+ }, [loadSettingsFn])
76
+
77
+ const handleSave = async () => {
78
+ setStatus(null)
79
+ try {
80
+ await saveSettingsFn({ enabled, message })
81
+ setStatus('Saved.')
82
+ } catch {
83
+ setStatus('Save failed.')
84
+ }
85
+ }
86
+
87
+ if (loading) return 'Loading settings...'
88
+
89
+ return el(
90
+ 'div',
91
+ { className: 'space-y-3' },
92
+ el(
93
+ 'div',
94
+ { className: 'space-y-2' },
95
+ el(
96
+ 'label',
97
+ {
98
+ className: 'text-sm font-medium',
99
+ htmlFor: 'example-settings-message',
100
+ },
101
+ 'Message',
102
+ ),
103
+ el('input', {
104
+ id: 'example-settings-message',
105
+ className:
106
+ 'w-full rounded-md border border-input bg-background px-3 py-2 text-sm',
107
+ placeholder: 'Message',
108
+ value: message,
109
+ onChange: (event) => setMessage(event.target.value),
110
+ }),
111
+ ),
112
+ el(
113
+ 'label',
114
+ { className: 'flex items-center gap-2 text-sm' },
115
+ el('input', {
116
+ type: 'checkbox',
117
+ className: 'h-4 w-4',
118
+ checked: enabled,
119
+ onChange: (event) => setEnabled(event.target.checked),
120
+ }),
121
+ 'Enabled',
122
+ ),
123
+ el(
124
+ 'div',
125
+ { className: 'flex items-center gap-2' },
126
+ el(
127
+ 'button',
128
+ {
129
+ type: 'button',
130
+ className:
131
+ 'inline-flex items-center rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground shadow-sm',
132
+ onClick: handleSave,
133
+ },
134
+ 'Save',
135
+ ),
136
+ status
137
+ ? el('span', { className: 'text-sm text-muted-foreground' }, status)
138
+ : null,
139
+ ),
140
+ )
141
+ }
@@ -0,0 +1,81 @@
1
+ """Example Observatory extension plugin."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import resources
6
+ from importlib.abc import Traversable
7
+
8
+ from phlo.plugins import PluginMetadata
9
+ from phlo_observatory import ObservatoryExtensionPlugin
10
+ from phlo_observatory.manifest import (
11
+ ObservatoryExtensionCompatibility,
12
+ ObservatoryExtensionManifest,
13
+ ObservatoryExtensionNavItem,
14
+ ObservatoryExtensionRoute,
15
+ ObservatoryExtensionSettings,
16
+ ObservatoryExtensionSettingsPanel,
17
+ ObservatoryExtensionSlot,
18
+ ObservatoryExtensionUI,
19
+ )
20
+
21
+
22
+ class ExampleObservatoryExtension(ObservatoryExtensionPlugin):
23
+ """Example Observatory extension showing routes, slots, and settings panels."""
24
+
25
+ @property
26
+ def metadata(self) -> PluginMetadata:
27
+ return PluginMetadata(
28
+ name="example",
29
+ version="0.1.0",
30
+ description="Example Observatory UI extension",
31
+ )
32
+
33
+ @property
34
+ def manifest(self) -> ObservatoryExtensionManifest:
35
+ return ObservatoryExtensionManifest(
36
+ name="example",
37
+ version="0.1.0",
38
+ compat=ObservatoryExtensionCompatibility(observatory_min="0.1.0"),
39
+ settings=ObservatoryExtensionSettings(
40
+ settings_schema={
41
+ "type": "object",
42
+ "properties": {
43
+ "enabled": {"type": "boolean"},
44
+ "message": {"type": "string"},
45
+ },
46
+ },
47
+ defaults={"enabled": True, "message": "Hello from extension settings."},
48
+ scope="extension",
49
+ ),
50
+ ui=ObservatoryExtensionUI(
51
+ routes=[
52
+ ObservatoryExtensionRoute(
53
+ path="/extensions/example",
54
+ module="/example.js",
55
+ export="registerRoutes",
56
+ )
57
+ ],
58
+ nav=[ObservatoryExtensionNavItem(title="Example", to="/extensions/example")],
59
+ slots=[
60
+ ObservatoryExtensionSlot(
61
+ slot_id="dashboard.after-cards",
62
+ module="/example.js",
63
+ export="registerDashboardSlot",
64
+ ),
65
+ ObservatoryExtensionSlot(
66
+ slot_id="hub.after-stats",
67
+ module="/example.js",
68
+ export="registerHubSlot",
69
+ ),
70
+ ],
71
+ settings=[
72
+ ObservatoryExtensionSettingsPanel(
73
+ module="/example.js", export="registerSettings"
74
+ )
75
+ ],
76
+ ),
77
+ )
78
+
79
+ @property
80
+ def asset_root(self) -> Traversable:
81
+ return resources.files("phlo_observatory_example").joinpath("observatory_assets")