From 39bd819cfd1477f3384c12baf1ab908ce8cbd489 Mon Sep 17 00:00:00 2001 From: Alexander Toth Date: Fri, 11 Sep 2026 15:34:31 -0400 Subject: [PATCH 1/2] replaced Label-Check with InSlide (except in pipeline-specific code) --- .env.example | 12 ++-- Dockerfile | 22 +++--- README.md | 84 +++++++++++------------ compose.direct.yaml | 4 +- compose.yaml | 34 ++++----- container/caddy/Caddyfile | 4 +- nightly_label_check.py | 2 +- src/app.py | 40 +++++------ src/batch_catalog.py | 4 +- src/container_paths.py | 2 +- src/copath_queue.py | 2 +- src/copath_utilities/copath_connection.py | 2 +- src/migrate_batch_catalog.py | 6 +- src/openapi.json | 2 +- src/other/old_app.py | 8 +-- src/pipeline.py | 6 +- src/stats_scheduler.py | 2 +- src/templates/_app_sidebar.html | 6 +- src/templates/batches.html | 4 +- src/templates/index.html | 4 +- src/templates/renaming.html | 2 +- src/templates/tq.html | 2 +- src/templates/tq_config.html | 2 +- src/templates/tq_logs.html | 2 +- tests/test_batch_catalog.py | 2 +- tests/test_completed_stages.py | 6 +- tests/test_container_paths.py | 6 +- tests/test_renaming.py | 6 +- tests/test_security_config.py | 22 +++--- tests/test_statistics.py | 4 +- tests/test_tq.py | 4 +- 31 files changed, 154 insertions(+), 154 deletions(-) diff --git a/.env.example b/.env.example index 61ecad6..38952d6 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,7 @@ IMAGE_TAG=latest # Server-local IANA timezone used for statistics day boundaries. TZ=America/New_York # Existing network hostname that coworkers use to reach this machine. -LABEL_CHECK_HOSTNAME=replace-with-machine-hostname +INSLIDE_HOSTNAME=replace-with-machine-hostname PIPELINE_MAX_WORKERS=8 PIPELINE_MAX_THUMBNAIL_DIMENSION=4096 LOGIN_PAIR_ATTEMPT_LIMIT=5 @@ -14,8 +14,8 @@ LOGIN_RATE_WINDOW_SECONDS=900 SESSION_COOKIE_SECURE=true # Inside-container allowlists separated by colon. Input/output paths submitted to # pipeline UI or API must resolve beneath these roots. -PIPELINE_INPUT_ROOTS=/data/gt450-images:/data/label-check-batches -PIPELINE_OUTPUT_ROOTS=/data/label-check-batches +PIPELINE_INPUT_ROOTS=/data/gt450-images:/data/inslide-batches +PIPELINE_OUTPUT_ROOTS=/data/inslide-batches # Generate long random values. SECRET_KEY must be at least 32 characters and # ADMIN_DEFAULT_PASSWORD at least 12. Placeholder values are rejected. Do not @@ -37,11 +37,11 @@ GT450_IMAGES_HOST_PREFIX=\\chp.clarian.org\app\Philips_Slide_Images\GT450_images # Docker Desktop must have permission to share each local Windows path below. SCANNER_INVENTORIES_HOST=D:\scanner_inventories -LABEL_CHECK_BATCHES_HOST=D:\label_check_batches -LABEL_CHECK_BATCHES_HOST_PREFIX=D:\label_check_batches +INSLIDE_BATCHES_HOST=D:\label_check_batches +INSLIDE_BATCHES_HOST_PREFIX=D:\label_check_batches IMAGE_STAGING_HOST=D:\image_staging COPATH_CLONE_HOST=D:\copath_clone -LABEL_CHECK_STATE_HOST=D:\label_check_batches\state +INSLIDE_STATE_HOST=D:\label_check_batches\state BATCH_CATALOG_RECONCILE_SECONDS=60 TQ_HOME_HOST=D:\label-check-tq SSH_HOME_HOST=D:\label-check-ssh diff --git a/Dockerfile b/Dockerfile index f03de4d..8b15771 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,11 +22,11 @@ FROM python:${PYTHON_VERSION}-slim-bookworm AS python-base ENV DEBIAN_FRONTEND=noninteractive \ PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ - LABEL_CHECK_CONTAINER=true \ + INSLIDE_CONTAINER=true \ EASYOCR_FORCE_CPU=true \ EASYOCR_MODEL_DIR=/opt/easyocr-models \ - HOME=/home/labelcheck \ - TQ_HOME_DIR=/home/labelcheck/.tq + HOME=/home/inslide \ + TQ_HOME_DIR=/home/inslide/.tq RUN apt-get update \ && apt-get install --yes --no-install-recommends \ @@ -92,11 +92,11 @@ FROM python-base AS runtime COPY --from=rust-builder /build/tq/target/release/tq /app/bin/tq -RUN groupadd --gid 10001 labelcheck \ - && useradd --uid 10001 --gid labelcheck --create-home labelcheck \ - && mkdir -p /data/state/instance /home/labelcheck/.ssh /home/labelcheck/.tq \ +RUN groupadd --gid 10001 inslide \ + && useradd --uid 10001 --gid inslide --create-home inslide \ + && mkdir -p /data/state/instance /home/inslide/.ssh /home/inslide/.tq \ && sha256sum /app/bin/tq > /app/bin/tq.sha256 \ - && chown -R labelcheck:labelcheck /app /data/state /home/labelcheck \ + && chown -R inslide:inslide /app /data/state /home/inslide \ && sh -c '/app/bin/tq >/dev/null 2>&1; test "$?" -eq 1' ENV TQ_EXECUTABLE=/app/bin/tq \ @@ -104,19 +104,19 @@ ENV TQ_EXECUTABLE=/app/bin/tq \ SDL_FILE_PATH=/data/state/Slide_Digitization_Log.xlsx \ BACKUP_DIR=/data/state/csv_backups \ SCANNER_INVENTORIES=/data/scanner-inventories \ - LABEL_CHECK_BATCHES=/data/label-check-batches \ + INSLIDE_BATCHES=/data/inslide-batches \ IMAGE_STAGING_ROOT=/data/image-staging \ COPATH_CLONE=/data/copath-clone \ - TQ_TRANSFER_LOG_DIR=/data/label-check-batches/transfer_logs \ + TQ_TRANSFER_LOG_DIR=/data/inslide-batches/transfer_logs \ GT450_IMAGES_CONTAINER_ROOT=/data/gt450-images \ - LABEL_CHECK_BATCHES_CONTAINER_ROOT=/data/label-check-batches \ + INSLIDE_BATCHES_CONTAINER_ROOT=/data/inslide-batches \ COPATH_QUERY_MODE=windows_queue \ COPATH_QUERY_QUEUE=/data/state/copath-query \ COPATH_QUERY_TIMEOUT_SECONDS=300 \ PORT=5000 WORKDIR /app/src -USER labelcheck +USER inslide EXPOSE 5000 HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ diff --git a/README.md b/README.md index b75ac9c..691d588 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Label-Check +# InSlide A label corrector ## Pipeline API The versioned pipeline API is available under `/api/v1` and requires HTTPS plus a -scoped personal access token. Create a token for an existing Label-Check user: +scoped personal access token. Create a token for an existing InSlide user: ```bash flask --app src/app.py api-token create USERNAME --label "integration name" @@ -17,8 +17,8 @@ manage credentials. Submit a job using server-visible input and output paths: ```bash -curl --request POST https://label-check.example/api/v1/pipeline/jobs \ - --header "Authorization: Bearer $LABEL_CHECK_TOKEN" \ +curl --request POST https://inslide.example/api/v1/pipeline/jobs \ + --header "Authorization: Bearer $INSLIDE_TOKEN" \ --header "Content-Type: application/json" \ --header "Idempotency-Key: unique-client-request-id" \ --data '{"input_dir":"/data/incoming","output_dir":"/data/output"}' @@ -39,8 +39,8 @@ the proxy over plaintext HTTP. Pipeline paths are restricted after path translation and symbolic-link resolution. `PIPELINE_INPUT_ROOTS` and `PIPELINE_OUTPUT_ROOTS` contain platform-path-separator-delimited allowlists. Compose permits input beneath -`/data/gt450-images` or `/data/label-check-batches` and output beneath -`/data/label-check-batches`. Worker fields default to a maximum of 8 and +`/data/gt450-images` or `/data/inslide-batches` and output beneath +`/data/inslide-batches`. Worker fields default to a maximum of 8 and thumbnail dimensions to 4096 pixels; deployments can lower these limits with `PIPELINE_MAX_WORKERS` and `PIPELINE_MAX_THUMBNAIL_DIMENSION`. @@ -57,7 +57,7 @@ Cargo nor GCC. - Repository cloned with its pinned TQ submodule: ```powershell - git clone --recurse-submodules + git clone --recurse-submodules ``` - Windows directories shared with Docker Desktop. @@ -79,16 +79,16 @@ Cargo nor GCC. network policy. Copy `.env.example` to `.env`, replace all placeholders, and create the host -directories. `LABEL_CHECK_STATE_HOST` must contain +directories. `INSLIDE_STATE_HOST` must contain `Slide_Digitization_Log.xlsx` before SDL workflows run. Batch workflow stages, queues, and leases are stored in -`LABEL_CHECK_STATE_HOST\instance\batch_catalog.sqlite3`. Batch list pages query +`INSLIDE_STATE_HOST\instance\batch_catalog.sqlite3`. Batch list pages query this catalog and load `enriched.csv` only after a batch is selected. A background reconciliation scans for externally-created batches at startup and every `BATCH_CATALOG_RECONCILE_SECONDS` seconds (60 by default). -Before the first catalog-aware deployment, stop the Label-Check container and +Before the first catalog-aware deployment, stop the InSlide container and validate legacy state from a PowerShell prompt: ```powershell @@ -125,7 +125,7 @@ application when the trusted proxy reports an HTTPS request. Compose uses Caddy's internal certificate authority because the organization does not supply the deployment certificate. Each authorized client must trust the generated root certificate before using the app. Protect and back up the -`label-check-caddy-data` volume: it contains the local CA private key. Deleting +`inslide-caddy-data` volume: it contains the local CA private key. Deleting that volume creates a new CA and requires every client to trust the replacement. New user passwords must contain 12–128 characters. Login failures are stored in @@ -137,7 +137,7 @@ counters. The limits and window are configurable through the corresponding Application startup creates runtime directories with mode `0700`, files with mode `0600`, and repairs existing instance state before loading it. Symbolic links in sensitive instance state are rejected. On Windows bind mounts, NTFS -ACLs remain the security boundary: restrict `LABEL_CHECK_STATE_HOST` to the +ACLs remain the security boundary: restrict `INSLIDE_STATE_HOST` to the service account, Docker Desktop service account, and administrators. User activity is accumulated in `/data/state/instance/statistics.sqlite3`. @@ -152,7 +152,7 @@ out-of-band channel. After verifying it, place the corresponding OpenSSH `known_hosts` entry in `${SSH_HOME_HOST}\known_hosts`. `ssh-keyscan` output must not be trusted until its fingerprint has been independently verified. -Only Label-Check administrators can view or edit the global TQ connection +Only InSlide administrators can view or edit the global TQ connection configuration. Authenticated operators can continue transferring approved slides through that administrator-managed destination. @@ -175,7 +175,7 @@ does not contain the Windows username or password. ```powershell git submodule update --init --recursive -docker build --target test --tag label-check:test . +docker build --target test --tag inslide:test . docker compose build ``` @@ -195,7 +195,7 @@ EasyOCR is CPU-only and its English models are baked into the image. Obtain the Linux binary hash when needed with: ```powershell -docker run --rm --entrypoint sha256sum label-check:latest /app/bin/tq +docker run --rm --entrypoint sha256sum inslide:latest /app/bin/tq ``` ### Paths @@ -204,7 +204,7 @@ Inside the container, Windows resources appear at stable Linux paths: - GT450 images: `/data/gt450-images` - scanner inventories: `/data/scanner-inventories` -- label-check batches: `/data/label-check-batches` +- Label-Check pipeline batches: `/data/inslide-batches` - deidentified transfer staging: `/data/image-staging` - CoPath clone: `/data/copath-clone` - persistent application state: `/data/state` @@ -212,12 +212,12 @@ Inside the container, Windows resources appear at stable Linux paths: Persisted UNC GT450 paths and `D:\label_check_batches` paths are translated to these mounts. New pipeline output records Linux mount paths directly. -The GT450 mount is the named Docker volume `label-check-gt450-images`. It mounts +The GT450 mount is the named Docker volume `inslide-gt450-images`. It mounts `//chp.clarian.org/app/Philips_Slide_Images/GT450_Images` through CIFS with read-only permissions. The remaining paths are ordinary Windows bind mounts. Create and share `IMAGE_STAGING_HOST` (normally `D:\image_staging`) with Docker -Desktop. Before each TQ upload, Label-Check copies selected GT450 slides to +Desktop. Before each TQ upload, InSlide copies selected GT450 slides to `\\.svs`, removes the identifying label and macro from those copies, then gives the staged slide paths and a generated metadata CSV to TQ. Each transfer uploads that CSV as @@ -234,9 +234,9 @@ Docker Desktop service account. CoPath queries are delegated to a worker that you start after signing into Windows. Docker and the worker communicate only through -`LABEL_CHECK_STATE_HOST\copath-query`; no SQL credentials enter the container. +`INSLIDE_STATE_HOST\copath-query`; no SQL credentials enter the container. The queue contains accessions and report data. Restrict the entire -`LABEL_CHECK_STATE_HOST` directory to the signed-in Windows account, the Docker +`INSLIDE_STATE_HOST` directory to the signed-in Windows account, the Docker Desktop service account, and administrators. Do not share it broadly or use a world-writable network directory. @@ -267,7 +267,7 @@ batch: ```powershell .\.venv-copath-worker\Scripts\python.exe src\copath_windows_worker.py ` - --queue "$env:LABEL_CHECK_STATE_HOST\copath-query" ` + --queue "$env:INSLIDE_STATE_HOST\copath-query" ` --connection-string-file "$env:COPATH_CONNECTION_STRING_FILE_HOST" ``` @@ -313,24 +313,24 @@ docker compose up -d docker compose ps ``` -Set `LABEL_CHECK_HOSTNAME` in `.env` to the machine's existing network hostname. +Set `INSLIDE_HOSTNAME` in `.env` to the machine's existing network hostname. After the first startup, export Caddy's root CA certificate to the current user's Downloads directory: ```powershell docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt ` - "$env:USERPROFILE\Downloads\label-check-local-ca.crt" + "$env:USERPROFILE\Downloads\inslide-local-ca.crt" ``` On the host and each authorized coworker's Windows device, import -`label-check-local-ca.crt` into **Trusted Root Certification Authorities** for +`inslide-local-ca.crt` into **Trusted Root Certification Authorities** for the current user. Treat the certificate as trusted software and distribute it through an authenticated channel. Do not distribute anything else from the Caddy data volume, especially `root.key`. ```powershell Import-Certificate ` - -FilePath "$env:USERPROFILE\Downloads\label-check-local-ca.crt" ` + -FilePath "$env:USERPROFILE\Downloads\inslide-local-ca.crt" ` -CertStoreLocation Cert:\CurrentUser\Root ``` @@ -341,13 +341,13 @@ Confirm that the hostname resolves to the Docker host and that HTTPS is reachable, then open the app: ```powershell -$labelCheckHostname = "replace-with-configured-hostname" -Resolve-DnsName $labelCheckHostname -Test-NetConnection $labelCheckHostname -Port 443 -Start-Process "https://$labelCheckHostname" +$inSlideHostname = "replace-with-configured-hostname" +Resolve-DnsName $inSlideHostname +Test-NetConnection $inSlideHostname -Port 443 +Start-Process "https://$inSlideHostname" ``` -Use the exact hostname configured in `LABEL_CHECK_HOSTNAME`; an IP address or a +Use the exact hostname configured in `INSLIDE_HOSTNAME`; an IP address or a different alias will fail certificate validation. Port 80 is intentionally not published, so include `https://` in the URL. If name resolution or inbound port 443 is blocked, ask IT to add the hostname/firewall allowance. Application port @@ -357,20 +357,20 @@ Verify that the SMB mount contains the expected scanner directories and is read-only for the application user: ```powershell -docker compose exec label-check ls -la /data/gt450-images -docker inspect $(docker compose ps -q label-check) ` +docker compose exec inslide ls -la /data/gt450-images +docker inspect $(docker compose ps -q inslide) ` --format '{{range .Mounts}}{{if eq .Destination "/data/gt450-images"}}{{println "RW:" .RW "Name:" .Name}}{{end}}{{end}}' ``` The inspection output must report `RW: false` and -`Name: label-check-gt450-images`. +`Name: inslide-gt450-images`. Docker volume options are fixed when the volume is created. After changing the SMB password or any `GT450_SMB_*` mount setting, recreate only this mount: ```powershell docker compose down -docker volume rm label-check-gt450-images +docker volume rm inslide-gt450-images docker compose up -d ``` @@ -381,13 +381,13 @@ The default command initializes persistent state and starts Waitress on port 5000. Other applications use the same image: ```powershell -docker compose run --rm label-check pipeline ` +docker compose run --rm inslide pipeline ` --input-dir /data/gt450-images/SS12797 ` - --output-dir /data/label-check-batches/SS12797/2026-07-31 ` + --output-dir /data/inslide-batches/SS12797/2026-07-31 ` --end-at name --ocr-use-cpu -docker compose run --rm label-check nightly -docker compose run --rm label-check python /app/src/deidentify_anonymize.py --help +docker compose run --rm inslide nightly +docker compose run --rm inslide python /app/src/deidentify_anonymize.py --help ``` Schedule `nightly` externally; it performs one cycle and exits. State, SDL, @@ -399,12 +399,12 @@ backups, TQ configuration, and transfer logs survive container replacement. account credentials and domain, SMB 3.0 connectivity, and access to the `app` share. Docker should fail container startup on a mount error instead of substituting an empty local directory. After changing mount options, remove - and recreate `label-check-gt450-images` as described above. + and recreate `inslide-gt450-images` as described above. ### CoPath troubleshooting - **Worker offline:** confirm the worker console is still running and that both - Windows and Docker can access `LABEL_CHECK_STATE_HOST\copath-query`. Check the + Windows and Docker can access `INSLIDE_STATE_HOST\copath-query`. Check the Windows clock if `worker.json` is present but considered stale. - **Query timeout:** the default is 300 seconds. Inspect the worker console and SQL connectivity before increasing `COPATH_QUERY_TIMEOUT_SECONDS`. @@ -425,9 +425,9 @@ direct-mode override: ```powershell docker compose -f compose.yaml -f compose.direct.yaml up -d -docker compose -f compose.yaml -f compose.direct.yaml exec label-check ` +docker compose -f compose.yaml -f compose.direct.yaml exec inslide ` kinit YOUR_USERNAME@YOUR.AD.REALM -docker compose -f compose.yaml -f compose.direct.yaml exec label-check klist +docker compose -f compose.yaml -f compose.direct.yaml exec inslide klist ``` In direct mode, use the SQL Server DNS name associated with its `MSSQLSvc` diff --git a/compose.direct.yaml b/compose.direct.yaml index 02a9f98..00f1751 100644 --- a/compose.direct.yaml +++ b/compose.direct.yaml @@ -1,10 +1,10 @@ services: - label-check: + inslide: environment: COPATH_QUERY_MODE: direct COPATH_CONNECTION_STRING_FILE: /run/secrets/copath_connection_string KRB5_CONFIG: /etc/krb5.conf - KRB5CCNAME: /tmp/krb5cc_labelcheck + KRB5CCNAME: /tmp/krb5cc_inslide volumes: - type: bind source: ${KRB5_CONFIG_HOST:?Set KRB5_CONFIG_HOST to the domain Kerberos configuration file} diff --git a/compose.yaml b/compose.yaml index ebc9c1f..3ceaa65 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,5 +1,5 @@ services: - label-check: + inslide: platform: linux/amd64 build: context: . @@ -7,7 +7,7 @@ services: args: RUST_VERSION: ${RUST_VERSION:-1.95} PYTHON_VERSION: ${PYTHON_VERSION:-3.12} - image: label-check:${IMAGE_TAG:-latest} + image: inslide:${IMAGE_TAG:-latest} environment: TZ: ${TZ:?Set TZ to the server-local IANA timezone} SECRET_KEY: ${SECRET_KEY:?Set SECRET_KEY} @@ -15,8 +15,8 @@ services: API_REQUIRE_HTTPS: "true" API_TRUST_PROXY_HEADERS: "true" SESSION_COOKIE_SECURE: "true" - PIPELINE_INPUT_ROOTS: ${PIPELINE_INPUT_ROOTS:-/data/gt450-images:/data/label-check-batches} - PIPELINE_OUTPUT_ROOTS: ${PIPELINE_OUTPUT_ROOTS:-/data/label-check-batches} + PIPELINE_INPUT_ROOTS: ${PIPELINE_INPUT_ROOTS:-/data/gt450-images:/data/inslide-batches} + PIPELINE_OUTPUT_ROOTS: ${PIPELINE_OUTPUT_ROOTS:-/data/inslide-batches} PIPELINE_MAX_WORKERS: ${PIPELINE_MAX_WORKERS:-8} PIPELINE_MAX_THUMBNAIL_DIMENSION: ${PIPELINE_MAX_THUMBNAIL_DIMENSION:-4096} LOGIN_PAIR_ATTEMPT_LIMIT: ${LOGIN_PAIR_ATTEMPT_LIMIT:-5} @@ -24,7 +24,7 @@ services: LOGIN_RATE_WINDOW_SECONDS: ${LOGIN_RATE_WINDOW_SECONDS:-900} BATCH_CATALOG_RECONCILE_SECONDS: ${BATCH_CATALOG_RECONCILE_SECONDS:-60} GT450_IMAGES_HOST_PREFIX: ${GT450_IMAGES_HOST_PREFIX:?Set GT450_IMAGES_HOST_PREFIX} - LABEL_CHECK_BATCHES_HOST_PREFIX: ${LABEL_CHECK_BATCHES_HOST_PREFIX:-D:\label_check_batches} + INSLIDE_BATCHES_HOST_PREFIX: ${INSLIDE_BATCHES_HOST_PREFIX:-D:\label_check_batches} IMAGE_STAGING_ROOT: /data/image-staging IMAGE_STAGING_HOST_DISPLAY: ${IMAGE_STAGING_HOST:?Set IMAGE_STAGING_HOST} COPATH_QUERY_MODE: ${COPATH_QUERY_MODE:-windows_queue} @@ -43,8 +43,8 @@ services: target: /data/scanner-inventories read_only: true - type: bind - source: ${LABEL_CHECK_BATCHES_HOST:?Set LABEL_CHECK_BATCHES_HOST} - target: /data/label-check-batches + source: ${INSLIDE_BATCHES_HOST:?Set INSLIDE_BATCHES_HOST} + target: /data/inslide-batches - type: bind source: ${IMAGE_STAGING_HOST:?Set IMAGE_STAGING_HOST} target: /data/image-staging @@ -52,14 +52,14 @@ services: source: ${COPATH_CLONE_HOST:?Set COPATH_CLONE_HOST} target: /data/copath-clone - type: bind - source: ${LABEL_CHECK_STATE_HOST:?Set LABEL_CHECK_STATE_HOST} + source: ${INSLIDE_STATE_HOST:?Set INSLIDE_STATE_HOST} target: /data/state - type: bind source: ${TQ_HOME_HOST:?Set TQ_HOME_HOST} - target: /home/labelcheck/.tq + target: /home/inslide/.tq - type: bind source: ${SSH_HOME_HOST:?Set SSH_HOME_HOST} - target: /home/labelcheck/.ssh + target: /home/inslide/.ssh read_only: true restart: unless-stopped @@ -71,23 +71,23 @@ services: args: RUST_VERSION: ${RUST_VERSION:-1.95} PYTHON_VERSION: ${PYTHON_VERSION:-3.12} - image: label-check:${IMAGE_TAG:-latest} + image: inslide:${IMAGE_TAG:-latest} command: stats-scheduler environment: TZ: ${TZ:?Set TZ to the server-local IANA timezone} INSTANCE_DIR: /data/state/instance volumes: - type: bind - source: ${LABEL_CHECK_STATE_HOST:?Set LABEL_CHECK_STATE_HOST} + source: ${INSLIDE_STATE_HOST:?Set INSLIDE_STATE_HOST} target: /data/state restart: unless-stopped caddy: image: caddy:2.11.4-alpine environment: - LABEL_CHECK_HOSTNAME: ${LABEL_CHECK_HOSTNAME:?Set LABEL_CHECK_HOSTNAME} + INSLIDE_HOSTNAME: ${INSLIDE_HOSTNAME:?Set INSLIDE_HOSTNAME} depends_on: - label-check: + inslide: condition: service_healthy ports: - "443:443" @@ -106,11 +106,11 @@ services: volumes: caddy-data: - name: label-check-caddy-data + name: inslide-caddy-data caddy-config: - name: label-check-caddy-config + name: inslide-caddy-config gt450-images: - name: label-check-gt450-images + name: inslide-gt450-images driver: local driver_opts: type: cifs diff --git a/container/caddy/Caddyfile b/container/caddy/Caddyfile index 775e26c..423d6dd 100644 --- a/container/caddy/Caddyfile +++ b/container/caddy/Caddyfile @@ -2,7 +2,7 @@ auto_https disable_redirects } -https://{$LABEL_CHECK_HOSTNAME} { +https://{$INSLIDE_HOSTNAME} { tls internal - reverse_proxy label-check:5000 + reverse_proxy inslide:5000 } diff --git a/nightly_label_check.py b/nightly_label_check.py index 4aed07c..52cd000 100644 --- a/nightly_label_check.py +++ b/nightly_label_check.py @@ -10,7 +10,7 @@ from container_paths import runtime_path label_check_batches = Path( - os.environ.get("LABEL_CHECK_BATCHES", "D:\\label_check_batches") + os.environ.get("INSLIDE_BATCHES", "D:\\label_check_batches") ) scanner_inventories = Path( os.environ.get("SCANNER_INVENTORIES", "D:\\scanner_inventories") diff --git a/src/app.py b/src/app.py index d846652..375ffd1 100755 --- a/src/app.py +++ b/src/app.py @@ -191,9 +191,9 @@ class Config: SCANNER_INVENTORIES = os.environ.get( "SCANNER_INVENTORIES", "D:\\scanner_inventories" ) - # Path to batches of new slides to label-check - LABEL_CHECK_BATCHES = os.environ.get( - "LABEL_CHECK_BATCHES", "D:\\label_check_batches" + # Path to batches of new slides to process in InSlide + INSLIDE_BATCHES = os.environ.get( + "INSLIDE_BATCHES", "D:\\label_check_batches" ) COPATH_CLONE = os.environ.get("COPATH_CLONE", "D:\\copath_clone") @@ -221,7 +221,7 @@ def _set_private_mode(path: Path, expected_mode: int) -> bool: os.chmod(path, expected_mode) return True except PermissionError: - containerized = os.environ.get("LABEL_CHECK_CONTAINER", "false").lower() == "true" + containerized = os.environ.get("INSLIDE_CONTAINER", "false").lower() == "true" required_access = os.R_OK | os.W_OK if path.is_dir(): required_access |= os.X_OK @@ -1044,7 +1044,7 @@ def dashboard(self, user_id: str) -> Dict[str, Any]: api_store = APIStore(Config.API_DB_PATH, Config.API_JOB_OUTPUT_DIR) -if os.environ.get("LABEL_CHECK_STATS_SCHEDULER") != "true": +if os.environ.get("INSLIDE_STATS_SCHEDULER") != "true": api_store.mark_stale_jobs_interrupted() stats_store = StatisticsStore(Config.STATS_DB_PATH, Config.USER_STATS_ROOT) @@ -1631,7 +1631,7 @@ def total_count(self) -> int: def _batch_relative_path(root: Path) -> str: - return normalize_relative_path(root.relative_to(Path(Config.LABEL_CHECK_BATCHES)).as_posix()) + return normalize_relative_path(root.relative_to(Path(Config.INSLIDE_BATCHES)).as_posix()) def _reconcile_queue_rows(public_id: str, slide_rows: Sequence[Dict[str, str]]) -> None: @@ -1659,7 +1659,7 @@ def _reconcile_queue_rows(public_id: str, slide_rows: Sequence[Dict[str, str]]) def reconcile_batch_catalog() -> List[str]: """Refresh catalog from batch directory; never read legacy stage/queue files.""" - base = Path(Config.LABEL_CHECK_BATCHES) + base = Path(Config.INSLIDE_BATCHES) warnings: List[str] = [] seen: List[str] = [] try: @@ -1668,7 +1668,7 @@ def reconcile_batch_catalog() -> List[str]: key=lambda path: path.name.lower(), ) except OSError as exc: - app.logger.warning("Label-check batch directory unavailable: %s", exc) + app.logger.warning("Label-Check pipeline batch directory unavailable: %s", exc) return [f"Batch directory is unavailable: {base}"] candidates: List[Path] = [] @@ -1798,7 +1798,7 @@ def _catalog_reconciler() -> None: def _ensure_catalog_reconciled() -> List[str]: global _catalog_reconciled_target, _catalog_reconciler_started - target = (str(Path(Config.INSTANCE_DIR)), str(Path(Config.LABEL_CHECK_BATCHES))) + target = (str(Path(Config.INSTANCE_DIR)), str(Path(Config.INSLIDE_BATCHES))) warnings: List[str] = [] with _catalog_reconcile_lock: if _catalog_reconciled_target != target: @@ -1836,7 +1836,7 @@ def discover_batches() -> Tuple[List[BatchContext], List[str]]: for row in batch_catalog.list_batches(Config.INSTANCE_DIR): if row["validity"] != "ready": continue - root = Path(Config.LABEL_CHECK_BATCHES) / Path(row["relative_path"]) + root = Path(Config.INSLIDE_BATCHES) / Path(row["relative_path"]) batch_id = str(row["public_id"]) with batch_contexts_lock: context = batch_contexts.get(batch_id) @@ -1918,7 +1918,7 @@ def worker() -> None: renaming.stage_longitudinal_history( context.root, Path(Config.COPATH_CLONE), - Path(Config.LABEL_CHECK_BATCHES), + Path(Config.INSLIDE_BATCHES), ) mapping_path = context.root / "name_mapping.csv" if mapping_path.exists(): @@ -1972,7 +1972,7 @@ def worker() -> None: renaming.retry_group( context.root, Path(Config.COPATH_CLONE), - Path(Config.LABEL_CHECK_BATCHES), + Path(Config.INSLIDE_BATCHES), old_accession, new_accession, ) @@ -1983,7 +1983,7 @@ def worker() -> None: renaming.prepare_batch( context.root, Path(Config.COPATH_CLONE), - Path(Config.LABEL_CHECK_BATCHES), + Path(Config.INSLIDE_BATCHES), ) state = {"status": "ready", "error": ""} except Exception as exc: @@ -3057,7 +3057,7 @@ def _tq_transfer_log_root() -> Path: return ( Path(configured).expanduser() if configured - else Path(Config.LABEL_CHECK_BATCHES) / "transfer_logs" + else Path(Config.INSLIDE_BATCHES) / "transfer_logs" ) @@ -4479,7 +4479,7 @@ def wrapped(*args, **kwargs): def _api_problem(status: int, code: str, title: str, detail: str): response = jsonify( { - "type": f"https://label-check.invalid/problems/{code}", + "type": f"https://inslide.invalid/problems/{code}", "status": status, "code": code, "title": title, @@ -4490,7 +4490,7 @@ def _api_problem(status: int, code: str, title: str, detail: str): response.status_code = status response.content_type = "application/problem+json" if status == 401: - response.headers["WWW-Authenticate"] = 'Bearer realm="label-check-api"' + response.headers["WWW-Authenticate"] = 'Bearer realm="inslide-api"' return response @@ -5275,7 +5275,7 @@ def renaming_page(): try: with _renaming_clone_lock: renaming.repair_staged_pid_assignments( - Path(Config.COPATH_CLONE), Path(Config.LABEL_CHECK_BATCHES) + Path(Config.COPATH_CLONE), Path(Config.INSLIDE_BATCHES) ) _, rows = renaming.read_csv(mapping_path) reports = renaming.report_rows(context.root, Path(Config.COPATH_CLONE)) @@ -5439,7 +5439,7 @@ def renaming_pid(batch_id: str): pid = renaming.pid_after_organ_change( context.root, Path(Config.COPATH_CLONE), - Path(Config.LABEL_CHECK_BATCHES), + Path(Config.INSLIDE_BATCHES), accession, organ, reserved_pids, @@ -5507,7 +5507,7 @@ def renaming_approve(batch_id: str): "The mapping changed in another session; reload and try again" ) renaming.repair_staged_pid_assignments( - Path(Config.COPATH_CLONE), Path(Config.LABEL_CHECK_BATCHES) + Path(Config.COPATH_CLONE), Path(Config.INSLIDE_BATCHES) ) _, current_rows = renaming.read_csv(mapping_path) repaired_signature = renaming.mapping_signature(current_rows) @@ -5537,7 +5537,7 @@ def renaming_approve(batch_id: str): values["PID"] = renaming.pid_after_organ_change( context.root, Path(Config.COPATH_CLONE), - Path(Config.LABEL_CHECK_BATCHES), + Path(Config.INSLIDE_BATCHES), old_accession, values["Organ"], ) diff --git a/src/batch_catalog.py b/src/batch_catalog.py index b4ebc02..4c09f42 100644 --- a/src/batch_catalog.py +++ b/src/batch_catalog.py @@ -1,4 +1,4 @@ -"""SQLite catalog for label-check batches and their QC queues.""" +"""SQLite catalog for Label-Check pipeline batches and their QC queues.""" from __future__ import annotations @@ -143,7 +143,7 @@ def _ensure_schema(self, path: Path) -> None: os.chmod(path, 0o600) except PermissionError: containerized = os.environ.get( - "LABEL_CHECK_CONTAINER", "false" + "INSLIDE_CONTAINER", "false" ).lower() == "true" if not containerized or not os.access(path, os.R_OK | os.W_OK): raise diff --git a/src/container_paths.py b/src/container_paths.py index e45a5f9..0a41e10 100644 --- a/src/container_paths.py +++ b/src/container_paths.py @@ -9,7 +9,7 @@ MAPPINGS = ( ("GT450_IMAGES_HOST_PREFIX", "GT450_IMAGES_CONTAINER_ROOT"), - ("LABEL_CHECK_BATCHES_HOST_PREFIX", "LABEL_CHECK_BATCHES_CONTAINER_ROOT"), + ("INSLIDE_BATCHES_HOST_PREFIX", "INSLIDE_BATCHES_CONTAINER_ROOT"), ) diff --git a/src/copath_queue.py b/src/copath_queue.py index fc73281..6d5de41 100644 --- a/src/copath_queue.py +++ b/src/copath_queue.py @@ -1,4 +1,4 @@ -"""Validated filesystem queue shared by Label-Check and its Windows worker.""" +"""Validated filesystem queue shared by InSlide and its Windows worker.""" from __future__ import annotations diff --git a/src/copath_utilities/copath_connection.py b/src/copath_utilities/copath_connection.py index 07e835b..7f5ba69 100644 --- a/src/copath_utilities/copath_connection.py +++ b/src/copath_utilities/copath_connection.py @@ -118,7 +118,7 @@ def require_windows_ticket(connection: str) -> None: if result.returncode: raise CoPathConfigurationError( "No valid Windows Authentication ticket is available. Run " - "'docker compose exec label-check kinit YOUR_ACCOUNT@YOUR.AD.REALM', " + "'docker compose exec inslide kinit YOUR_ACCOUNT@YOUR.AD.REALM', " "then retry CoPath preparation." ) diff --git a/src/migrate_batch_catalog.py b/src/migrate_batch_catalog.py index e531dbc..18acf1f 100644 --- a/src/migrate_batch_catalog.py +++ b/src/migrate_batch_catalog.py @@ -227,13 +227,13 @@ def parser() -> argparse.ArgumentParser: result = argparse.ArgumentParser(description=__doc__) result.add_argument( "--batches-root", type=Path, - default=Path(os.environ.get("LABEL_CHECK_BATCHES_HOST") or os.environ.get("LABEL_CHECK_BATCHES", r"D:\label_check_batches")), + default=Path(os.environ.get("INSLIDE_BATCHES_HOST") or os.environ.get("INSLIDE_BATCHES", r"D:\label_check_batches")), ) result.add_argument( "--state-root", type=Path, - default=Path(os.environ.get("LABEL_CHECK_STATE_HOST", r"D:\label_check_batches\state")), + default=Path(os.environ.get("INSLIDE_STATE_HOST", r"D:\label_check_batches\state")), ) - result.add_argument("--legacy-runtime-batches-root", default="/data/label-check-batches") + result.add_argument("--legacy-runtime-batches-root", default="/data/inslide-batches") result.add_argument("--apply", action="store_true", help="write database and archive imported files") result.add_argument("--replace", action="store_true", help="replace an existing catalog") return result diff --git a/src/openapi.json b/src/openapi.json index 7fc83e0..928e735 100644 --- a/src/openapi.json +++ b/src/openapi.json @@ -76,7 +76,7 @@ }, "components": { "securitySchemes": { - "bearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "Label-Check PAT"} + "bearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "InSlide PAT"} }, "parameters": { "IdempotencyKey": {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}, diff --git a/src/other/old_app.py b/src/other/old_app.py index fc2f6d5..e130607 100644 --- a/src/other/old_app.py +++ b/src/other/old_app.py @@ -10,10 +10,10 @@ app.secret_key = 'even_more_secret_key_for_robust_session' # MUST be set for session # --- Configuration --- -CSV_FILE_PATH = '/Users/siddheshthakur/Work/Projects/Label-Check/2025-07-20/output_processed_latest.csv' -IMAGE_BASE_DIR = '/Users/siddheshthakur/Work/Projects/Label-Check/2025-07-20' +CSV_FILE_PATH = '/Users/siddheshthakur/Work/Projects/InSlide/2025-07-20/output_processed_latest.csv' +IMAGE_BASE_DIR = '/Users/siddheshthakur/Work/Projects/InSlide/2025-07-20' BACKUP_DIR = 'csv_backups' # Directory to store backups -INTERMEDIATE_BACKUP_FILE = r'/Users/siddheshthakur/Work/Projects/Label-Check/2025-07-20/output-ocr-2025-07-20_bkup.csv' +INTERMEDIATE_BACKUP_FILE = r'/Users/siddheshthakur/Work/Projects/InSlide/2025-07-20/output-ocr-2025-07-20_bkup.csv' INTERMEDIATE_BACKUP_FREQUENCY = 10 # Save recovery file every 10 updates # --- End Configuration --- @@ -635,4 +635,4 @@ def flash_messages(): print("INFO: Starting Flask development server...") # Use host='0.0.0.0' to make accessible on local network if needed - app.run(debug=True, host='0.0.0.0') \ No newline at end of file + app.run(debug=True, host='0.0.0.0') diff --git a/src/pipeline.py b/src/pipeline.py index 5e39501..315858d 100644 --- a/src/pipeline.py +++ b/src/pipeline.py @@ -31,7 +31,7 @@ def parse_except(error: subprocess.CalledProcessError) -> None: def open_browser_wsl(url: str) -> None: """Open a URL using the host browser, including from WSL.""" - if os.environ.get("LABEL_CHECK_CONTAINER", "false").lower() == "true": + if os.environ.get("INSLIDE_CONTAINER", "false").lower() == "true": return if "microsoft-standard" in platform.uname().release.lower(): subprocess.run(["powershell.exe", "-Command", f"Start-Process '{url}'"]) @@ -184,7 +184,7 @@ def copy_app_bundle(output_dir: Path) -> Path: def create_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description=( - "Pipeline for the modules inside Label-Check. Runs preprocessing stages " + "Label-Check pipeline. Runs preprocessing stages " "and optionally initializes the QC application." ) ) @@ -256,7 +256,7 @@ def main() -> int: return error.returncode or 1 output_src = output_app.parent - containerized = os.environ.get("LABEL_CHECK_CONTAINER", "false").lower() == "true" + containerized = os.environ.get("INSLIDE_CONTAINER", "false").lower() == "true" if not containerized: kill_existing_flask() print("\n\x1b[1mOpening QC app...\x1b[0m\n", flush=True) diff --git a/src/stats_scheduler.py b/src/stats_scheduler.py index e2e0650..4cb39bc 100644 --- a/src/stats_scheduler.py +++ b/src/stats_scheduler.py @@ -6,7 +6,7 @@ import os import time -os.environ["LABEL_CHECK_STATS_SCHEDULER"] = "true" +os.environ["INSLIDE_STATS_SCHEDULER"] = "true" import app as app_module diff --git a/src/templates/_app_sidebar.html b/src/templates/_app_sidebar.html index 5176885..046078c 100644 --- a/src/templates/_app_sidebar.html +++ b/src/templates/_app_sidebar.html @@ -1,7 +1,7 @@