ethoscopy 2.0.3__tar.gz → 2.0.5__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 (92) hide show
  1. ethoscopy-2.0.5/.claude/settings.local.json +40 -0
  2. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/.pre-commit-config.yaml +2 -2
  3. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/CLAUDE.md +35 -0
  4. ethoscopy-2.0.5/Docker/.env.dummy.example +25 -0
  5. ethoscopy-2.0.5/Docker/.env.github.example +39 -0
  6. ethoscopy-2.0.5/Docker/.env.google.example +41 -0
  7. ethoscopy-2.0.5/Docker/.env.keycloak.example +53 -0
  8. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/Docker/Dockerfile +8 -1
  9. ethoscopy-2.0.5/Docker/README.md +220 -0
  10. ethoscopy-2.0.5/Docker/README_AUTH.md +548 -0
  11. ethoscopy-2.0.5/Docker/config/jupyterhub_config.py +154 -0
  12. ethoscopy-2.0.5/Docker/config/users.py +47 -0
  13. ethoscopy-2.0.5/Docker/docker-compose.yml +57 -0
  14. {ethoscopy-2.0.3/Docker/config → ethoscopy-2.0.5/Docker/jupyterhub_data}/jupyterhub.sqlite +0 -0
  15. ethoscopy-2.0.5/Docker/jupyterhub_data/jupyterhub_cookie_secret +1 -0
  16. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/PKG-INFO +22 -1
  17. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/README.md +21 -0
  18. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/pyproject.toml +8 -1
  19. ethoscopy-2.0.5/scripts/README.md +121 -0
  20. ethoscopy-2.0.5/scripts/convert_databases.sh +51 -0
  21. ethoscopy-2.0.5/scripts/convert_wal_to_delete.py +240 -0
  22. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/__init__.py +3 -0
  23. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/load.py +93 -11
  24. ethoscopy-2.0.5/src/ethoscopy/misc/__init__.py +0 -0
  25. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/misc/get_HMM.py +8 -18
  26. ethoscopy-2.0.5/src/ethoscopy/misc/get_tutorials.py +220 -0
  27. ethoscopy-2.0.5/tasks/todo.md +90 -0
  28. ethoscopy-2.0.5/tests/test_get_tutorials.py +338 -0
  29. ethoscopy-2.0.5/tests/test_load_metadata_fixes.py +214 -0
  30. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tutorial_notebook/1_Overview_tutorial.ipynb +26 -0
  31. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tutorial_notebook/2_HMM_tutorial.ipynb +26 -0
  32. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tutorial_notebook/3_Circadian_tutorial.ipynb +28 -2
  33. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tutorial_notebook/5_Ethoscopy_catch22_tutorial.ipynb +26 -0
  34. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tutorial_notebook/6_Ethoscopy_to_hctsa_tutorial.ipynb +26 -0
  35. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tutorial_notebook/notebook_paper.ipynb +26 -0
  36. ethoscopy-2.0.3/.claude/settings.local.json +0 -25
  37. ethoscopy-2.0.3/Docker/.claude/settings.local.json +0 -19
  38. ethoscopy-2.0.3/Docker/.env +0 -39
  39. ethoscopy-2.0.3/Docker/README.md +0 -85
  40. ethoscopy-2.0.3/Docker/config/jupyterhub_cookie_secret +0 -1
  41. ethoscopy-2.0.3/Docker/docker-compose.yml +0 -25
  42. ethoscopy-2.0.3/Docker/jupyterhub_data/jupyterhub-proxy.pid +0 -1
  43. ethoscopy-2.0.3/Docker/jupyterhub_data/jupyterhub.sqlite +0 -0
  44. ethoscopy-2.0.3/Docker/jupyterhub_data/jupyterhub_cookie_secret +0 -1
  45. ethoscopy-2.0.3/QWEN.md +0 -88
  46. ethoscopy-2.0.3/src/ethoscopy/misc/get_tutorials.py +0 -48
  47. ethoscopy-2.0.3/tests/test_get_tutorials.py +0 -136
  48. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/.codecov.yml +0 -0
  49. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/.github/workflows/ci.yml +0 -0
  50. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/.github/workflows/release.yml +0 -0
  51. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/.gitignore +0 -0
  52. /ethoscopy-2.0.3/src/ethoscopy/misc/__init__.py → /ethoscopy-2.0.5/Docker/.env.gitlab.example +0 -0
  53. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/Docker/.env.keycloak +0 -0
  54. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/Docker/install_r_packages.r +0 -0
  55. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/Docker/jupyterhub_data/jupyterhub_config.py +0 -0
  56. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/LICENSE +0 -0
  57. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/TESTING.md +0 -0
  58. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/_config.yml +0 -0
  59. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/pytest.ini +0 -0
  60. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/run_tests.py +0 -0
  61. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/setup.py +0 -0
  62. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/analyse.py +0 -0
  63. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/behavpy.py +0 -0
  64. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/behavpy_HMM_class.py +0 -0
  65. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/behavpy_class.py +0 -0
  66. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/behavpy_core.py +0 -0
  67. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/behavpy_draw.py +0 -0
  68. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/behavpy_periodogram_class.py +0 -0
  69. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/behavpy_plotly.py +0 -0
  70. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/behavpy_seaborn.py +0 -0
  71. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/metadata_db.py +0 -0
  72. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/misc/circadian_bars.py +0 -0
  73. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/misc/general_functions.py +0 -0
  74. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/misc/hmm_functions.py +0 -0
  75. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/misc/periodogram_functions.py +0 -0
  76. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/src/ethoscopy/misc/validate_datetime.py +0 -0
  77. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/__init__.py +0 -0
  78. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/conftest.py +0 -0
  79. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/data/README.md +0 -0
  80. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/data/test_ethoscope.db +0 -0
  81. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/test_analyse.py +0 -0
  82. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/test_baseline_enhancements.py +0 -0
  83. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/test_behavpy.py +0 -0
  84. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/test_behavpy_core_simple.py +0 -0
  85. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/test_compatibility_classes.py +0 -0
  86. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/test_general_functions.py +0 -0
  87. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/test_load.py +0 -0
  88. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/test_load_comprehensive.py +0 -0
  89. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tests/test_load_optimizations.py +0 -0
  90. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tutorial_notebook/4_Navigating_db_tutorial.ipynb +0 -0
  91. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tutorial_notebook/ethoscope_db.csv +0 -0
  92. {ethoscopy-2.0.3 → ethoscopy-2.0.5}/tutorial_notebook/jones_et_al_metadata.csv +0 -0
@@ -0,0 +1,40 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(wc:*)",
5
+ "Bash(paplay:*)",
6
+ "Bash(ETHOSCOPE_DATA_PATH=/mnt/ethoscope_data docker compose:*)",
7
+ "Bash(docker ps:*)",
8
+ "Bash(docker logs:*)",
9
+ "Bash(docker compose:*)",
10
+ "Bash(curl:*)",
11
+ "Bash(docker exec:*)",
12
+ "Bash(mkdir -p /tmp/ethoscopy_wheel_inspect)",
13
+ "Read(//tmp/**)",
14
+ "Bash(pip download *)",
15
+ "Bash(unzip -l ethoscopy-2.0.4-py3-none-any.whl)",
16
+ "Bash(git check-ignore *)",
17
+ "Bash(/home/gg/Code/ethoscope_project/ethoscopy/.venv/bin/pip install *)",
18
+ "Bash(.venv/bin/python -m pytest tests/test_get_tutorials.py -v)",
19
+ "Bash(.venv/bin/python *)",
20
+ "Bash(.venv/bin/pip install *)",
21
+ "Bash(gh auth *)",
22
+ "Bash(gh repo *)",
23
+ "Bash(docker info *)",
24
+ "mcp__bookstack__bookstack_search",
25
+ "Bash(.venv/bin/ruff check *)",
26
+ "Bash(.venv/bin/black --check src/ tests/)",
27
+ "Bash(.venv/bin/black src/ethoscopy/misc/get_tutorials.py tests/test_get_tutorials.py)",
28
+ "mcp__bookstack__bookstack_pages_read",
29
+ "mcp__bookstack__bookstack_pages_update",
30
+ "Bash(gh issue create --repo gilestrolab/ethoscopy --title 'Tutorial data pickles missing from PyPI wheel \\(2.0.0 – 2.0.4\\)' --body ' *)",
31
+ "Bash(git add *)",
32
+ "Bash(git commit *)",
33
+ "Bash(git push *)",
34
+ "Bash(git tag *)",
35
+ "Bash(gh release create v2.0.5 --repo gilestrolab/ethoscopy --title 'v2.0.5 — package tutorial data separately' --notes ' *)",
36
+ "Bash(gh run *)",
37
+ "Bash(gh issue *)"
38
+ ]
39
+ }
40
+ }
@@ -51,7 +51,7 @@ repos:
51
51
  language: system
52
52
  pass_filenames: false
53
53
  always_run: true
54
- stages: [commit]
54
+ stages: [pre-commit]
55
55
 
56
56
  - id: pytest-fast
57
57
  name: pytest-fast
@@ -59,4 +59,4 @@ repos:
59
59
  language: system
60
60
  pass_filenames: false
61
61
  always_run: true
62
- stages: [push]
62
+ stages: [pre-push]
@@ -45,6 +45,41 @@ pip install -e ".[dev]"
45
45
  - Build: `JUPYTER_HUB_TAG=5.3.0 ETHOSCOPE_LAB_TAG=1.0 docker compose build`
46
46
  - Run: `docker compose up -d` (from Docker/ directory)
47
47
 
48
+ ### Troubleshooting: Database "Malformed" Errors
49
+
50
+ **Problem**: Intermittent "database disk image is malformed" errors when loading ethoscope data in Docker
51
+
52
+ **Root Cause**: SQLite databases in WAL (Write-Ahead Logging) mode on read-only Docker mounts
53
+
54
+ **Solution Options**:
55
+ 1. **Convert databases to DELETE mode** (recommended for immediate fix)
56
+ ```bash
57
+ # Using the conversion script (from project root)
58
+ python3 scripts/convert_wal_to_delete.py /mnt/ethoscope_data/results --verbose
59
+
60
+ # Or using bash wrapper
61
+ ./scripts/convert_databases.sh /mnt/ethoscope_data/results
62
+ ```
63
+
64
+ 2. **Improved connection handling** (v2.0.4+)
65
+ - The `_connect_db()` function in `load.py` now detects WAL mode automatically
66
+ - Uses `mode=ro&nolock=1` URI parameters for WAL databases on read-only mounts
67
+ - Includes retry logic in `read_single_roi_optimized()` for resilience
68
+
69
+ **Key Implementation Details** (load.py):
70
+ - Lines 18-70: `_connect_db()` - Smart connection with WAL detection and appropriate parameters
71
+ - Lines 943-979: Retry logic in `read_single_roi_optimized()` - Handles transient errors with fresh connections
72
+
73
+ **Testing After Changes**:
74
+ ```python
75
+ import ethoscopy as etho
76
+ metadata = etho.link_meta_index('metadata.csv', '/mnt/ethoscope_results')
77
+ data = etho.load_ethoscope(metadata, reference_hour=9.0, FUN=etho.sleep_annotation)
78
+ # Should load all ROIs without "malformed" errors
79
+ ```
80
+
81
+ **See Also**: `Docker/README.md` for detailed database preparation instructions
82
+
48
83
  ## Architecture and Code Structure
49
84
 
50
85
  ### Core Architecture
@@ -0,0 +1,25 @@
1
+ # ============================================================================
2
+ # DUMMY AUTHENTICATOR (Simple Shared Password)
3
+ # ============================================================================
4
+ # Default authentication method - all users share the same password
5
+ # Best for: Lab environments, small teams, trusted networks
6
+
7
+ # Authenticator type (must be 'dummy' for this configuration)
8
+ AUTHENTICATOR_CLASS=dummy
9
+
10
+ # Shared password for all users
11
+ DUMMY_PASSWORD=ethoscope
12
+
13
+ # Comma-separated list of allowed users
14
+ ALLOWED_USERS=amadabhushi,ggilestro,mjoyce,lguo,labguest1,labguest2,labguest3,labguest4,labguest5,labguest6,labguest7,labguest8,ethoscopelab
15
+
16
+ # Comma-separated list of admin users
17
+ ADMIN_USERS=ggilestro
18
+
19
+ # Docker user/group (optional, defaults to 1000:1000)
20
+ UID=1000
21
+ GID=1000
22
+
23
+ # JupyterHub and Ethoscope Lab versions
24
+ JUPYTER_HUB_TAG=5.3.0
25
+ ETHOSCOPE_LAB_TAG=1.1
@@ -0,0 +1,39 @@
1
+ # ============================================================================
2
+ # GITHUB OAUTH AUTHENTICATOR
3
+ # ============================================================================
4
+ # Authenticate users via their GitHub accounts
5
+ # Best for: Open source projects, teams already using GitHub
6
+ #
7
+ # Setup Instructions:
8
+ # 1. Go to GitHub Settings > Developer Settings > OAuth Apps
9
+ # 2. Create a new OAuth App
10
+ # 3. Set Homepage URL: http://your-server:8082
11
+ # 4. Set Authorization callback URL: http://your-server:8082/hub/oauth_callback
12
+ # 5. Copy the Client ID and generate a Client Secret
13
+
14
+ # Authenticator type
15
+ AUTHENTICATOR_CLASS=github
16
+
17
+ # OAuth credentials from GitHub OAuth App
18
+ OAUTH_CLIENT_ID=your_github_client_id_here
19
+ OAUTH_CLIENT_SECRET=your_github_client_secret_here
20
+
21
+ # Callback URL (must match what you set in GitHub OAuth App)
22
+ OAUTH_CALLBACK_URL=http://your-server:8082/hub/oauth_callback
23
+
24
+ # (Optional) Restrict to GitHub organization members
25
+ # GITHUB_ORG=your-organization-name
26
+
27
+ # Comma-separated list of allowed users (GitHub usernames)
28
+ ALLOWED_USERS=username1,username2,username3
29
+
30
+ # Comma-separated list of admin users (GitHub usernames)
31
+ ADMIN_USERS=your-github-username
32
+
33
+ # Docker user/group (optional, defaults to 1000:1000)
34
+ UID=1000
35
+ GID=1000
36
+
37
+ # JupyterHub and Ethoscope Lab versions
38
+ JUPYTER_HUB_TAG=5.3.0
39
+ ETHOSCOPE_LAB_TAG=1.1
@@ -0,0 +1,41 @@
1
+ # ============================================================================
2
+ # GOOGLE OAUTH AUTHENTICATOR
3
+ # ============================================================================
4
+ # Authenticate users via their Google accounts
5
+ # Best for: Organizations using Google Workspace, universities with Google
6
+ #
7
+ # Setup Instructions:
8
+ # 1. Go to Google Cloud Console (console.cloud.google.com)
9
+ # 2. Create a new project or select existing
10
+ # 3. Enable Google+ API
11
+ # 4. Go to Credentials > Create Credentials > OAuth 2.0 Client ID
12
+ # 5. Set Application type: Web application
13
+ # 6. Add Authorized redirect URI: http://your-server:8082/hub/oauth_callback
14
+ # 7. Copy the Client ID and Client Secret
15
+
16
+ # Authenticator type
17
+ AUTHENTICATOR_CLASS=google
18
+
19
+ # OAuth credentials from Google Cloud Console
20
+ OAUTH_CLIENT_ID=your_google_client_id.apps.googleusercontent.com
21
+ OAUTH_CLIENT_SECRET=your_google_client_secret_here
22
+
23
+ # Callback URL (must match what you set in Google Cloud Console)
24
+ OAUTH_CALLBACK_URL=http://your-server:8082/hub/oauth_callback
25
+
26
+ # (Optional) Restrict to Google Workspace domain
27
+ # GOOGLE_HOSTED_DOMAIN=yourdomain.com
28
+
29
+ # Comma-separated list of allowed users (Gmail addresses without @gmail.com or @yourdomain.com)
30
+ ALLOWED_USERS=user1,user2,user3
31
+
32
+ # Comma-separated list of admin users
33
+ ADMIN_USERS=your-email-username
34
+
35
+ # Docker user/group (optional, defaults to 1000:1000)
36
+ UID=1000
37
+ GID=1000
38
+
39
+ # JupyterHub and Ethoscope Lab versions
40
+ JUPYTER_HUB_TAG=5.3.0
41
+ ETHOSCOPE_LAB_TAG=1.1
@@ -0,0 +1,53 @@
1
+ # ============================================================================
2
+ # KEYCLOAK / GENERIC OIDC AUTHENTICATOR
3
+ # ============================================================================
4
+ # Authenticate users via Keycloak or any OIDC-compliant provider
5
+ # Best for: Organizations with existing SSO, enterprise environments
6
+ #
7
+ # Setup Instructions for Keycloak:
8
+ # 1. Log into your Keycloak Admin Console
9
+ # 2. Select your realm (e.g., 'gilestro-lab')
10
+ # 3. Go to Clients > Create Client
11
+ # 4. Set Client ID: jupyterhub
12
+ # 5. Set Client Protocol: openid-connect
13
+ # 6. Set Access Type: confidential
14
+ # 7. Set Valid Redirect URIs: http://your-server:8082/hub/oauth_callback
15
+ # 8. Save and go to Credentials tab to get the Client Secret
16
+ # 9. Create users in Keycloak matching your existing home directory names
17
+
18
+ # Authenticator type
19
+ AUTHENTICATOR_CLASS=generic
20
+
21
+ # OAuth credentials from Keycloak client
22
+ OAUTH_CLIENT_ID=jupyterhub
23
+ OAUTH_CLIENT_SECRET=your_keycloak_client_secret_here
24
+
25
+ # Callback URL
26
+ OAUTH_CALLBACK_URL=http://your-server:8082/hub/oauth_callback
27
+
28
+ # Keycloak OIDC endpoints (replace YOUR_KEYCLOAK_SERVER and YOUR_REALM)
29
+ # Example: http://keycloak.example.com/realms/gilestro-lab
30
+ OAUTH_AUTHORIZE_URL=http://YOUR_KEYCLOAK_SERVER/realms/YOUR_REALM/protocol/openid-connect/auth
31
+ OAUTH_TOKEN_URL=http://YOUR_KEYCLOAK_SERVER/realms/YOUR_REALM/protocol/openid-connect/token
32
+ OAUTH_USERDATA_URL=http://YOUR_KEYCLOAK_SERVER/realms/YOUR_REALM/protocol/openid-connect/userinfo
33
+
34
+ # Username claim (Keycloak uses 'preferred_username' by default)
35
+ OAUTH_USERNAME_CLAIM=preferred_username
36
+
37
+ # OAuth scopes (comma-separated, optional)
38
+ OAUTH_SCOPE=openid,profile,email
39
+
40
+ # CRITICAL: Keycloak usernames MUST match existing home directory names exactly
41
+ # Comma-separated list of allowed users
42
+ ALLOWED_USERS=amadabhushi,ggilestro,mjoyce,lguo,labguest1,labguest2,labguest3,labguest4,labguest5,labguest6,labguest7,labguest8,ethoscopelab
43
+
44
+ # Comma-separated list of admin users
45
+ ADMIN_USERS=ggilestro
46
+
47
+ # Docker user/group (optional, defaults to 1000:1000)
48
+ UID=1000
49
+ GID=1000
50
+
51
+ # JupyterHub and Ethoscope Lab versions
52
+ JUPYTER_HUB_TAG=5.3.0
53
+ ETHOSCOPE_LAB_TAG=1.1
@@ -70,16 +70,23 @@ RUN pip3 install jupyterlab-spreadsheet-editor
70
70
  # ============================================================================
71
71
 
72
72
  # Install Python data science packages
73
+ # Including oauthenticator for flexible authentication support
73
74
  RUN pip3 install \
75
+ oauthenticator \
74
76
  pandas \
75
77
  seaborn \
76
78
  bokeh \
77
79
  lifelines \
78
80
  opencv-python \
79
81
  mysql-connector \
80
- ethoscopy
82
+ ethoscopy
81
83
  # pycatch22
82
84
 
85
+ # Pre-populate tutorial datasets inside the installed package so non-root
86
+ # JupyterHub users don't have to download them on first notebook run. The
87
+ # pickles (~36 MB total) live in the ethoscopy repo, not the PyPI wheel.
88
+ RUN python3 -c "from ethoscopy.misc.get_tutorials import download_tutorial_data, package_tutorial_data_dir; download_tutorial_data(dest_dir=package_tutorial_data_dir(), verbose=True)"
89
+
83
90
  # Install Ethoscope from source
84
91
  RUN git clone https://github.com/gilestrolab/ethoscope.git /opt/ethoscope && \
85
92
  cd /opt/ethoscope/ && git checkout dev && \
@@ -0,0 +1,220 @@
1
+ # Ethoscopelab docker instance
2
+
3
+ Note: Most users will **not** need to recreate this image. These instructions are just provided as reference.
4
+ The ethoscopelab docker instance lives on dockerhub at the following address: [https://hub.docker.com/r/ggilestro/ethoscope-lab](https://hub.docker.com/r/ggilestro/ethoscope-lab) and this is what regular users should download and run. Follow instructions there and on the [ethoscopy manual](https://bookstack.lab.gilest.ro/books/ethoscopy/page/getting-started).
5
+
6
+
7
+ ## Docker files that were used to create the ethoscope-lab docker instance
8
+
9
+ The files in this folder can be used to recreate the image as uploaded on dockerhub.
10
+
11
+ The command to use to recreate that image is `JUPYTER_HUB_TAG=5.3.0 ETHOSCOPE_LAB_TAG=1.0 docker compose build`. This creates the image with the specified tag. For Docker Hub deployment, push the image: `docker push ggilestro/ethoscope-lab:1.0`. To also create a latest tag: `docker tag ggilestro/ethoscope-lab:1.0 ggilestro/ethoscope-lab:latest && docker push ggilestro/ethoscope-lab:latest`. You can verify your local images with `docker images | grep ethoscope-lab`.
12
+
13
+ After creation, the image can be run using the enclosed `docker-compose.yml` file, replacing values as fit.
14
+
15
+ ## Authentication and User Management
16
+
17
+ This JupyterHub instance supports multiple authentication methods including:
18
+ - **Keycloak OAuth** (current production setup at auth.gilest.ro)
19
+ - GitHub OAuth
20
+ - Google OAuth
21
+ - GitLab OAuth
22
+ - Dummy authenticator (shared password)
23
+
24
+ ### Adding New Users
25
+
26
+ **⚠️ Important**: Adding a new user requires steps on BOTH the authentication provider AND the JupyterHub host.
27
+
28
+ For detailed instructions on adding users, making users admins, and switching authentication methods, see:
29
+
30
+ **📖 [README_AUTH.md](./README_AUTH.md) - Complete Authentication and User Management Guide**
31
+
32
+ ### Quick Reference
33
+
34
+ **For Keycloak (current setup)**:
35
+ 1. Add user to Keycloak at `auth.gilest.ro` in the `gilestro-lab` realm
36
+ 2. Create home directory on host: `sudo mkdir -p /mnt/homes/username && sudo chown 1000:1000 /mnt/homes/username`
37
+ 3. (Optional) Add to `config/users.py` if using whitelist enforcement
38
+
39
+ **For Dummy Auth**:
40
+ 1. Add username to `config/users.py` or `.env` file
41
+ 2. Create home directory on host
42
+ 3. Restart container
43
+
44
+ See [README_AUTH.md](./README_AUTH.md) for complete step-by-step instructions.
45
+
46
+ ## Mounting Home Directories as Volumes
47
+
48
+ To persist user data and notebooks across container restarts, you should mount user home directories as Docker volumes. This is done by modifying the `docker-compose.yml` file.
49
+
50
+ ### Benefits of mounting home directories:
51
+
52
+ 1. **Data Persistence**: User notebooks, data files, and configurations survive container restarts and updates
53
+ 2. **Backup and Recovery**: Easy to backup user data by copying the mounted directories
54
+ 3. **Performance**: Direct access to host filesystem, avoiding container storage overhead
55
+ 4. **Sharing**: Users can access their files from the host system if needed
56
+
57
+ ### Example volume configuration:
58
+
59
+ Add volumes to your `docker-compose.yml`:
60
+
61
+ ```yaml
62
+ services:
63
+ jupyterhub:
64
+ volumes:
65
+ - ./user_data:/home # Maps host ./user_data to container /home
66
+ - ./jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py
67
+ ```
68
+
69
+ Or for individual user directories:
70
+
71
+ ```yaml
72
+ volumes:
73
+ - ./users/ggilestro:/home/ggilestro
74
+ - ./users/amadabhushi:/home/amadabhushi
75
+ - ./users/shared:/home/shared # Shared directory for all users
76
+ ```
77
+
78
+ This ensures all user work is preserved even when containers are recreated or updated.
79
+
80
+ ## Database Preparation for Docker
81
+
82
+ When mounting ethoscope database files into Docker containers with read-only permissions (`:ro`), you may encounter "database disk image is malformed" errors. This occurs when SQLite databases are in WAL (Write-Ahead Logging) mode but mounted on read-only filesystems where WAL companion files (`.db-wal`, `.db-shm`) cannot be accessed.
83
+
84
+ ### Understanding the Issue
85
+
86
+ **Why this happens:**
87
+ - Ethoscope databases may be created in **WAL mode** for better write performance
88
+ - WAL mode requires companion files (`.db-wal` and `.db-shm`) alongside the main `.db` file
89
+ - Docker read-only mounts (`:ro`) prevent SQLite from accessing these files properly
90
+ - This causes intermittent "database disk image is malformed" errors during data loading
91
+
92
+ **Symptoms:**
93
+ - Intermittent loading failures for specific ROIs
94
+ - Error message: "database disk image is malformed"
95
+ - Same ROI may succeed on some loads and fail on others
96
+ - More common with large databases (>1 GB)
97
+
98
+ ### Solution: Convert Databases to DELETE Mode
99
+
100
+ Before mounting databases in Docker, convert them from WAL to DELETE mode:
101
+
102
+ #### Using the Python Script (Recommended)
103
+
104
+ ```bash
105
+ # From the ethoscopy project root
106
+ python3 scripts/convert_wal_to_delete.py /mnt/ethoscope_data/results --dry-run
107
+
108
+ # Convert all databases with detailed output
109
+ python3 scripts/convert_wal_to_delete.py /mnt/ethoscope_data/results --verbose
110
+
111
+ # Force conversion even for recently modified files (use with caution)
112
+ python3 scripts/convert_wal_to_delete.py /mnt/ethoscope_data/results --force
113
+ ```
114
+
115
+ #### Using the Bash Wrapper
116
+
117
+ ```bash
118
+ # From the ethoscopy project root
119
+ ./scripts/convert_databases.sh /mnt/ethoscope_data/results
120
+ ```
121
+
122
+ #### Manual Conversion Using sqlite3
123
+
124
+ For individual databases:
125
+
126
+ ```bash
127
+ sqlite3 /path/to/database.db "PRAGMA wal_checkpoint(TRUNCATE); PRAGMA journal_mode=DELETE;"
128
+ ```
129
+
130
+ For bulk conversion:
131
+
132
+ ```bash
133
+ find /mnt/ethoscope_data/results -name "*.db" -type f -exec sqlite3 {} "PRAGMA wal_checkpoint(TRUNCATE); PRAGMA journal_mode=DELETE;" \;
134
+ ```
135
+
136
+ ### Verification
137
+
138
+ After conversion, verify the database is in DELETE mode:
139
+
140
+ ```bash
141
+ sqlite3 /path/to/database.db "PRAGMA journal_mode;"
142
+ # Should output: delete
143
+
144
+ sqlite3 /path/to/database.db "PRAGMA integrity_check;"
145
+ # Should output: ok
146
+ ```
147
+
148
+ ### Best Practices
149
+
150
+ 1. **Run conversion BEFORE starting Docker containers**
151
+ ```bash
152
+ # From the ethoscopy project root, convert databases first
153
+ ./scripts/convert_databases.sh /mnt/ethoscope_data/results
154
+
155
+ # Then start containers
156
+ cd Docker && docker compose up -d
157
+ ```
158
+
159
+ 2. **Do NOT convert databases while ethoscopes are actively writing**
160
+ - The conversion script skips files modified in the last 24 hours by default
161
+ - Use `--force` to override this safety check if needed
162
+
163
+ 3. **Test on a backup first**
164
+ ```bash
165
+ # Create a test copy
166
+ cp /path/to/database.db /tmp/test.db
167
+
168
+ # Test conversion
169
+ sqlite3 /tmp/test.db "PRAGMA wal_checkpoint(TRUNCATE); PRAGMA journal_mode=DELETE;"
170
+
171
+ # Verify it works
172
+ sqlite3 /tmp/test.db "PRAGMA integrity_check;"
173
+ ```
174
+
175
+ 4. **Consider converting at the ethoscope source** (upstream fix)
176
+ - Configure ethoscopes to use DELETE mode by default
177
+ - Prevents the need for post-processing
178
+
179
+ ### Troubleshooting
180
+
181
+ **Q: I still get "malformed" errors after conversion**
182
+
183
+ A: The ethoscopy library now includes improved connection handling that should work with both WAL and DELETE mode databases. If you still encounter issues:
184
+ 1. Verify the database was actually converted: `sqlite3 database.db "PRAGMA journal_mode;"`
185
+ 2. Check database integrity: `sqlite3 database.db "PRAGMA integrity_check;"`
186
+ 3. Ensure you're using ethoscopy version 2.0.4 or later
187
+
188
+ **Q: Can I convert databases while Docker is running?**
189
+
190
+ A: Yes, but you should restart the container after conversion:
191
+ ```bash
192
+ # From the ethoscopy project root
193
+ ./scripts/convert_databases.sh /mnt/ethoscope_data/results
194
+ cd Docker && docker compose restart ethoscope-lab
195
+ ```
196
+
197
+ **Q: Will this affect data quality or analysis?**
198
+
199
+ A: No. The conversion only changes how SQLite manages the database internally. All data remains identical and analysis results are unaffected.
200
+
201
+ **Q: How do I check if a database is in WAL mode?**
202
+
203
+ A:
204
+ ```bash
205
+ sqlite3 database.db "PRAGMA journal_mode;"
206
+ ```
207
+ Output will be either `wal` or `delete` (or `truncate`, `persist`, `memory`).
208
+
209
+ ### Emergency Quick Fix
210
+
211
+ If you need to work immediately and can't run the full conversion:
212
+
213
+ ```bash
214
+ # SSH to host machine (not inside container)
215
+ # Convert just the failing database
216
+ sqlite3 /path/to/failing/database.db "PRAGMA wal_checkpoint(TRUNCATE); PRAGMA journal_mode=DELETE;"
217
+
218
+ # Restart container
219
+ docker compose restart ethoscope-lab
220
+ ```