diff --git a/.github/workflows/medcat-demo-app_build.yml b/.github/workflows/medcat-demo-app_build.yml new file mode 100644 index 000000000..ea50da788 --- /dev/null +++ b/.github/workflows/medcat-demo-app_build.yml @@ -0,0 +1,35 @@ +name: medcat-demo - Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +defaults: + run: + working-directory: ./medcat-demo-app + +jobs: + integration-test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Docker Compose + run: sudo apt-get update && sudo apt-get install -y docker-compose + + - name: Build and start containers + run: docker-compose -f docker-compose-test.yml up -d --build + + - name: Run integration test + run: ./tests/test_integration.sh + + - name: Check container logs for errors + run: | + docker-compose logs medcatweb + docker-compose logs medcatweb | grep -i 'error' && exit 1 || true + + - name: Tear down + run: docker-compose -f docker-compose-test.yml down diff --git a/medcat-demo-app/docker-compose-test.yml b/medcat-demo-app/docker-compose-test.yml new file mode 100644 index 000000000..5dbf9c737 --- /dev/null +++ b/medcat-demo-app/docker-compose-test.yml @@ -0,0 +1,16 @@ +version: '3.4' + +services: + medcatweb: + build: + context: ./webapp + command: > + bash -c "/etc/init.d/cron start && + python /webapp/manage.py runserver 0.0.0.0:8000" + volumes: + - ../medcat-v2/tests/resources:/webapp/models + ports: + - "8000:8000" + env_file: + - ./envs/env_medmen_test + tty: true diff --git a/medcat-demo-app/envs/env_medmen_test b/medcat-demo-app/envs/env_medmen_test new file mode 100644 index 000000000..21baaf5c7 --- /dev/null +++ b/medcat-demo-app/envs/env_medmen_test @@ -0,0 +1 @@ +MODEL_PACK_PATH=/webapp/models/mct2_model_pack.zip diff --git a/medcat-demo-app/tests/test_integration.py b/medcat-demo-app/tests/test_integration.py new file mode 100644 index 000000000..228b00192 --- /dev/null +++ b/medcat-demo-app/tests/test_integration.py @@ -0,0 +1,33 @@ +# tests/test_integration.py + +import requests + +from bs4 import BeautifulSoup + +URL = "http://localhost:8000/" +session = requests.Session() + +# GET the page to get the CSRF token +resp = session.get(URL) +soup = BeautifulSoup(resp.text, "html.parser") +csrf = soup.find("input", {"name": "csrfmiddlewaretoken"}).get("value") + +disease = "kidney failure" + +text = f"Patient had been diagnosed with acute {disease} the week before" + +# POST with the token and same session (cookies preserved) +resp = session.post(URL, data={ + "text": text, + "csrfmiddlewaretoken": csrf}) + +print(f"RESPOONSE:\n{resp.text}") + +soup = BeautifulSoup(resp.text, "html.parser") + +annotations = soup.select("div.entities mark.entity") +assert annotations, "No annotations found in the response" +assert any(disease in mark.text.lower() for mark in annotations), ( + f"Disease '{disease}' not found in annotations") + +assert disease in resp.text diff --git a/medcat-demo-app/tests/test_integration.sh b/medcat-demo-app/tests/test_integration.sh new file mode 100755 index 000000000..f81c30a12 --- /dev/null +++ b/medcat-demo-app/tests/test_integration.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -euo pipefail + +echo "Waiting for service to start..." + +# install bs4 for tests +python -m pip install beautifulsoup4 + +# Wait until curl doesn't fail +for i in {1..60}; do + if curl -s --fail http://localhost:8000 > /dev/null; then + echo "Service is up" + break + else + echo "Waiting ($i)..." + sleep 2 + fi +done + +python tests/test_integration.py diff --git a/medcat-demo-app/webapp/Dockerfile b/medcat-demo-app/webapp/Dockerfile index 21d19078f..16555a0bc 100644 --- a/medcat-demo-app/webapp/Dockerfile +++ b/medcat-demo-app/webapp/Dockerfile @@ -1,37 +1,69 @@ -FROM python:3.7 +# Stage 1: Build stage (dependencies and compilation) +FROM python:3.12-slim as build # Create the required folders RUN mkdir -p /webapp/models -# Copy everything +# Copy the application code COPY . /webapp +# Install dependencies for building (git, etc.) +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + build-essential \ + apt-utils \ + cron \ + sqlite3 \ + libsqlite3-dev + +# Install Python dependencies +ARG USE_CPU_TORCH=false +# NOTE: Allow building without GPU so as to lower image size (disabled by default) +RUN pip install -U pip && if [ "$USE_CPU_TORCH" = "true" ]; then \ + pip install -r /webapp/requirements.txt --extra-index-url https://download.pytorch.org/whl/cpu/; \ + else \ + pip install -r /webapp/requirements.txt; \ + fi + +# Get the spacy model (for later copy) +RUN python -m spacy download en_core_web_md + +# Stage 2: Final (production) image +FROM python:3.12-slim as final + +# Install runtime dependencies (you don’t need git in production) +RUN apt-get update && apt-get install -y --no-install-recommends \ + cron \ + sqlite3 \ + libsqlite3-dev && apt-get autoremove + +# Create the required folders (if not created already) +RUN mkdir -p /webapp/models && mkdir -p /medcat_data + +# Copy only necessary files from build stage +COPY --from=build /webapp /webapp +# COPY --from=build /root/.cache /root/.cache # Copy pip cache if needed + +# Copy Python site-packages (installed by pip) from build stage +COPY --from=build /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages + +# Set environment variables ENV VOCAB_URL=https://medcat.rosalind.kcl.ac.uk/media/vocab.dat ENV CDB_URL=https://medcat.rosalind.kcl.ac.uk/media/cdb-medmen-v1.dat ENV CDB_PATH=/webapp/models/cdb.dat ENV VOCAB_PATH=/webapp/models/vocab.dat -# Create the data directory -RUN mkdir -p /medcat_data - # Set the pythonpath WORKDIR /webapp -RUN pip install -r requirements.txt - -# Get the spacy model -RUN python -m spacy download en_core_web_md +# Create the db backup cron job (copied from your setup) +COPY etc/cron.d/db-backup-cron /etc/cron.d/db-backup-cron +RUN chmod 0644 /etc/cron.d/db-backup-cron && crontab /etc/cron.d/db-backup-cron -# Build the db +# Run migrations and collect static (could be in entrypoint script) RUN python manage.py makemigrations && \ python manage.py makemigrations demo && \ python manage.py migrate && \ python manage.py migrate demo && \ python manage.py collectstatic --noinput - -# Create the db backup cron job -RUN apt-get update && apt-get install -y --no-install-recommends apt-utils cron sqlite3 libsqlite3-dev -COPY etc/cron.d/db-backup-cron /etc/cron.d/db-backup-cron -RUN chmod 0644 /etc/cron.d/db-backup-cron -RUN crontab /etc/cron.d/db-backup-cron diff --git a/medcat-demo-app/webapp/demo/forms.py b/medcat-demo-app/webapp/demo/forms.py index 100efc438..cbf740144 100644 --- a/medcat-demo-app/webapp/demo/forms.py +++ b/medcat-demo-app/webapp/demo/forms.py @@ -46,3 +46,8 @@ class Meta: "funder": forms.TextInput(attrs={"size": 40}), "use_case": forms.Textarea(attrs={"rows": 5, "cols": 40}), } + + +class UMLSApiKeyForm(forms.Form): + apikey = forms.CharField(label='UMLS API Key', + widget=forms.TextInput(attrs={'size': 50})) diff --git a/medcat-demo-app/webapp/demo/templates/base.html b/medcat-demo-app/webapp/demo/templates/base.html index 2f6df42ee..1c37e4fc8 100644 --- a/medcat-demo-app/webapp/demo/templates/base.html +++ b/medcat-demo-app/webapp/demo/templates/base.html @@ -15,7 +15,7 @@