diff options
| -rw-r--r-- | Dockerfile (renamed from Dockerfile.heroku) | 11 | ||||
| -rw-r--r-- | README.rst | 8 | ||||
| -rw-r--r-- | VERSION | 2 | ||||
| -rw-r--r-- | heroku.yml | 7 | ||||
| -rw-r--r-- | requirements.pip.dev.txt | 2 | ||||
| -rw-r--r-- | requirements.pip.runtime.txt | 2 | ||||
| -rw-r--r-- | src_c/opcut.c | 12 | ||||
| -rw-r--r-- | src_doit/__init__.py | 9 | ||||
| -rw-r--r-- | src_doit/dist/__init__.py | 38 | ||||
| -rw-r--r-- | src_doit/dist/container/Dockerfile | 6 | ||||
| -rw-r--r-- | src_doit/dist/windows/opcut-server.cmd (renamed from src_doit/dist/windows/opcut-server.bat) | 0 | ||||
| -rw-r--r-- | src_doit/dist/windows/opcut.cmd (renamed from src_doit/dist/windows/opcut.bat) | 0 | ||||
| -rw-r--r-- | src_js/states.js | 2 | ||||
| -rw-r--r-- | src_py/opcut/common.py | 11 | ||||
| -rw-r--r-- | src_py/opcut/server.py | 33 |
15 files changed, 56 insertions, 87 deletions
diff --git a/Dockerfile.heroku b/Dockerfile index 03ca85f..e01a1b2 100644 --- a/Dockerfile.heroku +++ b/Dockerfile @@ -1,15 +1,16 @@ -FROM python:3.9-slim-bullseye as opcut-base +FROM python:3.10-slim-bullseye as opcut-base WORKDIR /opcut RUN apt update -qy && \ apt install -qy pkg-config gcc libcairo2-dev FROM opcut-base as opcut-build WORKDIR /opcut -COPY . . RUN apt install -qy nodejs yarnpkg git gcc-mingw-w64-x86-64-win32 && \ ln -sT /usr/bin/yarnpkg /usr/bin/yarn && \ - ln -sT /usr/bin/x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-cc && \ - pip install -qq -r requirements.pip.dev.txt && \ + ln -sT /usr/bin/x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-cc +COPY . . +RUN pip install -qq -r requirements.pip.dev.txt && \ + doit clean_all && \ doit FROM opcut-base as opcut-run @@ -17,3 +18,5 @@ WORKDIR /opcut COPY --from=opcut-build /opcut/build/py/dist/*.whl . RUN pip install -qq *.whl && \ rm *.whl +EXPOSE 8080 +CMD ["/usr/local/bin/opcut", "server"] @@ -1,7 +1,7 @@ opcut ===== -`https://opcut.herokuapp.com/` +`https://opcut.kopic.xyz/`_ `opcut` is cutting stock problem optimizer (`https://en.wikipedia.org/wiki/Cutting_stock_problem`) utilizing multiple @@ -14,6 +14,11 @@ panels and guillotine cuts (end-to-end cuts). This project includes: Git repository is available at `<https://github.com/bozokopic/opcut.git>`_. +Public instance `https://opcut.kopic.xyz/`_ is constrained with limited +resources and should be used only for functionality evaluation purposes. +In case of complex and repetitive calculations, please consider running +self hosted instance. + Runtime requirements -------------------- @@ -133,6 +138,7 @@ creates wheel package inside `build` directory. TODO ---- +* ui svg rewrite * unit tests * changelog * alternative hosting (https://opcut.herokuapp.com is shutting down) @@ -1 +1 @@ -0.4.0-dev +0.4.0 diff --git a/heroku.yml b/heroku.yml deleted file mode 100644 index 71420a4..0000000 --- a/heroku.yml +++ /dev/null @@ -1,7 +0,0 @@ -build: - docker: - web: - dockerfile: Dockerfile.heroku - target: opcut-run -run: - web: opcut server --port $PORT diff --git a/requirements.pip.dev.txt b/requirements.pip.dev.txt index 33d4612..1efce28 100644 --- a/requirements.pip.dev.txt +++ b/requirements.pip.dev.txt @@ -1,3 +1,3 @@ -r requirements.pip.runtime.txt -hat-doit ~= 0.11.3 +hat-doit ~= 0.11.4 diff --git a/requirements.pip.runtime.txt b/requirements.pip.runtime.txt index 9d02fa1..16bcd3e 100644 --- a/requirements.pip.runtime.txt +++ b/requirements.pip.runtime.txt @@ -1,4 +1,4 @@ aiohttp ~= 3.8.1 hat-aio ~= 0.7.0 -hat-json ~= 0.5.8 +hat-json ~= 0.5.10 pycairo ~= 1.21.0 diff --git a/src_c/opcut.c b/src_c/opcut.c index f60e236..5e4205a 100644 --- a/src_c/opcut.c +++ b/src_c/opcut.c @@ -131,7 +131,8 @@ static void free_unused_until(opcut_allocator_t *a, opcut_unused_t *unused, } -static inline double compare_item(opcut_params_t *params, size_t id1, size_t id2) { +static inline double compare_item(opcut_params_t *params, size_t id1, + size_t id2) { return params->items[id1].area - params->items[id2].area; } @@ -155,8 +156,8 @@ static inline void swap_ids(size_t *ids, size_t pos1, size_t pos2) { } -static size_t partition_item_ids(opcut_params_t *params, - size_t *item_ids, ssize_t start, ssize_t stop) { +static size_t partition_item_ids(opcut_params_t *params, size_t *item_ids, + ssize_t start, ssize_t stop) { ssize_t pivot = start - 1; for (size_t i = start; i < stop; ++i) if (compare_item(params, item_ids[i], item_ids[stop]) >= 0) @@ -168,7 +169,7 @@ static size_t partition_item_ids(opcut_params_t *params, static void sort_item_ids(opcut_params_t *params, size_t *item_ids, - ssize_t start, ssize_t stop) { + ssize_t start, ssize_t stop) { if (start >= stop || stop < 0) return; @@ -215,7 +216,6 @@ static void insert_unused(opcut_unused_t **list, opcut_unused_t *el) { } - static size_t *create_initial_item_ids(opcut_allocator_t *a, opcut_params_t *params) { if (!params->items_len) @@ -307,8 +307,6 @@ static void calculate_fitness(opcut_params_t *params, result_t *result, } - - static inline bool item_fits_unused(opcut_item_t *item, opcut_unused_t *unused, bool rotate) { if (rotate && !item->can_rotate) diff --git a/src_doit/__init__.py b/src_doit/__init__.py index c25a42c..2639ffe 100644 --- a/src_doit/__init__.py +++ b/src_doit/__init__.py @@ -44,10 +44,11 @@ json_schema_repo_path = src_py_dir / 'opcut/json_schema_repo.json' def task_clean_all(): """Clean all""" - return {'actions': [(common.rm_rf, [build_dir, - ui_dir, - json_schema_repo_path, - src_py_dir / 'opcut/bin'])]} + return {'actions': [(common.rm_rf, [ + build_dir, + ui_dir, + json_schema_repo_path, + *src_py_dir.glob('opcut/_libopcut.*')])]} def task_wheel(): diff --git a/src_doit/dist/__init__.py b/src_doit/dist/__init__.py index 6c490fa..51add24 100644 --- a/src_doit/dist/__init__.py +++ b/src_doit/dist/__init__.py @@ -6,8 +6,7 @@ from hat.doit import common __all__ = ['task_dist', - 'task_dist_windows', - 'task_dist_container'] + 'task_dist_windows'] package_path = Path(__file__).parent @@ -18,9 +17,8 @@ cache_dir = Path('cache') wheel_dir = build_dir / 'py/dist' dist_dir = build_dir / 'dist' dist_windows_dir = dist_dir / f'opcut-{common.get_version()}-windows' -dist_container_dir = dist_dir / f'opcut-{common.get_version()}-container' -win_python_url = 'https://www.python.org/ftp/python/3.9.7/python-3.9.7-embed-amd64.zip' # NOQA +win_python_url = 'https://www.python.org/ftp/python/3.10.7/python-3.10.7-embed-amd64.zip' # NOQA cache_win_python_path = cache_dir / win_python_url.split('/')[-1] @@ -28,8 +26,7 @@ def task_dist(): """Build distribution""" return {'actions': None, - 'task_dep': ['dist_windows', - 'dist_container']} + 'task_dep': ['dist_windows']} def task_dist_windows(): @@ -52,14 +49,14 @@ def task_dist_windows(): with zipfile.ZipFile(str(cache_win_python_path)) as f: f.extractall(str(python_dir)) - python_lib_path = python_dir / 'python39.zip' + python_lib_path = python_dir / 'python310.zip' python_lib_dir = python_dir / 'lib' common.mkdir_p(dist_windows_dir / 'python/lib') with zipfile.ZipFile(str(python_lib_path)) as f: f.extractall(str(python_lib_dir)) common.rm_rf(python_lib_path) - (python_dir / 'python39._pth').write_text( + (python_dir / 'python310._pth').write_text( '..\\packages\n' 'lib\n' '.\n' @@ -87,28 +84,3 @@ def task_dist_windows(): return {'actions': [build], 'task_dep': ['wheel']} - - -def task_dist_container(): - """Build container distribution""" - - def build(): - common.rm_rf(dist_container_dir) - common.mkdir_p(dist_container_dir.parent) - common.cp_r(package_path / 'container', dist_container_dir) - - for i in wheel_dir.glob('*.whl'): - common.cp_r(i, dist_container_dir / i.name) - - name = f'opcut:{common.get_version()}' - img_path = dist_dir / f'{dist_container_dir.name}.tar' - - subprocess.run(['podman', 'build', '-q', '-t', name, '.'], - cwd=str(dist_container_dir), - check=True) - - subprocess.run(['podman', 'save', '-q', '-o', str(img_path), name], - check=True) - - return {'actions': [build], - 'task_dep': ['wheel']} diff --git a/src_doit/dist/container/Dockerfile b/src_doit/dist/container/Dockerfile deleted file mode 100644 index 32978c1..0000000 --- a/src_doit/dist/container/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM python:3.9-slim-bullseye -COPY *.whl . -RUN apt update -qy && \ - apt install -qy pkg-config gcc libcairo2-dev && \ - pip install -qq *.whl && \ - rm *.whl diff --git a/src_doit/dist/windows/opcut-server.bat b/src_doit/dist/windows/opcut-server.cmd index 25ce4f9..25ce4f9 100644 --- a/src_doit/dist/windows/opcut-server.bat +++ b/src_doit/dist/windows/opcut-server.cmd diff --git a/src_doit/dist/windows/opcut.bat b/src_doit/dist/windows/opcut.cmd index 68ae30a..68ae30a 100644 --- a/src_doit/dist/windows/opcut.bat +++ b/src_doit/dist/windows/opcut.cmd diff --git a/src_js/states.js b/src_js/states.js index bc50eb3..5f1599d 100644 --- a/src_js/states.js +++ b/src_js/states.js @@ -1,7 +1,7 @@ export const main = { form: { - method: 'forward_greedy', + method: 'forward_greedy_native', cut_width: 0.3, min_initial_usage: true, panels: [], diff --git a/src_py/opcut/common.py b/src_py/opcut/common.py index 5f6f26b..0bda8c5 100644 --- a/src_py/opcut/common.py +++ b/src_py/opcut/common.py @@ -1,5 +1,5 @@ -from pathlib import Path import enum +import importlib.resources import typing from hat import json @@ -7,11 +7,10 @@ from hat import json mm: float = 72 / 25.4 -package_path: Path = Path(__file__).parent - -json_schema_repo: json.SchemaRepository = json.SchemaRepository( - json.json_schema_repo, - json.SchemaRepository.from_json(package_path / 'json_schema_repo.json')) +with importlib.resources.path(__package__, 'json_schema_repo.json') as _path: + json_schema_repo: json.SchemaRepository = json.SchemaRepository( + json.json_schema_repo, + json.SchemaRepository.from_json(_path)) class Panel(typing.NamedTuple): diff --git a/src_py/opcut/server.py b/src_py/opcut/server.py index 939222c..379a571 100644 --- a/src_py/opcut/server.py +++ b/src_py/opcut/server.py @@ -1,5 +1,6 @@ -from pathlib import Path import asyncio +import contextlib +import importlib.resources import subprocess import sys @@ -10,27 +11,29 @@ import aiohttp.web from opcut import common -static_dir: Path = common.package_path / 'ui' - - async def create(host: str, port: int ) -> 'Server': server = Server() server._async_group = aio.Group() - app = aiohttp.web.Application() - app.add_routes([ - aiohttp.web.get('/', server._root_handler), - aiohttp.web.post('/calculate', server._calculate_handler), - aiohttp.web.post('/generate', server._generate_handler), - aiohttp.web.static('/', static_dir)]) - - runner = aiohttp.web.AppRunner(app) - await runner.setup() - server.async_group.spawn(aio.call_on_cancel, runner.cleanup) - try: + exit_stack = contextlib.ExitStack() + static_dir = exit_stack.enter_context( + importlib.resources.path(__package__, 'ui')) + server.async_group.spawn(aio.call_on_cancel, exit_stack.close) + + app = aiohttp.web.Application() + app.add_routes([ + aiohttp.web.get('/', server._root_handler), + aiohttp.web.post('/calculate', server._calculate_handler), + aiohttp.web.post('/generate', server._generate_handler), + aiohttp.web.static('/', static_dir)]) + + runner = aiohttp.web.AppRunner(app) + await runner.setup() + server.async_group.spawn(aio.call_on_cancel, runner.cleanup) + site = aiohttp.web.TCPSite(runner=runner, host=host, port=port, |
