dbxj 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 (198) hide show
  1. dbxj-0.1.0/.github/workflows/ci.yml +23 -0
  2. dbxj-0.1.0/.github/workflows/release.yml +17 -0
  3. dbxj-0.1.0/.gitignore +180 -0
  4. dbxj-0.1.0/.justfile +29 -0
  5. dbxj-0.1.0/.pre-commit-config.yaml +33 -0
  6. dbxj-0.1.0/CHANGELOG.md +12 -0
  7. dbxj-0.1.0/LICENSE +21 -0
  8. dbxj-0.1.0/PKG-INFO +101 -0
  9. dbxj-0.1.0/README.md +76 -0
  10. dbxj-0.1.0/docs/design.md +148 -0
  11. dbxj-0.1.0/docs/screenshot.svg +142 -0
  12. dbxj-0.1.0/docs/sessions.md +150 -0
  13. dbxj-0.1.0/pyproject.toml +57 -0
  14. dbxj-0.1.0/scripts/create_job_with_task.py +59 -0
  15. dbxj-0.1.0/scripts/create_jobs.py +19 -0
  16. dbxj-0.1.0/scripts/delete_jobs.py +15 -0
  17. dbxj-0.1.0/scripts/generate_sample_data.py +144 -0
  18. dbxj-0.1.0/scripts/job.py +11 -0
  19. dbxj-0.1.0/scripts/trigger_job_with_task.py +20 -0
  20. dbxj-0.1.0/src/dbxj/__init__.py +0 -0
  21. dbxj-0.1.0/src/dbxj/auth.py +131 -0
  22. dbxj-0.1.0/src/dbxj/body.py +530 -0
  23. dbxj-0.1.0/src/dbxj/dialogs.py +100 -0
  24. dbxj-0.1.0/src/dbxj/jobs.py +89 -0
  25. dbxj-0.1.0/src/dbxj/main.py +206 -0
  26. dbxj-0.1.0/src/dbxj/main.tcss +104 -0
  27. dbxj-0.1.0/src/dbxj/mock_workspace_client.py +203 -0
  28. dbxj-0.1.0/src/dbxj/sample_data/job_100000000000000.json +112 -0
  29. dbxj-0.1.0/src/dbxj/sample_data/job_100007919000000.json +112 -0
  30. dbxj-0.1.0/src/dbxj/sample_data/job_100015838000000.json +64 -0
  31. dbxj-0.1.0/src/dbxj/sample_data/job_100023757000000.json +90 -0
  32. dbxj-0.1.0/src/dbxj/sample_data/job_100031676000000.json +222 -0
  33. dbxj-0.1.0/src/dbxj/sample_data/job_100039595000000.json +24 -0
  34. dbxj-0.1.0/src/dbxj/sample_data/jobs.json +44 -0
  35. dbxj-0.1.0/src/dbxj/sample_data/run_500000000000000.json +55 -0
  36. dbxj-0.1.0/src/dbxj/sample_data/run_500000006363900.json +55 -0
  37. dbxj-0.1.0/src/dbxj/sample_data/run_500000007613857.json +55 -0
  38. dbxj-0.1.0/src/dbxj/sample_data/run_500000010463046.json +55 -0
  39. dbxj-0.1.0/src/dbxj/sample_data/run_500000012320447.json +55 -0
  40. dbxj-0.1.0/src/dbxj/sample_data/run_500000021057221.json +55 -0
  41. dbxj-0.1.0/src/dbxj/sample_data/run_500000022726564.json +55 -0
  42. dbxj-0.1.0/src/dbxj/sample_data/run_500000029348400.json +55 -0
  43. dbxj-0.1.0/src/dbxj/sample_data/run_500000033531502.json +55 -0
  44. dbxj-0.1.0/src/dbxj/sample_data/run_500000035086732.json +55 -0
  45. dbxj-0.1.0/src/dbxj/sample_data/run_500000042336152.json +55 -0
  46. dbxj-0.1.0/src/dbxj/sample_data/run_500000044098755.json +55 -0
  47. dbxj-0.1.0/src/dbxj/sample_data/run_500000048300009.json +55 -0
  48. dbxj-0.1.0/src/dbxj/sample_data/run_500000050235776.json +55 -0
  49. dbxj-0.1.0/src/dbxj/sample_data/run_500000058538215.json +55 -0
  50. dbxj-0.1.0/src/dbxj/sample_data/run_500000065934619.json +55 -0
  51. dbxj-0.1.0/src/dbxj/sample_data/run_500000074305872.json +55 -0
  52. dbxj-0.1.0/src/dbxj/sample_data/run_500000081730156.json +55 -0
  53. dbxj-0.1.0/src/dbxj/sample_data/run_500000090522447.json +55 -0
  54. dbxj-0.1.0/src/dbxj/sample_data/run_500000093602030.json +55 -0
  55. dbxj-0.1.0/src/dbxj/sample_data/run_500000099538223.json +52 -0
  56. dbxj-0.1.0/src/dbxj/sample_data/run_500000102534750.json +55 -0
  57. dbxj-0.1.0/src/dbxj/sample_data/run_500000110308525.json +55 -0
  58. dbxj-0.1.0/src/dbxj/sample_data/run_500000111816980.json +55 -0
  59. dbxj-0.1.0/src/dbxj/sample_data/run_500000114356189.json +55 -0
  60. dbxj-0.1.0/src/dbxj/sample_data/run_500000116138533.json +55 -0
  61. dbxj-0.1.0/src/dbxj/sample_data/run_500000120413628.json +21 -0
  62. dbxj-0.1.0/src/dbxj/sample_data/run_500000121904221.json +21 -0
  63. dbxj-0.1.0/src/dbxj/sample_data/run_500000127804657.json +21 -0
  64. dbxj-0.1.0/src/dbxj/sample_data/run_500000135946235.json +21 -0
  65. dbxj-0.1.0/src/dbxj/sample_data/run_500000140333349.json +21 -0
  66. dbxj-0.1.0/src/dbxj/sample_data/run_500000142021075.json +21 -0
  67. dbxj-0.1.0/src/dbxj/sample_data/run_500000145023317.json +21 -0
  68. dbxj-0.1.0/src/dbxj/sample_data/run_500000148675603.json +21 -0
  69. dbxj-0.1.0/src/dbxj/sample_data/run_500000150290158.json +21 -0
  70. dbxj-0.1.0/src/dbxj/sample_data/run_500000159118905.json +21 -0
  71. dbxj-0.1.0/src/dbxj/sample_data/run_500000161442181.json +21 -0
  72. dbxj-0.1.0/src/dbxj/sample_data/run_500000168044623.json +21 -0
  73. dbxj-0.1.0/src/dbxj/sample_data/run_500000171263123.json +21 -0
  74. dbxj-0.1.0/src/dbxj/sample_data/run_500000174626665.json +21 -0
  75. dbxj-0.1.0/src/dbxj/sample_data/run_500000179866343.json +21 -0
  76. dbxj-0.1.0/src/dbxj/sample_data/run_500000187823419.json +21 -0
  77. dbxj-0.1.0/src/dbxj/sample_data/run_500000194167913.json +21 -0
  78. dbxj-0.1.0/src/dbxj/sample_data/run_500000199873140.json +21 -0
  79. dbxj-0.1.0/src/dbxj/sample_data/run_500000201175365.json +21 -0
  80. dbxj-0.1.0/src/dbxj/sample_data/run_500000204761223.json +21 -0
  81. dbxj-0.1.0/src/dbxj/sample_data/run_500000211355865.json +21 -0
  82. dbxj-0.1.0/src/dbxj/sample_data/run_500000218642938.json +21 -0
  83. dbxj-0.1.0/src/dbxj/sample_data/run_500000219850861.json +21 -0
  84. dbxj-0.1.0/src/dbxj/sample_data/run_500000222186309.json +21 -0
  85. dbxj-0.1.0/src/dbxj/sample_data/run_500000224860447.json +21 -0
  86. dbxj-0.1.0/src/dbxj/sample_data/run_500000226058958.json +21 -0
  87. dbxj-0.1.0/src/dbxj/sample_data/run_500000234309676.json +21 -0
  88. dbxj-0.1.0/src/dbxj/sample_data/run_500000241009887.json +21 -0
  89. dbxj-0.1.0/src/dbxj/sample_data/run_500000243507822.json +55 -0
  90. dbxj-0.1.0/src/dbxj/sample_data/run_500000248153207.json +55 -0
  91. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000000000001.json +3 -0
  92. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000000000002.json +3 -0
  93. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000000000003.json +3 -0
  94. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000006363901.json +3 -0
  95. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000006363902.json +3 -0
  96. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000006363903.json +3 -0
  97. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000007613858.json +3 -0
  98. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000007613859.json +3 -0
  99. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000007613860.json +3 -0
  100. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000010463047.json +3 -0
  101. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000010463048.json +3 -0
  102. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000010463049.json +3 -0
  103. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000012320448.json +3 -0
  104. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000012320449.json +3 -0
  105. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000012320450.json +3 -0
  106. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000021057222.json +3 -0
  107. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000021057223.json +3 -0
  108. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000021057224.json +3 -0
  109. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000022726565.json +3 -0
  110. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000022726566.json +3 -0
  111. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000022726567.json +3 -0
  112. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000029348401.json +3 -0
  113. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000029348402.json +3 -0
  114. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000029348403.json +3 -0
  115. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000033531503.json +3 -0
  116. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000033531504.json +3 -0
  117. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000033531505.json +3 -0
  118. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000035086733.json +3 -0
  119. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000035086734.json +3 -0
  120. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000035086735.json +3 -0
  121. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000042336153.json +3 -0
  122. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000042336154.json +4 -0
  123. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000044098756.json +3 -0
  124. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000044098757.json +3 -0
  125. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000044098758.json +3 -0
  126. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000048300010.json +3 -0
  127. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000048300011.json +3 -0
  128. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000048300012.json +3 -0
  129. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000050235777.json +3 -0
  130. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000050235778.json +4 -0
  131. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000058538216.json +3 -0
  132. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000058538217.json +3 -0
  133. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000058538218.json +3 -0
  134. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000065934620.json +3 -0
  135. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000065934621.json +3 -0
  136. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000065934622.json +3 -0
  137. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000074305873.json +3 -0
  138. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000074305874.json +3 -0
  139. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000074305875.json +3 -0
  140. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000081730157.json +3 -0
  141. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000081730158.json +3 -0
  142. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000081730159.json +3 -0
  143. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000090522448.json +3 -0
  144. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000090522449.json +4 -0
  145. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000093602031.json +3 -0
  146. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000093602032.json +3 -0
  147. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000093602033.json +3 -0
  148. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000099538224.json +3 -0
  149. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000099538225.json +3 -0
  150. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000102534751.json +3 -0
  151. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000102534752.json +3 -0
  152. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000102534753.json +3 -0
  153. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000110308526.json +3 -0
  154. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000110308527.json +3 -0
  155. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000110308528.json +3 -0
  156. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000111816981.json +3 -0
  157. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000111816982.json +3 -0
  158. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000111816983.json +3 -0
  159. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000114356190.json +3 -0
  160. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000114356191.json +3 -0
  161. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000114356192.json +3 -0
  162. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000116138534.json +3 -0
  163. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000116138535.json +3 -0
  164. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000116138536.json +3 -0
  165. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000121904222.json +3 -0
  166. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000127804658.json +3 -0
  167. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000140333350.json +3 -0
  168. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000142021076.json +3 -0
  169. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000145023318.json +3 -0
  170. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000148675604.json +3 -0
  171. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000150290159.json +3 -0
  172. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000159118906.json +3 -0
  173. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000161442182.json +3 -0
  174. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000168044624.json +4 -0
  175. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000171263124.json +3 -0
  176. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000174626666.json +3 -0
  177. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000179866344.json +3 -0
  178. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000187823420.json +3 -0
  179. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000194167914.json +3 -0
  180. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000199873141.json +3 -0
  181. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000201175366.json +3 -0
  182. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000204761224.json +3 -0
  183. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000211355866.json +3 -0
  184. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000218642939.json +3 -0
  185. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000219850862.json +3 -0
  186. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000222186310.json +3 -0
  187. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000224860448.json +3 -0
  188. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000226058959.json +3 -0
  189. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000234309677.json +3 -0
  190. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000241009888.json +3 -0
  191. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000243507823.json +3 -0
  192. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000243507824.json +3 -0
  193. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000243507825.json +3 -0
  194. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000248153208.json +3 -0
  195. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000248153209.json +3 -0
  196. dbxj-0.1.0/src/dbxj/sample_data/run_output_500000248153210.json +3 -0
  197. dbxj-0.1.0/tests/test_smoke.py +61 -0
  198. dbxj-0.1.0/uv.lock +1524 -0
@@ -0,0 +1,23 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ check:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.11", "3.13"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v6
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - run: uv sync --locked
20
+ - run: uv run ruff check
21
+ - run: uv run ruff format --check
22
+ - run: uv run mypy src
23
+ - run: uv run pytest
@@ -0,0 +1,17 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write # PyPI trusted publishing
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v6
16
+ - run: uv build
17
+ - uses: pypa/gh-action-pypi-publish@release/v1
dbxj-0.1.0/.gitignore ADDED
@@ -0,0 +1,180 @@
1
+ ### Generated by gibo (https://github.com/simonwhitaker/gibo)
2
+ ### https://raw.github.com/github/gitignore/6eeebe6f49678aacd8311ce079842c971b3ebe96/Python.gitignore
3
+
4
+ # Byte-compiled / optimized / DLL files
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+
9
+ # C extensions
10
+ *.so
11
+
12
+ # Distribution / packaging
13
+ .Python
14
+ build/
15
+ develop-eggs/
16
+ dist/
17
+ downloads/
18
+ eggs/
19
+ .eggs/
20
+ lib/
21
+ lib64/
22
+ parts/
23
+ sdist/
24
+ var/
25
+ wheels/
26
+ share/python-wheels/
27
+ *.egg-info/
28
+ .installed.cfg
29
+ *.egg
30
+ MANIFEST
31
+
32
+ # PyInstaller
33
+ # Usually these files are written by a python script from a template
34
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
35
+ *.manifest
36
+ *.spec
37
+
38
+ # Installer logs
39
+ pip-log.txt
40
+ pip-delete-this-directory.txt
41
+
42
+ # Unit test / coverage reports
43
+ htmlcov/
44
+ .tox/
45
+ .nox/
46
+ .coverage
47
+ .coverage.*
48
+ .cache
49
+ nosetests.xml
50
+ coverage.xml
51
+ *.cover
52
+ *.py,cover
53
+ .hypothesis/
54
+ .pytest_cache/
55
+ cover/
56
+
57
+ # Translations
58
+ *.mo
59
+ *.pot
60
+
61
+ # Django stuff:
62
+ *.log
63
+ local_settings.py
64
+ db.sqlite3
65
+ db.sqlite3-journal
66
+
67
+ # Flask stuff:
68
+ instance/
69
+ .webassets-cache
70
+
71
+ # Scrapy stuff:
72
+ .scrapy
73
+
74
+ # Sphinx documentation
75
+ docs/_build/
76
+
77
+ # PyBuilder
78
+ .pybuilder/
79
+ target/
80
+
81
+ # Jupyter Notebook
82
+ .ipynb_checkpoints
83
+
84
+ # IPython
85
+ profile_default/
86
+ ipython_config.py
87
+
88
+ # pyenv
89
+ # For a library or package, you might want to ignore these files since the code is
90
+ # intended to run in multiple environments; otherwise, check them in:
91
+ # .python-version
92
+
93
+ # pipenv
94
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
96
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
97
+ # install all needed dependencies.
98
+ #Pipfile.lock
99
+
100
+ # UV
101
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
102
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
103
+ # commonly ignored for libraries.
104
+ #uv.lock
105
+
106
+ # poetry
107
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
108
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
109
+ # commonly ignored for libraries.
110
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
111
+ #poetry.lock
112
+
113
+ # pdm
114
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
115
+ #pdm.lock
116
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
117
+ # in version control.
118
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
119
+ .pdm.toml
120
+ .pdm-python
121
+ .pdm-build/
122
+
123
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
124
+ __pypackages__/
125
+
126
+ # Celery stuff
127
+ celerybeat-schedule
128
+ celerybeat.pid
129
+
130
+ # SageMath parsed files
131
+ *.sage.py
132
+
133
+ # Environments
134
+ .env
135
+ .venv
136
+ env/
137
+ venv/
138
+ ENV/
139
+ env.bak/
140
+ venv.bak/
141
+
142
+ # Spyder project settings
143
+ .spyderproject
144
+ .spyproject
145
+
146
+ # Rope project settings
147
+ .ropeproject
148
+
149
+ # mkdocs documentation
150
+ /site
151
+
152
+ # mypy
153
+ .mypy_cache/
154
+ .dmypy.json
155
+ dmypy.json
156
+
157
+ # Pyre type checker
158
+ .pyre/
159
+
160
+ # pytype static type analyzer
161
+ .pytype/
162
+
163
+ # Cython debug symbols
164
+ cython_debug/
165
+
166
+ # PyCharm
167
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
168
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
169
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
170
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
171
+ #.idea/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
175
+
176
+ # dev
177
+ tmp
178
+ .python-version
179
+ .DS_Store
180
+ cached
dbxj-0.1.0/.justfile ADDED
@@ -0,0 +1,29 @@
1
+ # just run <profile>, or just run for the default profile
2
+ run profile="":
3
+ uv run dbxj {{ if profile != "" { "--profile " + profile } else { "" } }}
4
+
5
+ run_offline:
6
+ uv run dbxj --offline
7
+
8
+ setup_python:
9
+ uv venv --python 3.11
10
+ uv sync
11
+
12
+ upgrade_and_sync:
13
+ uv upgrade
14
+ uv sync
15
+
16
+ sync: # adds all packages to .venv
17
+ uv sync
18
+
19
+ create_jobs:
20
+ uv run ./scripts/create_jobs.py --count 10
21
+
22
+ create_jobs_50:
23
+ uv run ./scripts/create_jobs.py --count 50
24
+
25
+ delete_jobs:
26
+ uv run ./scripts/delete_jobs.py
27
+
28
+ install:
29
+ uv tool install --reinstall .
@@ -0,0 +1,33 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v5.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-docstring-first
8
+ - id: debug-statements
9
+ - id: check-yaml
10
+ - id: check-ast
11
+ - id: check-json
12
+ - id: pretty-format-json
13
+ args: [--autofix]
14
+ - repo: https://github.com/astral-sh/ruff-pre-commit
15
+ # Ruff version.
16
+ rev: v0.14.0
17
+ hooks:
18
+ # Run the linter.
19
+ - id: ruff
20
+ args: [--fix]
21
+ # Run the formatter.
22
+ - id: ruff-format
23
+ - repo: local
24
+ hooks:
25
+ - id: mypy
26
+ name: mypy
27
+ entry: mypy
28
+ language: system
29
+ types: [python]
30
+ args:
31
+ - --install-types
32
+ - --non-interactive
33
+ exclude: 'scripts/.*'
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ First public release.
6
+
7
+ - Browse jobs, their runs, the tasks in a run and task output/error traces
8
+ - `Last 5` column with the results of each job's recent runs
9
+ - Filter jobs by name, trigger a job, open the selection in the browser
10
+ - Profile selection (`--profile` / `DATABRICKS_CONFIG_PROFILE`) and in-app re-login when the
11
+ session has expired
12
+ - `--offline` / `--demo` mode with bundled demo data
dbxj-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Travis Dent
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.
dbxj-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.5
2
+ Name: dbxj
3
+ Version: 0.1.0
4
+ Summary: A keyboard-driven terminal UI for browsing Databricks jobs, runs and task logs
5
+ Project-URL: Homepage, https://github.com/pugillum/dbxj
6
+ Project-URL: Issues, https://github.com/pugillum/dbxj/issues
7
+ Author: Travis Dent
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: cli,databricks,jobs,textual,tui
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: databricks-sdk>=0.41.0
23
+ Requires-Dist: textual>=1.0.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # dbxj
27
+
28
+ A keyboard-driven terminal UI for browsing Databricks jobs, their runs and task logs, in the
29
+ spirit of k9s and lazygit.
30
+
31
+ ![dbxj showing a list of jobs with their last 5 run results](https://raw.githubusercontent.com/pugillum/dbxj/main/docs/screenshot.svg)
32
+
33
+ - See every job with the results of its last 5 runs at a glance
34
+ - Drill into a job's runs, a run's tasks and a task's output or error trace
35
+ - Filter jobs by name, trigger a job, open anything in the browser
36
+ - Switch workspaces with `--profile`; re-login happens in-app when your session expires
37
+
38
+ # Install
39
+
40
+ ```sh
41
+ uv tool install dbxj # or: pipx install dbxj
42
+ ```
43
+
44
+ Requirements:
45
+
46
+ - Python 3.11+
47
+ - A profile in `~/.databrickscfg`, e.g. from `databricks auth login --host <workspace-url>`
48
+ - The [Databricks CLI](https://docs.databricks.com/dev-tools/cli/install.html) on your `PATH`, used
49
+ to log in again when your session has expired
50
+
51
+ Try it without a workspace:
52
+
53
+ ```sh
54
+ dbxj --demo
55
+ ```
56
+
57
+ Upgrade with `uv tool upgrade dbxj` and remove with `uv tool uninstall dbxj`.
58
+
59
+ # Usage
60
+
61
+ ```sh
62
+ dbxj --profile <profile name> # a profile from ~/.databrickscfg
63
+ dbxj --demo # bundled demo data, no workspace needed (alias: --offline)
64
+ ```
65
+
66
+ If `--profile` isn't given, `DATABRICKS_CONFIG_PROFILE` or the SDK's default auth is used. On start
67
+ the login is checked; if it's expired a dialog appears and `databricks auth login` opens your browser,
68
+ then the app continues once you've logged in.
69
+
70
+ | Key | Action |
71
+ | --- | --- |
72
+ | `r` | Refresh the current view |
73
+ | `q` | Quit |
74
+ | `?` | Show all key bindings (`esc` closes) |
75
+ | `/` | Filter jobs by name (kept while you drill in) |
76
+ | `t` | Trigger the selected job (asks to confirm) |
77
+ | `o` | Open the selected job, run or task in the browser |
78
+ | `enter` / `esc` | Drill into / back out of jobs, runs and tasks |
79
+ | `j` / `k`, `d` / `u` | Down / up, page down / up |
80
+
81
+ # Development
82
+
83
+ Requires [uv](https://docs.astral.sh/uv/) and optionally [just](https://github.com/casey/just).
84
+
85
+ ```sh
86
+ uv sync # create .venv
87
+ uv run pre-commit install
88
+ uv run dbxj --demo # or: just run_offline / just run <profile>
89
+ uv run pytest
90
+ ```
91
+
92
+ - In demo mode, data is read from `./cached` if that directory exists, otherwise from
93
+ `src/dbxj/sample_data` (regenerate with `uv run scripts/generate_sample_data.py`)
94
+ - `scripts/` has helpers to create, trigger and delete test jobs in a workspace
95
+ - `docs/design.md` describes how the app is put together
96
+
97
+ Releases are published to PyPI by pushing a `v*` tag.
98
+
99
+ # License
100
+
101
+ MIT
dbxj-0.1.0/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # dbxj
2
+
3
+ A keyboard-driven terminal UI for browsing Databricks jobs, their runs and task logs, in the
4
+ spirit of k9s and lazygit.
5
+
6
+ ![dbxj showing a list of jobs with their last 5 run results](https://raw.githubusercontent.com/pugillum/dbxj/main/docs/screenshot.svg)
7
+
8
+ - See every job with the results of its last 5 runs at a glance
9
+ - Drill into a job's runs, a run's tasks and a task's output or error trace
10
+ - Filter jobs by name, trigger a job, open anything in the browser
11
+ - Switch workspaces with `--profile`; re-login happens in-app when your session expires
12
+
13
+ # Install
14
+
15
+ ```sh
16
+ uv tool install dbxj # or: pipx install dbxj
17
+ ```
18
+
19
+ Requirements:
20
+
21
+ - Python 3.11+
22
+ - A profile in `~/.databrickscfg`, e.g. from `databricks auth login --host <workspace-url>`
23
+ - The [Databricks CLI](https://docs.databricks.com/dev-tools/cli/install.html) on your `PATH`, used
24
+ to log in again when your session has expired
25
+
26
+ Try it without a workspace:
27
+
28
+ ```sh
29
+ dbxj --demo
30
+ ```
31
+
32
+ Upgrade with `uv tool upgrade dbxj` and remove with `uv tool uninstall dbxj`.
33
+
34
+ # Usage
35
+
36
+ ```sh
37
+ dbxj --profile <profile name> # a profile from ~/.databrickscfg
38
+ dbxj --demo # bundled demo data, no workspace needed (alias: --offline)
39
+ ```
40
+
41
+ If `--profile` isn't given, `DATABRICKS_CONFIG_PROFILE` or the SDK's default auth is used. On start
42
+ the login is checked; if it's expired a dialog appears and `databricks auth login` opens your browser,
43
+ then the app continues once you've logged in.
44
+
45
+ | Key | Action |
46
+ | --- | --- |
47
+ | `r` | Refresh the current view |
48
+ | `q` | Quit |
49
+ | `?` | Show all key bindings (`esc` closes) |
50
+ | `/` | Filter jobs by name (kept while you drill in) |
51
+ | `t` | Trigger the selected job (asks to confirm) |
52
+ | `o` | Open the selected job, run or task in the browser |
53
+ | `enter` / `esc` | Drill into / back out of jobs, runs and tasks |
54
+ | `j` / `k`, `d` / `u` | Down / up, page down / up |
55
+
56
+ # Development
57
+
58
+ Requires [uv](https://docs.astral.sh/uv/) and optionally [just](https://github.com/casey/just).
59
+
60
+ ```sh
61
+ uv sync # create .venv
62
+ uv run pre-commit install
63
+ uv run dbxj --demo # or: just run_offline / just run <profile>
64
+ uv run pytest
65
+ ```
66
+
67
+ - In demo mode, data is read from `./cached` if that directory exists, otherwise from
68
+ `src/dbxj/sample_data` (regenerate with `uv run scripts/generate_sample_data.py`)
69
+ - `scripts/` has helpers to create, trigger and delete test jobs in a workspace
70
+ - `docs/design.md` describes how the app is put together
71
+
72
+ Releases are published to PyPI by pushing a `v*` tag.
73
+
74
+ # License
75
+
76
+ MIT
@@ -0,0 +1,148 @@
1
+ # Design
2
+
3
+ `dbxj` is a terminal UI for browsing Databricks jobs, their runs, the tasks in a run and task output.
4
+ It's built on [Textual](https://textual.textualize.io/) and the
5
+ [Databricks Python SDK](https://github.com/databricks/databricks-sdk-py).
6
+
7
+ ## Style: minimal TUI like lazygit and k9s
8
+
9
+ `dbxj` should look and feel like [lazygit](https://github.com/jesseduffield/lazygit) and
10
+ [k9s](https://k9scli.io/), not like a typical Textual app. Every UI change must follow these rules:
11
+
12
+ - **Keyboard only.** Every action is a single key (`t`, `o`, `r`, `/`, ...) with vim-style
13
+ navigation (`j`/`k`, `d`/`u`). There are no buttons, checkboxes or clickable widgets, and nothing
14
+ that needs the mouse.
15
+ - **Plain bordered panels.** Use `solid white` borders on the default background. Put titles and key
16
+ hints in the border (`border_title` / `border_subtitle`, e.g. `y confirm · n cancel`), not in extra
17
+ widgets. Don't add Textual's stock look: no themed dialog boxes, header bars or coloured
18
+ backgrounds.
19
+ - **Popups are overlays.** Dialogs, confirmations and help are a small centred box on a
20
+ `ModalScreen` over the current view (see `dialogs.py`). `esc` always closes them. `y`/`n` answers
21
+ confirmations. Textual's default dimming of the screen behind a modal is turned off
22
+ (`background: transparent`). The box itself is opaque (`background: $background`), so the table
23
+ doesn't show through.
24
+ - **Dense, text-first content.** Prefer tables and compact symbols (`✓ ✗ ● ⊘`) with sparing colour
25
+ (green/red/yellow/grey for state, yellow for keys). Don't use decorative padding or spacing.
26
+ - **One bottom bar.** `StatusBar` (`main.py`) holds the key hints on the left and a brief status
27
+ message on the right, like lazygit. The hints are only the essential keys (`r` refresh, `q` quit,
28
+ `?` help). Every other binding uses `show=False` and is listed in the `?` help overlay. The `^p`
29
+ command palette hint is hidden too, though `ctrl+p` still works.
30
+ - **Feedback is a status message, never an overlay.** `DatabricksTUI.notify` is overridden, so every
31
+ `notify()` call writes one line into the bottom bar instead of showing Textual's toast cards.
32
+ Messages are green / yellow / red for information / warning / error and clear themselves after
33
+ 4s, or 8s for errors. Use a popup only when the user has to make a decision.
34
+ - **No scrollbars.** A global `* { scrollbar-size: 0 0; }` in `main.tcss` hides them. In a narrow or
35
+ short window the content is clipped like in k9s and scrolled with keys (`j`/`k`, `d`/`u`, and
36
+ `h`/`l` or arrows to pan wide tables).
37
+ - **Restyle stock Textual widgets rather than accepting their defaults.** Check new widgets against
38
+ these rules. Textual's `DEFAULT_CSS` is overridden by `main.tcss`.
39
+
40
+ ## Principles
41
+
42
+ - **`q` / `esc` always work**, including while waiting on something slow (e.g. browser login).
43
+ - **Destructive or costly actions ask first**, e.g. `t` triggering a job shows a y/n popup.
44
+ - **Never block the UI thread**: API calls run in Textual workers (`@work(thread=True)` or
45
+ `asyncio.to_thread`).
46
+
47
+ ## Layout
48
+
49
+ ```
50
+ src/dbxj/
51
+ main.py entry point, arg parsing, App, MainScreen, SplashScreen, Header
52
+ body.py Body widget: table/filter/details panels and the navigation state machine
53
+ jobs.py JobsAPI: cached job list
54
+ auth.py profiles, connection check, ReauthScreen
55
+ dialogs.py HelpScreen (? overlay), ConfirmScreen (y/n popup)
56
+ mock_workspace_client.py offline client backed by ./cached/*.json or sample_data/*.json
57
+ sample_data/ fake workspace for --offline/--demo (scripts/generate_sample_data.py)
58
+ main.tcss styles
59
+ ```
60
+
61
+ ## Startup flow
62
+
63
+ 1. `main()` parses `--profile` (default `DATABRICKS_CONFIG_PROFILE`), `--offline` (alias `--demo`)
64
+ and `--version`. An unknown
65
+ profile exits with the list from `~/.databrickscfg` (or `DATABRICKS_CONFIG_FILE`).
66
+ 2. `DatabricksTUI` pushes `MainScreen`, then `SplashScreen` on top.
67
+ 3. `MainScreen.setup_client` (async worker) calls `auth.connect(profile)` in a thread. It builds a
68
+ `WorkspaceClient` and calls `current_user.me()` / `get_workspace_id()` to prove the login is valid.
69
+ 4. On `AuthError` it awaits `ReauthScreen` (see below). If login succeeds it loops back to step 3;
70
+ if the user quits, the app exits.
71
+ 5. `Header` and `Body` are initialised. When the first job list loads, `Body` posts
72
+ `JobsInitialised` and the app pops the splash screen.
73
+
74
+ Offline mode skips 3–4 and uses `get_mock_client()`. It reads `./cached` if that directory exists
75
+ (for local development against captured data), otherwise the fake workspace bundled in
76
+ `dbxj/sample_data`, so `dbxj --demo` works after a plain install. `tests/test_smoke.py` drives the
77
+ app against that data.
78
+
79
+ ## Re-authentication
80
+
81
+ All profiles are expected to use `auth_type = databricks-cli` (OAuth U2M), so re-auth shells out to
82
+ `databricks auth login --profile <p>`. The CLI opens the browser and blocks until the OAuth callback.
83
+
84
+ `ReauthScreen` (a `ModalScreen[bool]`):
85
+ - starts the login as soon as it mounts, with output captured so it can't corrupt the TUI
86
+ - `q` / `esc` terminates the login process and dismisses with `False`
87
+ - on failure it shows stderr; `enter` / `r` retries
88
+ - any exception from `connect` is treated as an auth failure; the error text is shown so network
89
+ problems can be told apart
90
+
91
+ ## Navigation state machine (`body.py`)
92
+
93
+ `BodyState`: `JOBS` → `JOB_RUNS` → `JOB_RUN` (tasks) → `TASK` (logs). `enter` drills in, `esc` goes
94
+ back. Each level clears the shared `DataTablePlus` and reloads it in a worker. `TASK` hides the table
95
+ and shows `DetailsPanel`, a scrollable view of the run output with ANSI colours.
96
+
97
+ `FilterState`: `NONE` → `TYPING` (`/` opens the filter input; the table filters live) → `SELECTING`
98
+ (`enter` moves focus back to the table and saves the text in `Body.job_filter`). Only available in
99
+ `JOBS`. Drilling into a job only hides the filter panel; `esc` back to `JOBS` shows it again and
100
+ reapplies `job_filter`. `esc` in `JOBS` clears the filter.
101
+
102
+ Tables key their rows by job / run id. Going back with `esc` puts the cursor back on the row that
103
+ was drilled into (`current_job_key`, `current_run_key`, `current_task_run_id`), via the loaders'
104
+ `select_key` argument.
105
+
106
+ ## Actions
107
+
108
+ Bound on `MainScreen` and delegated to `Body`:
109
+
110
+ - `r` → `Body.refresh_view`: reloads the current view (jobs with the cache cleared and the filter
111
+ kept, runs, tasks, or the task logs), keeping the cursor on the same row.
112
+ - `t` → `Body.trigger_job`: jobs view only. It shows a `ConfirmScreen`, then `jobs.run_now` in a
113
+ worker. It doesn't wait on the returned `Wait`; the new run id comes from `.bind()["run_id"]`. A
114
+ status message in the bottom bar reports the result, and the job's `Last 5` cell is refetched.
115
+ - `o` → `Body.open_in_browser` → `App.open_url`. Jobs use `{host}/jobs/{id}?o={workspace_id}`. Runs
116
+ and tasks use the API's `run_page_url`, with a fallback of `{host}/jobs/{job}/runs/{run}?o=...`.
117
+ - `?` → `HelpScreen`, a static key list in `dialogs.HELP_SECTIONS`. Most navigation keys are
118
+ handled in `Body.on_key` rather than as `Binding`s, so they can't be generated.
119
+
120
+ Single-letter screen bindings don't fire while typing in the filter, because `Input` consumes
121
+ printable keys.
122
+
123
+ ## Last 5 runs column
124
+
125
+ The jobs table has a `Last 5` column, ordered oldest → newest: `✓` success, `✗` failed, `●` running/
126
+ queued, `⊘` cancelled/skipped, `-` no run, `?` fetch failed, `…` loading.
127
+
128
+ The public Jobs API can't return recent runs for many jobs in one call. `jobs.list` has no runs
129
+ option and `jobs.list_runs` takes a single `job_id`. The portal uses an internal GraphQL route
130
+ (`/graphql/LakeflowListAssetsQuery`) that only accepts browser session cookies: bearer tokens get a
131
+ 303 to the login page (tested 2026-11-14), so we can't use it. Instead `Body.load_recent_runs` makes one
132
+ `list_runs(job_id, limit=5)` per job on an 8-thread pool. The table renders at once and cells fill in
133
+ as results arrive. The SDK iterator follows `next_page_token`, so `JobsAPI.get_recent_runs` caps the
134
+ results with `islice`. The column has a fixed width (`2 * RECENT_RUNS - 1`): `update_cell` doesn't resize a column, so
135
+ sizing it from the `…` placeholders cut cells off at 3 symbols. Results (including failures) are cached in `JobsAPI.recent_runs`, so filtering
136
+ doesn't refetch. `r` clears the cache.
137
+
138
+ ## Packaging
139
+
140
+ - The package and command are both `dbxj` (`[project.scripts] dbxj = "dbxj.main:main"`), installed
141
+ with `uv tool install .`.
142
+ - Runtime deps are only `databricks-sdk` and `textual`; tooling lives in the `dev` group.
143
+ - `just run [profile]`, `just run_offline`, `just install`.
144
+
145
+ ## Known gaps
146
+
147
+ - Offline mode needs a `./cached/` directory of JSON fixtures that isn't in the repo.
148
+ - Run/task lists are capped at 25 and not paginated.