Skip to content

Commit

Permalink
Fixes to make UI working with docker
Browse files Browse the repository at this point in the history
  • Loading branch information
lingyielia committed Jan 14, 2025
1 parent fbfd6e9 commit 74a3a2d
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 14 deletions.
10 changes: 5 additions & 5 deletions vizro-ai/examples/dashboard_ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
# you will also find guides on how best to write your Dockerfile
FROM python:3.12-slim

FROM python:3.12
# Install lsof
RUN apt-get update && apt-get install -y lsof && rm -rf /var/lib/apt/lists/*

RUN useradd -m -u 1000 user

Expand All @@ -13,8 +13,8 @@ RUN pip install --no-cache-dir --upgrade -r requirements.txt

COPY --chown=user . /app

EXPOSE 7868
EXPOSE 7868 8051

USER user

CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:7868", "app:server"]
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:7868", "--timeout", "180", "app:server"]
19 changes: 19 additions & 0 deletions vizro-ai/examples/dashboard_ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
```bash
docker buildx build --platform linux/amd64 \
-t vizro-ai-ui-for-dashboard:0.0.1 \
-t vizro-ai-ui-for-dashboard:latest \
--load \
.
```

(local distribution only)
```bash
docker save vizro-ai-ui-for-dashboard:latest -o image.tar
docker load -i image.tar
```


```bash
docker run -p 7868:7868 -p 8051:8051 vizro-ai-ui-for-dashboard:latest
```

64 changes: 61 additions & 3 deletions vizro-ai/examples/dashboard_ui/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import io
import logging
import socket
import os
import signal
import subprocess
import time

import pandas as pd

Expand Down Expand Up @@ -39,14 +43,14 @@ def process_file(contents, filename):
return {"error_message": f"There was an error processing the file '{filename}'."}


def format_output(generated_code):
def format_output(generated_code, port):
generated_code = generated_code.replace("```python", "")
generated_code = generated_code.replace("```", "")

code_lines = generated_code.split("\n")

for i, line in enumerate(code_lines):
if line.startswith("import vizro.plotly.express"):
if line.startswith("import") or line.startswith("from"):
# Insert the additional import statement right after the first import
code_lines.insert(i + 1, "from vizro import Vizro")
code_lines.insert(i + 2, "import os")
Expand Down Expand Up @@ -80,7 +84,7 @@ def format_output(generated_code):
generated_code = "\n".join(code_lines)
generated_code += "\napp = Vizro().build(model)\n"
generated_code += '\nif __name__ == "__main__":\n'
generated_code += " app.run(port=8051)\n"
generated_code += f" app.run(host='0.0.0.0', port={port})\n"

return generated_code

Expand All @@ -94,3 +98,57 @@ def find_available_port(base_port=8051):
while not check_available_port(base_port):
base_port += 1
return base_port

def kill_process_on_port(port):
"""
Kills any process and its children running on the specified TCP port.
Uses lsof to find the process and kills it with SIGTERM followed by SIGKILL if necessary.
Args:
port (int): The port number to kill
Returns:
bool: True if process was killed successfully, False otherwise
"""
try:
# Find process ID using lsof
cmd = f"lsof -ti :{port}"
pids = subprocess.check_output(cmd, shell=True).decode().strip().split('\n')

if not pids or pids[0] == '':
print(f"No process found running on port {port}")
return False

# Kill each process
for pid in pids:
pid = int(pid)
try:
# First try SIGTERM
os.kill(pid, signal.SIGTERM)
print(f"Sent SIGTERM to process {pid}")

# Give it a moment to terminate
time.sleep(2)

# Check if it's still running
try:
os.kill(pid, 0)
# If we get here, process is still running - use SIGKILL
print(f"Process {pid} still running, sending SIGKILL")
os.kill(pid, signal.SIGKILL)
except ProcessLookupError:
# Process is already gone
pass

except ProcessLookupError:
continue

print(f"Successfully killed all processes on port {port}")
return True

except subprocess.CalledProcessError:
print(f"No process found running on port {port}")
return False
except Exception as e:
print(f"Error: {str(e)}")
return False
13 changes: 8 additions & 5 deletions vizro-ai/examples/dashboard_ui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import dash_bootstrap_components as dbc
import vizro.models as vm
from _utils import find_available_port, format_output
from _utils import find_available_port, format_output, kill_process_on_port
from actions import data_upload_action, display_filename, save_files
from components import (
CodeClipboard,
Expand All @@ -25,9 +25,7 @@

SUPPORTED_MODELS = [
"gpt-4o-mini",
"gpt-4",
"gpt-4-turbo",
"gpt-3.5-turbo",
"gpt-4o",
]
vm.Container.add_type("components", UserUpload)
Expand Down Expand Up @@ -235,7 +233,10 @@ def save_to_file(generated_code):
gen_ai_file = "output_files/run_vizro_ai_output.py"

# format code
generated_code = format_output(generated_code)
kill_process_on_port(8051)
port = 8051
# port = find_available_port()
generated_code = format_output(generated_code, port)

if generated_code:
with open(gen_ai_file, "w") as f:
Expand All @@ -252,7 +253,9 @@ def show_button(ai_response):
"""Displays a button to launch the dashboard in a subprocess."""
if not ai_response:
raise PreventUpdate
port = find_available_port()
kill_process_on_port(8051)
port = 8051
# port = find_available_port()
subprocess.Popen(["python", "output_files/run_vizro_ai_output.py", str(port)])
href = f"http://localhost:{port}/"
return {}, href
Expand Down
2 changes: 1 addition & 1 deletion vizro-ai/examples/dashboard_ui/requirements.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
gunicorn
vizro-ai>=0.2.1
black
jupyter
jupyter

0 comments on commit 74a3a2d

Please sign in to comment.