patholens 2.0.2__tar.gz → 2.0.4__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.
- patholens-2.0.4/PKG-INFO +88 -0
- patholens-2.0.4/README.md +67 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-backend/app/main.py +32 -1
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/dashboard/page.tsx +116 -1
- {patholens-2.0.2 → patholens-2.0.4}/patholens_cli/__init__.py +1 -1
- {patholens-2.0.2 → patholens-2.0.4}/patholens_cli/main.py +6 -15
- {patholens-2.0.2 → patholens-2.0.4}/pyproject.toml +2 -2
- patholens-2.0.2/PKG-INFO +0 -40
- patholens-2.0.2/README.md +0 -19
- {patholens-2.0.2 → patholens-2.0.4}/.gitignore +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-backend/app/database.py +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-backend/app/models.py +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-backend/requirements.txt +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/.gitignore +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/OncoGemma-logo-no-bg_low.png +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/OncoGemma-no-bg-high_gemini.png +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/README.md +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/favicon.ico +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/globals.css +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/layout.tsx +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/lib/auth.ts +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/lib/config.ts +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/login/page.tsx +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/page.tsx +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/viewer/page.tsx +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/components/AIResults.tsx +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/components/ClinicalChat.tsx +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/components/OncoLLM.tsx +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/components/WSIViewer.tsx +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/eslint.config.mjs +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/next-env.d.ts +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/next.config.ts +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/package-lock.json +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/package.json +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/postcss.config.mjs +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/file.svg +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/globe.svg +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/hawkfranklin_logo.png +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/next.svg +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/onco-gemma-logo.png +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/oncogemma_full_high_crop.png +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/oncogemma_logo_only.png +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/oncogemma_text_only.png +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/pathogemma_icon.png +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/vercel.svg +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/window.svg +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/tsconfig.json +0 -0
- {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/types.d.ts +0 -0
patholens-2.0.4/PKG-INFO
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: patholens
|
|
3
|
+
Version: 2.0.4
|
|
4
|
+
Summary: A command-line tool to manage, deploy, and launch PathoLens with Google Colab or local backends.
|
|
5
|
+
Requires-Python: >=3.13
|
|
6
|
+
Requires-Dist: click>=8.0.0
|
|
7
|
+
Requires-Dist: fastapi>=0.110.0
|
|
8
|
+
Requires-Dist: google-colab-cli>=0.1.0
|
|
9
|
+
Requires-Dist: httpx>=0.24.0
|
|
10
|
+
Requires-Dist: numpy>=1.24.0
|
|
11
|
+
Requires-Dist: openslide-bin>=4.0.1.1
|
|
12
|
+
Requires-Dist: openslide-python>=1.3.0
|
|
13
|
+
Requires-Dist: pillow>=10.0.0
|
|
14
|
+
Requires-Dist: pydantic>=2.0.0
|
|
15
|
+
Requires-Dist: pyjwt[crypto]>=2.8.0
|
|
16
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
17
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
18
|
+
Requires-Dist: uvicorn[standard]>=0.20.0
|
|
19
|
+
Requires-Dist: websockets>=12.0
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# 🔬 PathoLens Digital Pathology Workstation
|
|
23
|
+
|
|
24
|
+
PathoLens is a digital pathology workstation & AI workspace with a command-line launcher supporting **Local Machine** and **Google Colab Cloud VM** execution backends.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 🚀 Key Features
|
|
29
|
+
|
|
30
|
+
* **Dual Deployment Target:** Run completely locally on your hardware, or spin up a free Google Colab GPU-accelerated cloud VM with one command.
|
|
31
|
+
* **Zero-friction Colab Deploy (Zero Code Uploads):** The CLI uses PyPI directly. Instead of slow zip uploads of the codebase, it initiates a PyPI installation (`uv pip install`) over Google's datacenter backbone (under 5 seconds) and only uploads the locally compiled static Next.js frontend (~6MB `static.zip`).
|
|
32
|
+
* **Dynamic Slide Directory Configuration:** Switch slide folders dynamically inside the browser UI Settings modal without having to restart the backend. Point it to local directories (e.g. `/home/prime/slides`) or Google Drive paths (`/content/drive/MyDrive/...`).
|
|
33
|
+
* **Edge AI Diagnostics:** Supports local execution of the `gemma-4-E2B-it-litert-lm` LiteRT model for local digital pathology clinical questions and diagnostics.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 📦 Installation
|
|
38
|
+
|
|
39
|
+
To install the CLI globally using `uv`:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv tool install patholens
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🛠️ Usage
|
|
48
|
+
|
|
49
|
+
Simply run:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
patholens launch
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This launches the onboarding wizard in your terminal:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
█▀█ █▀█ ▀█▀ █▄█ █▀█ █ █▀▀ █▄ █ █▀
|
|
59
|
+
█▀▀ █▀█ █ █ █ █▄█ █▄▄ ██▄ █ ▀█ ▄█ cli v2.0.2
|
|
60
|
+
🔬 Digital Pathology Workstation & AI Workspace
|
|
61
|
+
|
|
62
|
+
} Environment: Cloud-Scalable / Multi-Tenant Ready
|
|
63
|
+
} Local Fallback: SQLite + Local Slide Storage
|
|
64
|
+
|
|
65
|
+
? Where would you like to run the PathoLens backend?
|
|
66
|
+
1) Local Machine (Your computer's CPU/GPU)
|
|
67
|
+
2) Google Colab VM (Free cloud CPU/GPU accelerators)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Option 1: Local Machine
|
|
71
|
+
Runs a local FastAPI backend and serves the Next.js frontend statically.
|
|
72
|
+
* **Local AI option:** You can choose to download the `litert_lm` package and cache the `gemma-4-E2B-it-litert-lm` model for local inference.
|
|
73
|
+
* **Launch link:** The CLI automatically opens your default browser at `http://localhost:8000`.
|
|
74
|
+
|
|
75
|
+
### Option 2: Google Colab VM
|
|
76
|
+
* **Drive Integration:** Prompts you to mount your Google Drive on the VM.
|
|
77
|
+
* **Fast Setup:** Installs `patholens` from PyPI, uploads and extracts the frontend `static.zip`, installs slide bindings (`libopenslide0`), and opens a Cloudflare secure tunnel.
|
|
78
|
+
* **Direct Browser Launch:** The CLI automatically grabs the public `*.trycloudflare.com` tunnel URL and opens it in your web browser.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## ⚙️ Slide Directory Configuration
|
|
83
|
+
|
|
84
|
+
You can configure the active slide folder inside the browser UI dashboard.
|
|
85
|
+
1. Click the **Settings** gear icon in the header.
|
|
86
|
+
2. Input the absolute path to your slide directory (e.g. `/content/drive/MyDrive/Slides` on Colab, or a local directory).
|
|
87
|
+
3. Click **Save Settings**. The backend updates its active path on the fly and immediately lists the `.svs` files found in the folder.
|
|
88
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# 🔬 PathoLens Digital Pathology Workstation
|
|
2
|
+
|
|
3
|
+
PathoLens is a digital pathology workstation & AI workspace with a command-line launcher supporting **Local Machine** and **Google Colab Cloud VM** execution backends.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🚀 Key Features
|
|
8
|
+
|
|
9
|
+
* **Dual Deployment Target:** Run completely locally on your hardware, or spin up a free Google Colab GPU-accelerated cloud VM with one command.
|
|
10
|
+
* **Zero-friction Colab Deploy (Zero Code Uploads):** The CLI uses PyPI directly. Instead of slow zip uploads of the codebase, it initiates a PyPI installation (`uv pip install`) over Google's datacenter backbone (under 5 seconds) and only uploads the locally compiled static Next.js frontend (~6MB `static.zip`).
|
|
11
|
+
* **Dynamic Slide Directory Configuration:** Switch slide folders dynamically inside the browser UI Settings modal without having to restart the backend. Point it to local directories (e.g. `/home/prime/slides`) or Google Drive paths (`/content/drive/MyDrive/...`).
|
|
12
|
+
* **Edge AI Diagnostics:** Supports local execution of the `gemma-4-E2B-it-litert-lm` LiteRT model for local digital pathology clinical questions and diagnostics.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 📦 Installation
|
|
17
|
+
|
|
18
|
+
To install the CLI globally using `uv`:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv tool install patholens
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🛠️ Usage
|
|
27
|
+
|
|
28
|
+
Simply run:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
patholens launch
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This launches the onboarding wizard in your terminal:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
█▀█ █▀█ ▀█▀ █▄█ █▀█ █ █▀▀ █▄ █ █▀
|
|
38
|
+
█▀▀ █▀█ █ █ █ █▄█ █▄▄ ██▄ █ ▀█ ▄█ cli v2.0.2
|
|
39
|
+
🔬 Digital Pathology Workstation & AI Workspace
|
|
40
|
+
|
|
41
|
+
} Environment: Cloud-Scalable / Multi-Tenant Ready
|
|
42
|
+
} Local Fallback: SQLite + Local Slide Storage
|
|
43
|
+
|
|
44
|
+
? Where would you like to run the PathoLens backend?
|
|
45
|
+
1) Local Machine (Your computer's CPU/GPU)
|
|
46
|
+
2) Google Colab VM (Free cloud CPU/GPU accelerators)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Option 1: Local Machine
|
|
50
|
+
Runs a local FastAPI backend and serves the Next.js frontend statically.
|
|
51
|
+
* **Local AI option:** You can choose to download the `litert_lm` package and cache the `gemma-4-E2B-it-litert-lm` model for local inference.
|
|
52
|
+
* **Launch link:** The CLI automatically opens your default browser at `http://localhost:8000`.
|
|
53
|
+
|
|
54
|
+
### Option 2: Google Colab VM
|
|
55
|
+
* **Drive Integration:** Prompts you to mount your Google Drive on the VM.
|
|
56
|
+
* **Fast Setup:** Installs `patholens` from PyPI, uploads and extracts the frontend `static.zip`, installs slide bindings (`libopenslide0`), and opens a Cloudflare secure tunnel.
|
|
57
|
+
* **Direct Browser Launch:** The CLI automatically grabs the public `*.trycloudflare.com` tunnel URL and opens it in your web browser.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## ⚙️ Slide Directory Configuration
|
|
62
|
+
|
|
63
|
+
You can configure the active slide folder inside the browser UI dashboard.
|
|
64
|
+
1. Click the **Settings** gear icon in the header.
|
|
65
|
+
2. Input the absolute path to your slide directory (e.g. `/content/drive/MyDrive/Slides` on Colab, or a local directory).
|
|
66
|
+
3. Click **Save Settings**. The backend updates its active path on the fly and immediately lists the `.svs` files found in the folder.
|
|
67
|
+
|
|
@@ -468,6 +468,34 @@ async def get_toxicity(current_user: dict = Depends(get_current_user)):
|
|
|
468
468
|
}
|
|
469
469
|
]
|
|
470
470
|
|
|
471
|
+
class SlidePathRequest(pydantic.BaseModel):
|
|
472
|
+
path: str
|
|
473
|
+
|
|
474
|
+
@app.post("/api/settings/slide_path")
|
|
475
|
+
async def update_slide_path(request: SlidePathRequest, current_user: dict = Depends(get_current_user)):
|
|
476
|
+
global SLIDE_DIR
|
|
477
|
+
target_path = os.path.abspath(request.path)
|
|
478
|
+
if not os.path.exists(target_path):
|
|
479
|
+
raise HTTPException(status_code=400, detail=f"Directory path does not exist: {target_path}")
|
|
480
|
+
|
|
481
|
+
SLIDE_DIR = target_path
|
|
482
|
+
logger.info("Slide directory dynamically changed to: %s", SLIDE_DIR)
|
|
483
|
+
|
|
484
|
+
# Return updated slide list
|
|
485
|
+
slides = []
|
|
486
|
+
if os.path.exists(SLIDE_DIR):
|
|
487
|
+
slides = [f for f in os.listdir(SLIDE_DIR) if f.endswith('.svs')]
|
|
488
|
+
return {
|
|
489
|
+
"status": "success",
|
|
490
|
+
"slide_dir": SLIDE_DIR,
|
|
491
|
+
"slides": slides
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
@app.get("/api/settings/slide_path")
|
|
495
|
+
async def get_slide_path(current_user: dict = Depends(get_current_user)):
|
|
496
|
+
return {"slide_dir": SLIDE_DIR}
|
|
497
|
+
|
|
498
|
+
|
|
471
499
|
class OncoLLMQuery(pydantic.BaseModel):
|
|
472
500
|
query: str
|
|
473
501
|
context: Optional[dict] = None
|
|
@@ -534,8 +562,11 @@ async def serve_frontend(full_path: str):
|
|
|
534
562
|
if os.path.isfile(candidate):
|
|
535
563
|
return FileResponse(candidate)
|
|
536
564
|
|
|
565
|
+
html_candidate = os.path.join(STATIC_FRONTEND_DIR, f"{full_path}.html")
|
|
566
|
+
if full_path and os.path.isfile(html_candidate):
|
|
567
|
+
return FileResponse(html_candidate)
|
|
568
|
+
|
|
537
569
|
if STATIC_INDEX and os.path.isfile(STATIC_INDEX):
|
|
538
570
|
return FileResponse(STATIC_INDEX)
|
|
539
571
|
|
|
540
572
|
raise HTTPException(status_code=404, detail="Not Found")
|
|
541
|
-
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useEffect, useState } from 'react';
|
|
4
|
-
import { Search, AlertCircle, RefreshCw, Clock, FileText, CheckCircle, LogOut } from 'lucide-react';
|
|
4
|
+
import { Search, AlertCircle, RefreshCw, Clock, FileText, CheckCircle, LogOut, Settings } from 'lucide-react';
|
|
5
5
|
import Link from 'next/link';
|
|
6
6
|
import { useRouter } from 'next/navigation';
|
|
7
7
|
import Image from 'next/image';
|
|
@@ -14,6 +14,53 @@ export default function Dashboard() {
|
|
|
14
14
|
const [loading, setLoading] = useState(true);
|
|
15
15
|
const [error, setError] = useState<string | null>(null);
|
|
16
16
|
|
|
17
|
+
// Dynamic Slide Storage Configuration
|
|
18
|
+
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
19
|
+
const [newSlidePath, setNewSlidePath] = useState('');
|
|
20
|
+
const [currentSlidePath, setCurrentSlidePath] = useState('');
|
|
21
|
+
|
|
22
|
+
const fetchSettings = async () => {
|
|
23
|
+
try {
|
|
24
|
+
const res = await fetch(`${config.apiBaseUrl}/api/settings/slide_path`, {
|
|
25
|
+
headers: {
|
|
26
|
+
'Authorization': `Bearer ${authHelper.getToken()}`
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
if (res.ok) {
|
|
30
|
+
const data = await res.json();
|
|
31
|
+
setCurrentSlidePath(data.slide_dir);
|
|
32
|
+
setNewSlidePath(data.slide_dir);
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error("Failed to fetch settings:", err);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const saveSlidePath = async () => {
|
|
40
|
+
try {
|
|
41
|
+
const res = await fetch(`${config.apiBaseUrl}/api/settings/slide_path`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
'Authorization': `Bearer ${authHelper.getToken()}`
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify({ path: newSlidePath })
|
|
48
|
+
});
|
|
49
|
+
if (res.ok) {
|
|
50
|
+
const data = await res.json();
|
|
51
|
+
setCurrentSlidePath(data.slide_dir);
|
|
52
|
+
setIsSettingsOpen(false);
|
|
53
|
+
fetchSlides();
|
|
54
|
+
} else {
|
|
55
|
+
const errData = await res.json();
|
|
56
|
+
alert(`Error: ${errData.detail || 'Failed to update slide path'}`);
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error("Failed to save slide path:", err);
|
|
60
|
+
alert("Connection error. Could not save settings.");
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
17
64
|
const handleLogout = () => {
|
|
18
65
|
authHelper.clearToken();
|
|
19
66
|
router.push('/login');
|
|
@@ -41,6 +88,7 @@ export default function Dashboard() {
|
|
|
41
88
|
|
|
42
89
|
useEffect(() => {
|
|
43
90
|
fetchSlides();
|
|
91
|
+
fetchSettings();
|
|
44
92
|
}, []);
|
|
45
93
|
|
|
46
94
|
// Mock data for patient insights
|
|
@@ -92,6 +140,13 @@ export default function Dashboard() {
|
|
|
92
140
|
<div className="w-10 h-10 bg-purple-600 rounded-full flex items-center justify-center text-white font-bold">
|
|
93
141
|
SC
|
|
94
142
|
</div>
|
|
143
|
+
<button
|
|
144
|
+
onClick={() => setIsSettingsOpen(true)}
|
|
145
|
+
className="p-2 text-gray-400 hover:text-blue-600 transition-colors"
|
|
146
|
+
title="Slide Folder Settings"
|
|
147
|
+
>
|
|
148
|
+
<Settings size={20} />
|
|
149
|
+
</button>
|
|
95
150
|
<button
|
|
96
151
|
onClick={handleLogout}
|
|
97
152
|
className="p-2 text-gray-400 hover:text-red-600 transition-colors"
|
|
@@ -240,6 +295,66 @@ export default function Dashboard() {
|
|
|
240
295
|
</div>
|
|
241
296
|
)}
|
|
242
297
|
</div>
|
|
298
|
+
|
|
299
|
+
{/* Slide Directory Settings Modal */}
|
|
300
|
+
{isSettingsOpen && (
|
|
301
|
+
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
|
302
|
+
<div className="bg-white rounded-2xl border border-gray-200 shadow-2xl w-full max-w-md overflow-hidden text-gray-900">
|
|
303
|
+
<div className="p-6 border-b border-gray-100 flex items-center justify-between bg-gray-50">
|
|
304
|
+
<h3 className="font-bold text-gray-800 flex items-center gap-2">
|
|
305
|
+
<Settings size={18} className="text-blue-600" />
|
|
306
|
+
Slide Storage Configuration
|
|
307
|
+
</h3>
|
|
308
|
+
<button
|
|
309
|
+
onClick={() => setIsSettingsOpen(false)}
|
|
310
|
+
className="text-gray-400 hover:text-gray-600 text-sm font-semibold"
|
|
311
|
+
>
|
|
312
|
+
Close
|
|
313
|
+
</button>
|
|
314
|
+
</div>
|
|
315
|
+
<div className="p-6 space-y-4">
|
|
316
|
+
<div>
|
|
317
|
+
<label className="block text-xs font-bold text-gray-500 uppercase tracking-wider mb-2">
|
|
318
|
+
Current Slides Folder
|
|
319
|
+
</label>
|
|
320
|
+
<div className="bg-gray-50 p-3 rounded-lg border border-gray-100 font-mono text-xs text-gray-700 select-all truncate" title={currentSlidePath}>
|
|
321
|
+
{currentSlidePath || 'Not configured'}
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
<div>
|
|
325
|
+
<label className="block text-xs font-bold text-gray-500 uppercase tracking-wider mb-2">
|
|
326
|
+
New Slide Directory Path
|
|
327
|
+
</label>
|
|
328
|
+
<input
|
|
329
|
+
type="text"
|
|
330
|
+
value={newSlidePath}
|
|
331
|
+
onChange={(e) => setNewSlidePath(e.target.value)}
|
|
332
|
+
placeholder="e.g. /home/prime/data/slides"
|
|
333
|
+
className="w-full px-3 py-2 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm text-gray-900 bg-white"
|
|
334
|
+
/>
|
|
335
|
+
<p className="text-[10px] text-gray-400 mt-1.5 leading-relaxed">
|
|
336
|
+
Enter the absolute directory path on the server/VM where SVS slides are located.
|
|
337
|
+
For Colab sessions, this is typically under <code className="bg-gray-100 px-1 py-0.5 rounded text-blue-600 font-mono">/content/drive/MyDrive/...</code>
|
|
338
|
+
</p>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
<div className="p-4 border-t border-gray-100 bg-gray-50 flex justify-end gap-2">
|
|
342
|
+
<button
|
|
343
|
+
onClick={() => setIsSettingsOpen(false)}
|
|
344
|
+
className="px-4 py-2 border border-gray-200 rounded-lg text-sm text-gray-600 hover:bg-gray-100 bg-white"
|
|
345
|
+
>
|
|
346
|
+
Cancel
|
|
347
|
+
</button>
|
|
348
|
+
<button
|
|
349
|
+
onClick={saveSlidePath}
|
|
350
|
+
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm font-semibold shadow-sm"
|
|
351
|
+
>
|
|
352
|
+
Save Settings
|
|
353
|
+
</button>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
)}
|
|
243
358
|
</div>
|
|
244
359
|
);
|
|
245
360
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# PathoLens CLI module
|
|
2
|
-
__version__ = "2.0.
|
|
2
|
+
__version__ = "2.0.4"
|
|
@@ -18,7 +18,7 @@ CONFIG_PATH = os.path.join(CONFIG_DIR, "config.json")
|
|
|
18
18
|
|
|
19
19
|
BANNER = """\033[95m
|
|
20
20
|
█▀█ █▀█ ▀█▀ █▄█ █▀█ █ █▀▀ █▄ █ █▀
|
|
21
|
-
█▀▀ █▀█ █ █ █ █▄█ █▄▄ ██▄ █ ▀█ ▄█\033[0m \033[93mcli v2.0.
|
|
21
|
+
█▀▀ █▀█ █ █ █ █▄█ █▄▄ ██▄ █ ▀█ ▄█\033[0m \033[93mcli v2.0.4\033[0m
|
|
22
22
|
\033[90m🔬 Digital Pathology Workstation & AI Workspace\033[0m
|
|
23
23
|
"""
|
|
24
24
|
|
|
@@ -272,7 +272,8 @@ def launch(colab, gpu, session, local, litert_model, litert_backend):
|
|
|
272
272
|
port=8000,
|
|
273
273
|
static_dir=os.path.join(project_root, "patholens-ui", "out"),
|
|
274
274
|
litert_model=litert_model,
|
|
275
|
-
litert_backend=litert_backend or "cpu"
|
|
275
|
+
litert_backend=litert_backend or "cpu",
|
|
276
|
+
sample_data_path=None
|
|
276
277
|
)
|
|
277
278
|
except KeyboardInterrupt:
|
|
278
279
|
click.echo("\n[patholens] Local server stopped.")
|
|
@@ -336,12 +337,8 @@ def launch(colab, gpu, session, local, litert_model, litert_backend):
|
|
|
336
337
|
click.echo("[patholens] Aborting launch: Frontend static export files missing.")
|
|
337
338
|
return
|
|
338
339
|
|
|
339
|
-
# Create temporary zip archive of
|
|
340
|
+
# Create temporary zip archive of static frontend
|
|
340
341
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
341
|
-
click.echo("[patholens] Packaging codebase for Colab installation (excluding local slide directories)...")
|
|
342
|
-
project_zip = os.path.join(temp_dir, "patholens.zip")
|
|
343
|
-
zip_project(project_root, project_zip)
|
|
344
|
-
|
|
345
342
|
click.echo("[patholens] Packaging static UI files...")
|
|
346
343
|
ui_zip = os.path.join(temp_dir, "static.zip")
|
|
347
344
|
zip_dir(os.path.join(project_root, "patholens-ui", "out"), ui_zip)
|
|
@@ -362,9 +359,6 @@ def launch(colab, gpu, session, local, litert_model, litert_backend):
|
|
|
362
359
|
time.sleep(10)
|
|
363
360
|
|
|
364
361
|
# Upload files
|
|
365
|
-
click.echo("[patholens] Uploading project codebase...")
|
|
366
|
-
subprocess.run(["colab", "upload", "-s", session, project_zip, "content/patholens.zip"], check=True)
|
|
367
|
-
|
|
368
362
|
click.echo("[patholens] Uploading static UI bundle...")
|
|
369
363
|
subprocess.run(["colab", "upload", "-s", session, ui_zip, "content/static.zip"], check=True)
|
|
370
364
|
|
|
@@ -383,9 +377,6 @@ import subprocess
|
|
|
383
377
|
import time
|
|
384
378
|
|
|
385
379
|
print("[colab-setup] Extracting files...")
|
|
386
|
-
with zipfile.ZipFile('/content/patholens.zip', 'r') as zip_ref:
|
|
387
|
-
zip_ref.extractall('/content')
|
|
388
|
-
|
|
389
380
|
with zipfile.ZipFile('/content/static.zip', 'r') as zip_ref:
|
|
390
381
|
zip_ref.extractall('/content/static')
|
|
391
382
|
|
|
@@ -395,8 +386,8 @@ os.system("apt-get update && apt-get install -y libopenslide0")
|
|
|
395
386
|
print("[colab-setup] Installing uv package manager...")
|
|
396
387
|
os.system("pip install uv")
|
|
397
388
|
|
|
398
|
-
print("[colab-setup] Installing patholens package via uv...")
|
|
399
|
-
os.system("uv pip install --system
|
|
389
|
+
print("[colab-setup] Installing patholens package via uv from PyPI...")
|
|
390
|
+
os.system("uv pip install --system patholens")
|
|
400
391
|
|
|
401
392
|
if "{litert_model or ''}":
|
|
402
393
|
print("[colab-setup] Installing litert-lm package...")
|
|
@@ -4,10 +4,10 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "patholens"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.4"
|
|
8
8
|
description = "A command-line tool to manage, deploy, and launch PathoLens with Google Colab or local backends."
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
11
|
dependencies = [
|
|
12
12
|
"click>=8.0.0",
|
|
13
13
|
"fastapi>=0.110.0",
|
patholens-2.0.2/PKG-INFO
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: patholens
|
|
3
|
-
Version: 2.0.2
|
|
4
|
-
Summary: A command-line tool to manage, deploy, and launch PathoLens with Google Colab or local backends.
|
|
5
|
-
Requires-Python: >=3.10
|
|
6
|
-
Requires-Dist: click>=8.0.0
|
|
7
|
-
Requires-Dist: fastapi>=0.110.0
|
|
8
|
-
Requires-Dist: google-colab-cli>=0.1.0
|
|
9
|
-
Requires-Dist: httpx>=0.24.0
|
|
10
|
-
Requires-Dist: numpy>=1.24.0
|
|
11
|
-
Requires-Dist: openslide-bin>=4.0.1.1
|
|
12
|
-
Requires-Dist: openslide-python>=1.3.0
|
|
13
|
-
Requires-Dist: pillow>=10.0.0
|
|
14
|
-
Requires-Dist: pydantic>=2.0.0
|
|
15
|
-
Requires-Dist: pyjwt[crypto]>=2.8.0
|
|
16
|
-
Requires-Dist: python-multipart>=0.0.9
|
|
17
|
-
Requires-Dist: sqlalchemy>=2.0.0
|
|
18
|
-
Requires-Dist: uvicorn[standard]>=0.20.0
|
|
19
|
-
Requires-Dist: websockets>=12.0
|
|
20
|
-
Description-Content-Type: text/markdown
|
|
21
|
-
|
|
22
|
-
# PathoLens
|
|
23
|
-
|
|
24
|
-
PathoLens is a digital pathology workstation with a CLI launcher for local or Google Colab-backed execution.
|
|
25
|
-
|
|
26
|
-
## Install
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
uv tool install patholens
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Usage
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
patholens launch
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
The launcher can run the backend locally or package and deploy the application to a Google Colab VM. Local launch builds the bundled Next.js UI if a static export is not already present.
|
|
39
|
-
|
|
40
|
-
For whole-slide image viewing, the backend uses OpenSlide.
|
patholens-2.0.2/README.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# PathoLens
|
|
2
|
-
|
|
3
|
-
PathoLens is a digital pathology workstation with a CLI launcher for local or Google Colab-backed execution.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
uv tool install patholens
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
patholens launch
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
The launcher can run the backend locally or package and deploy the application to a Google Colab VM. Local launch builds the bundled Next.js UI if a static export is not already present.
|
|
18
|
-
|
|
19
|
-
For whole-slide image viewing, the backend uses OpenSlide.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|