Skip to content

Commit

Permalink
Reorganizes hamilton ui to be in its own package
Browse files Browse the repository at this point in the history
This is much cleaner/decoupled from the hamilton core capabilities.

See README for installation/publishing instructions.
  • Loading branch information
elijahbenizzy committed Jun 13, 2024
1 parent fbae515 commit 3f96279
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 33 deletions.
2 changes: 0 additions & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
include requirements.txt
include requirements-test.txt
include hamilton/server/requirements-mini.txt
recursive-include hamilton/server/build *
include LICENSE
include *.md
14 changes: 12 additions & 2 deletions hamilton/cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,21 @@ def ui(
port: int = 8241,
base_dir: str = os.path.join(Path.home(), ".hamilton", "db"),
no_migration: bool = False,
no_open: bool = False,
):
"""Runs the Hamilton UI on sqllite in port 8241"""
from hamilton.server import commands
try:
from hamilton_ui import commands
except ImportError:
logger.error(
"hamilton[ui] not installed -- you have to install this to run the UI. "
'Run `pip install "sf-hamilton[ui]"` to install and get started with the UI!'
)
raise typer.Exit(code=1)

ctx.invoke(commands.run, port=port, base_dir=base_dir, no_migration=no_migration)
ctx.invoke(
commands.run, port=port, base_dir=base_dir, no_migration=no_migration, no_open=no_open
)


if __name__ == "__main__":
Expand Down
1 change: 0 additions & 1 deletion hamilton/server

This file was deleted.

2 changes: 1 addition & 1 deletion hamilton/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = (1, 65, 0, "rc1")
VERSION = (1, 66, 0, "rc1")
11 changes: 1 addition & 10 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,6 @@ def load_requirements():
return list(requirements)


def load_server_requirements():
# TODO -- confirm below works/delete this
requirements = {"click", "loguru", "requests", "typer"}
with open("hamilton/server/requirements-mini.txt") as f:
requirements.update(line.strip() for line in f)
return list(requirements)


setup(
name="sf-hamilton", # there's already a hamilton in pypi
version=VERSION,
Expand Down Expand Up @@ -105,13 +97,12 @@ def load_server_requirements():
"diskcache": ["diskcache"],
"cli": ["typer"],
"sdk": ["sf-hamilton-sdk"],
"ui": load_server_requirements(),
"ui": ["sf-hamilton-ui"],
},
entry_points={
"console_scripts": [
"h_experiments = hamilton.plugins.h_experiments.__main__:main",
"hamilton = hamilton.cli.__main__:cli",
"hamilton-serve = hamilton.server.__main__:run",
"hamilton-admin-build-ui = hamilton.admin:build_ui",
"hamilton-admin-build-and-publish = hamilton.admin:build_and_publish",
]
Expand Down
20 changes: 14 additions & 6 deletions ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,19 @@ on local mode. The frontend is a simple React application. There are a few authe
but the default is to use local/unauthenticated (open). Please talk to us if you have a need for more custom authentication.


## License
## Development

There are a few directories that are not licensed under the BSD-3 Clear Clause license. These are:
* frontend/src/ee
* backend/server/trackingserver_auth
The structure involves a bit of cleverness to ensure the UI can easily be deployed and served from the CLI.

See the main repository [LICENSE](../LICENSE) for details, else the LICENSE file in the respective directories
mentioned above.
We have a symlink from `backend/hamilton_ui` to `backend/server`, allowing us to work with django's structure
while simultaneously allowing for import as hamilton_ui. (this should probably be changed at some point but not worth it now).

To deploy, use the `admin.py` script in the UI directory.

This:

1. Builds the frontend
2. Copies it into the build/ directory
3. Publishes to the [sf-hamilton-ui](https://pypi.org/project/sf-hamilton-ui/) package on pypi

Then you'll run it with `hamilton ui` after installing `sf-hamilton[ui]`.
13 changes: 7 additions & 6 deletions hamilton/admin.py → ui/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ def _build_ui():
# building the UI
cmd = "npm run build --prefix ui/frontend"
_command(cmd, capture_output=False)
# wipring the old build if it exists
cmd = "rm -rf hamilton/server/build"
# wiping the old build if it exists
cmd = "rm -rf ui/backend/build"
_command(cmd, capture_output=False)
cmd = "cp -R ui/frontend/build hamilton/server/build"
cmd = "cp -R ui/frontend/build ui/backend/server/build"
_command(cmd, capture_output=False)


Expand All @@ -87,9 +87,10 @@ def build_ui():
@click.option("--no-wipe-dist", is_flag=True, help="Wipe the dist/ directory before building")
def build_and_publish(prod: bool, no_wipe_dist: bool):
git_root = _get_git_root()
with cd(git_root):
logger.info("Building UI -- this may take a bit...")
_build_ui()
install_path = os.path.join(git_root, "ui/backend")
logger.info("Building UI -- this may take a bit...")
# build_ui.callback() # use the underlying function, not click's object
with cd(install_path):
logger.info("Built UI!")
if not no_wipe_dist:
logger.info("Wiping dist/ directory for a clean publish.")
Expand Down
4 changes: 4 additions & 0 deletions ui/backend/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include hamilton_ui/requirements-mini.txt
recursive-include hamilton_ui/build *
include ../../LICENSE
include *.md
1 change: 1 addition & 0 deletions ui/backend/hamilton_ui
36 changes: 31 additions & 5 deletions ui/backend/server/commands.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import os
import sys
import threading
import time
import webbrowser
from contextlib import contextmanager

import hamilton_ui
import requests
from django.core.management import execute_from_command_line

import hamilton


@contextmanager
def extend_sys_path(path):
Expand Down Expand Up @@ -40,13 +43,36 @@ def set_env_variables(vars: dict):
os.environ[key] = original_values[key]


def run(port: int, base_dir: str, no_migration: bool):
def _open_when_ready(check_url: str, open_url: str):
while True:
try:
response = requests.get(check_url)
if response.status_code == 200:
webbrowser.open(open_url)
return
else:
pass
except requests.exceptions.RequestException:
pass
time.sleep(0.1)


def run(port: int, base_dir: str, no_migration: bool, no_open: bool):
env = {
"DJANGO_SETTINGS_MODULE": "hamilton.server.server.settings_mini",
"HAMILTON_BASE_DIR": base_dir,
}
if not no_open:
thread = threading.Thread(
target=_open_when_ready,
kwargs={
"open_url": (open_url := f"http://localhost:{port}"),
"check_url": f"{open_url}/api/v0/health",
},
daemon=True,
)
thread.start()
with set_env_variables(env):
with extend_sys_path(os.path.join(*hamilton.__path__, "server")):
with extend_sys_path(hamilton_ui.__path__[0]):
if not no_migration:
execute_from_command_line(
["manage.py", "migrate", "--settings=server.settings_mini"]
Expand Down
9 changes: 9 additions & 0 deletions ui/backend/server/trackingserver_base/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,12 @@ async def get_node_metadata_types(
) -> AllNodeMetadataTypes:
error = HttpError(status_code=400, message="This only exists to populate the openAPI schema.")
raise error


@router.get(
"/v1/health",
tags=["health"],
response=bool,
)
def health_check(request):
return True
54 changes: 54 additions & 0 deletions ui/backend/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""The setup script."""

from setuptools import find_packages, setup

REQUIREMENTS_FILES = ["requirements.txt"]


def load_requirements():
# TODO -- confirm below works/delete this
requirements = {"click", "loguru", "requests", "typer"}
with open("hamilton_ui/requirements-mini.txt") as f:
requirements.update(line.strip() for line in f)
return list(requirements)


setup(
name="sf-hamilton-ui", # there's already a hamilton in pypi
version="0.0.3",
description="Hamilton, the micro-framework for creating dataframes.",
long_description="""Hamilton tracking server, see [the docs for more](https://github.com/dagworks-inc/hamilton/tree/main/ui/)""",
long_description_content_type="text/markdown",
author="Stefan Krawczyk, Elijah ben Izzy",
author_email="[email protected],[email protected]",
url="https://github.com/dagworks-inc/hamilton",
packages=find_packages(exclude=["tests"], include=["hamilton_ui", "hamilton_ui.*"]),
include_package_data=True,
install_requires=load_requirements(),
zip_safe=False,
keywords="hamilton",
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Natural Language :: English",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
],
# Note that this feature requires pep8 >= v9 and a version of setup tools greater than the
# default version installed with virtualenv. Make sure to update your tools!
python_requires=">=3.6, <4",
# adding this to slim the package down, since these dependencies are only used in certain contexts.
# Relevant project URLs
project_urls={ # Optional
"Bug Reports": "https://github.com/dagworks-inc/hamilton/issues",
"Source": "https://github.com/dagworks-inc/hamilton",
},
)

0 comments on commit 3f96279

Please sign in to comment.