add S3 browser section to bib_explorer notebook
refs #94 New section 10 connects to the zotero S3 bucket via s3fs and lists files synced from Zotero through the WebDAV-to-S3 bridge. Shows both the bucket root and the zotero/zotero/ sync directory.
This commit is contained in:
@@ -716,6 +716,95 @@ def _(library_df, mo):
|
||||
return
|
||||
|
||||
|
||||
@app.cell(hide_code=True)
|
||||
def _(mo):
|
||||
mo.md("""
|
||||
## 10. Zotero WebDAV Storage (S3)
|
||||
|
||||
Browse files synced from Zotero to the WebDAV-backed S3 bucket.
|
||||
The `webdav` service (rclone) serves the `zotero` bucket in RustFS.
|
||||
""")
|
||||
return
|
||||
|
||||
|
||||
@app.cell(hide_code=True)
|
||||
def _(mo):
|
||||
import os
|
||||
import s3fs
|
||||
|
||||
_endpoint = os.environ.get("S3_ENDPOINT", "http://rustfs:9000")
|
||||
_key = os.environ.get("RUSTFS_ACCESS_KEY", "")
|
||||
_secret = os.environ.get("RUSTFS_SECRET_KEY", "")
|
||||
|
||||
fs = s3fs.S3FileSystem(
|
||||
key=_key,
|
||||
secret=_secret,
|
||||
endpoint_url=_endpoint,
|
||||
client_kwargs={"region_name": "us-east-1"},
|
||||
)
|
||||
|
||||
try:
|
||||
_files = fs.ls("zotero/", detail=True)
|
||||
_rows = [
|
||||
{
|
||||
"path": f["Key"].removeprefix("zotero/"),
|
||||
"size_kb": round(f.get("size", f.get("Size", 0)) / 1024, 1),
|
||||
"modified": str(f.get("LastModified", "")),
|
||||
}
|
||||
for f in _files
|
||||
if f.get("type", f.get("StorageClass", "")) != "directory"
|
||||
]
|
||||
except Exception as e:
|
||||
_rows = []
|
||||
mo.md(f"Could not list S3 bucket: `{e}`")
|
||||
|
||||
import polars as _pl
|
||||
|
||||
s3_df = _pl.DataFrame(_rows) if _rows else _pl.DataFrame()
|
||||
|
||||
if s3_df.height > 0:
|
||||
mo.md(f"**{s3_df.height}** files in `s3://zotero/`")
|
||||
else:
|
||||
mo.md("No files in `s3://zotero/` yet. Sync from Zotero to populate.")
|
||||
|
||||
return (fs, s3_df)
|
||||
|
||||
|
||||
@app.cell(hide_code=True)
|
||||
def _(mo, s3_df):
|
||||
mo.ui.table(s3_df, label="Zotero S3 Files") if s3_df.height > 0 else mo.md("")
|
||||
return
|
||||
|
||||
|
||||
@app.cell(hide_code=True)
|
||||
def _(fs, mo):
|
||||
_zotero_dir = []
|
||||
try:
|
||||
_zotero_dir = fs.ls("zotero/zotero/", detail=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_sync_rows = [
|
||||
{
|
||||
"file": f["Key"].removeprefix("zotero/zotero/"),
|
||||
"size_kb": round(f.get("size", f.get("Size", 0)) / 1024, 1),
|
||||
}
|
||||
for f in _zotero_dir
|
||||
if f.get("type", f.get("StorageClass", "")) != "directory"
|
||||
]
|
||||
|
||||
import polars as _pl
|
||||
|
||||
_sync_df = _pl.DataFrame(_sync_rows) if _sync_rows else _pl.DataFrame()
|
||||
|
||||
if _sync_df.height > 0:
|
||||
mo.md(f"### Zotero Sync Directory\n\n**{_sync_df.height}** synced items")
|
||||
mo.ui.table(_sync_df, label="Synced Files")
|
||||
else:
|
||||
mo.md("No synced files in `zotero/zotero/` yet.")
|
||||
return
|
||||
|
||||
|
||||
@app.cell(hide_code=True)
|
||||
def _(mo):
|
||||
mo.md("""
|
||||
|
||||
Reference in New Issue
Block a user