├── .codeclimate.yml ├── .coveragerc ├── .dockerignore ├── .gitattributes ├── .github └── workflows │ ├── codecov.yml │ ├── docker.yml │ ├── release.yml │ └── unittests.yml ├── .gitignore ├── .gitmodules ├── .landscape.yml ├── .readthedocs.yaml ├── .style.yapf ├── Dockerfile ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── Procfile ├── README.rst ├── app.json ├── bin ├── codeclimate ├── pre_compile └── yapf.sh ├── business_logic ├── __init__.py ├── admin.py ├── apps.py ├── blockly │ ├── __init__.py │ ├── build.py │ ├── create.py │ ├── data.py │ ├── exceptions.py │ └── parse.py ├── config.py ├── exceptions.py ├── fields.py ├── locale │ └── ru │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models │ ├── __init__.py │ ├── assignment.py │ ├── constant.py │ ├── context.py │ ├── foreach.py │ ├── frame.py │ ├── function.py │ ├── ifstatement.py │ ├── log.py │ ├── node.py │ ├── operator_.py │ ├── program.py │ ├── reference.py │ ├── stop.py │ ├── table.py │ ├── types_.py │ └── variable.py ├── rest │ ├── __init__.py │ ├── serializers.py │ ├── urls.py │ └── views.py ├── signals.py ├── static │ └── business_logic │ │ ├── blockly │ │ ├── 1x1.gif │ │ ├── accessible.css │ │ ├── click.mp3 │ │ ├── click.ogg │ │ ├── click.wav │ │ ├── delete.mp3 │ │ ├── delete.ogg │ │ ├── delete.wav │ │ ├── disconnect.mp3 │ │ ├── disconnect.ogg │ │ ├── disconnect.wav │ │ ├── handclosed.cur │ │ ├── handdelete.cur │ │ ├── handopen.cur │ │ ├── quote0.png │ │ ├── quote1.png │ │ ├── sprites.png │ │ └── sprites.svg │ │ ├── index.html │ │ ├── main.bundle.js │ │ ├── main.bundle.map │ │ ├── polyfills.bundle.js │ │ ├── polyfills.bundle.map │ │ ├── src │ │ └── assets │ │ │ └── fonts │ │ │ ├── icons.eot │ │ │ ├── icons.ttf │ │ │ ├── icons.woff │ │ │ └── icons.woff2 │ │ ├── vendor.bundle.js │ │ └── vendor.bundle.map ├── urls.py ├── utils.py └── validators.py ├── docker-compose.yml ├── docs ├── Makefile ├── architecture │ ├── constants.rst │ ├── context.rst │ ├── environment.rst │ ├── exceptions.rst │ ├── functions.rst │ ├── index.rst │ ├── logging.rst │ ├── node.rst │ ├── operands.rst │ ├── operators.rst │ ├── program.rst │ ├── references.rst │ ├── signals.rst │ ├── statements.rst │ └── variables.rst ├── caveats.rst ├── conf.py ├── configuration.rst ├── credits.rst ├── demo.rst ├── development.rst ├── index.rst ├── installation.rst ├── license.rst ├── make.bat ├── requirements.in ├── requirements.rst ├── requirements.txt ├── roadmap.rst ├── screenshots.rst ├── static │ ├── gif │ │ └── animate.sh │ ├── screenshots │ │ ├── editing.png │ │ ├── log.png │ │ └── python-function.png │ ├── staruml.mdj │ └── uml │ │ ├── ExecutionEnvironment.svg │ │ ├── Function.svg │ │ ├── Program.svg │ │ └── Variable.svg ├── visual-programming.rst └── watch.sh ├── frontend ├── .editorconfig ├── .github │ ├── CONTRIBUTING.md │ ├── ISSUE_TEMPLATE.md │ └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .nvmrc ├── .travis.yml ├── .vscode │ ├── launch.json │ └── settings.json ├── Dockerfile ├── LICENSE ├── README.md ├── blockly │ ├── blockly_compressed.js │ ├── blockly_uncompressed.js │ └── blocks_compressed.js ├── config │ ├── github-deploy │ │ └── index.js │ ├── head-config.common.js │ ├── helpers.js │ ├── html-elements-plugin │ │ └── index.js │ ├── karma.conf.js │ ├── modules │ │ └── angular2-hmr-prod.js │ ├── protractor.conf.js │ ├── spec-bundle.js │ ├── webpack.common.js │ ├── webpack.dev.js │ ├── webpack.github-deploy.js │ ├── webpack.prod.js │ └── webpack.test.js ├── d_ts │ ├── blockly.d.ts │ ├── msg.d.ts │ └── semantic.d.ts ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── semantic.json ├── src │ ├── app │ │ ├── actions │ │ │ ├── execution.ts │ │ │ ├── info.ts │ │ │ ├── prInterfaceList.ts │ │ │ ├── programList.ts │ │ │ ├── referenceList.ts │ │ │ └── versionList.ts │ │ ├── app.component.css │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── app.resolver.ts │ │ ├── app.routes.ts │ │ ├── app.service.ts │ │ ├── blocks │ │ │ ├── blocks.service.ts │ │ │ ├── consts │ │ │ │ ├── blockTypes.ts │ │ │ │ └── colors.ts │ │ │ └── fields │ │ │ │ ├── argument_field.ts │ │ │ │ ├── func_label_field.ts │ │ │ │ ├── ref_dropdown_field.ts │ │ │ │ └── ref_label_field.ts │ │ ├── components │ │ │ ├── blockly │ │ │ │ ├── blockly-toolset.html │ │ │ │ ├── blockly.component.spec.ts │ │ │ │ ├── blockly.component.ts │ │ │ │ └── blocklyReadOnly.component.ts │ │ │ ├── breadcrumb.component.ts │ │ │ ├── helpcard.component.ts │ │ │ ├── list.component.ts │ │ │ ├── modal.component.ts │ │ │ └── no-content │ │ │ │ └── no-content.component.ts │ │ ├── css │ │ │ ├── breadcrumb.component.css │ │ │ ├── helpcard.component.css │ │ │ ├── list.component.css │ │ │ └── semantic.css │ │ ├── environment.ts │ │ ├── index.ts │ │ ├── pages │ │ │ ├── EditorPage.ts │ │ │ ├── ExecutionListPage.ts │ │ │ ├── HomePage.ts │ │ │ ├── InterfaceListPage.ts │ │ │ ├── ProgramListPage.ts │ │ │ ├── ReadonlyEditorPage.ts │ │ │ └── VersionListPage.ts │ │ ├── reducers │ │ │ ├── executionList.reducer.ts │ │ │ ├── index.ts │ │ │ ├── info.reducer.ts │ │ │ ├── prInterfaceList.reducer.ts │ │ │ ├── programList.reducer.ts │ │ │ ├── referenceList.reducer.ts │ │ │ └── versionList.reducer.ts │ │ └── services │ │ │ ├── breadcrumb.service.ts │ │ │ ├── fetch.service.ts │ │ │ ├── post.service.ts │ │ │ ├── rest.service.ts │ │ │ ├── state.service.ts │ │ │ └── xmlGenerator.service.ts │ ├── assets │ │ ├── css │ │ │ └── .gitkeep │ │ ├── data.json │ │ ├── fonts │ │ │ ├── icons.eot │ │ │ ├── icons.otf │ │ │ ├── icons.svg │ │ │ ├── icons.ttf │ │ │ ├── icons.woff │ │ │ └── icons.woff2 │ │ ├── humans.txt │ │ ├── icon │ │ │ ├── browserconfig.xml │ │ │ └── favicon.ico │ │ ├── images │ │ │ └── flags.png │ │ ├── img │ │ │ ├── angular-logo.png │ │ │ ├── angularclass-avatar.png │ │ │ └── angularclass-logo.png │ │ ├── mock-data │ │ │ └── mock-data.json │ │ ├── robots.txt │ │ └── service-worker.js │ ├── custom-typings.d.ts │ ├── index.html │ ├── main.browser.ts │ ├── polyfills.browser.ts │ ├── tests │ │ ├── app.e2e.ts │ │ ├── app.spec.ts │ │ ├── blocks.spec.ts │ │ └── mock.service.ts │ └── vendor.browser.ts ├── tsconfig.json ├── tslint.json ├── typedoc.json └── webpack.config.js ├── makemigration.sh ├── manage.py ├── noxfile.py ├── requirements.dev.txt ├── requirements.heroku.txt ├── requirements.test.txt ├── requirements.txt ├── resetdb.sh ├── setup.cfg ├── setup.py ├── sites ├── __init__.py ├── dev │ ├── __init__.py │ ├── books │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── static │ │ │ └── books │ │ │ │ ├── favicon-book.ico │ │ │ │ └── theme.css │ │ ├── templates │ │ │ └── books │ │ │ │ ├── author_list.html │ │ │ │ ├── base.html │ │ │ │ ├── book_detail.html │ │ │ │ ├── book_list.html │ │ │ │ ├── form.html │ │ │ │ ├── publisher_detail.html │ │ │ │ └── publisher_list.html │ │ ├── urls.py │ │ └── views.py │ ├── fixtures │ │ ├── data.json │ │ └── dumpdata.sh │ ├── heroku │ │ ├── __init__.py │ │ ├── settings.py │ │ └── wsgi.py │ ├── settings.py │ ├── templates │ │ ├── admin │ │ │ ├── base.html │ │ │ └── login.html │ │ ├── ga.html │ │ └── rest_framework │ │ │ └── api.html │ ├── urls.py │ ├── utils │ │ ├── __init__.py │ │ └── staticfiles │ │ │ ├── __init__.py │ │ │ └── finders.py │ └── wsgi.py ├── settings.py └── test │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── tests ├── __init__.py ├── blockly ├── __init__.py ├── common.py ├── test_build.py ├── test_create.py └── test_parse.py ├── common.py ├── rest ├── __init__.py ├── common.py ├── test_log.py ├── test_program_interface.py ├── test_program_version.py ├── test_reference.py └── test_urls.py ├── test_app ├── __init__.py ├── apps.py └── models.py ├── test_assignment.py ├── test_constant.py ├── test_context.py ├── test_frame.py ├── test_function.py ├── test_ifstatement.py ├── test_log.py ├── test_node.py ├── test_operator.py ├── test_program.py ├── test_reference.py ├── test_signals.py ├── test_stop.py ├── test_variable.py └── utils.py /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - python 8 | exclude_paths: 9 | - business_logic/models/constant.py 10 | - business_logic/models/stop.py 11 | - business_logic/models/table.py 12 | pep8: 13 | enabled: true 14 | fixme: 15 | enabled: true 16 | exclude_paths: 17 | - README.md 18 | 19 | radon: 20 | enabled: true 21 | config: 22 | threshold: "C" 23 | ratings: 24 | paths: 25 | - "business_logic/**.py" 26 | exclude_paths: 27 | - "**/migrations/*.py" 28 | - "business_logic/static/**.*" 29 | - .idea/ 30 | - .tox/ 31 | - bin/ 32 | - build/ 33 | - dist/ 34 | - frontend/ 35 | - sites/ 36 | - tests/ 37 | - venv/ 38 | - virtualenv/ 39 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | include= 3 | business_logic/* 4 | omit= 5 | business_logic/migrations/* 6 | 7 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !business_logic 3 | !docs/requirements.txt 4 | !manage.py 5 | !README.* 6 | !requirements.* 7 | !setup.* 8 | !sites 9 | !tests 10 | sites/dev/db.sqlite3 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | frontend/* linguist-vendored=false 2 | frontend/src/* linguist-vendored 3 | -------------------------------------------------------------------------------- /.github/workflows/codecov.yml: -------------------------------------------------------------------------------- 1 | name: codecov 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - develop 8 | 9 | jobs: 10 | main: 11 | runs-on: ubuntu-latest 12 | 13 | env: 14 | DJANGO_SETTINGS_MODULE: sites.test.settings 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | - name: Set up Python 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: '3.10' 24 | 25 | - name: Install dependencies 26 | run: | 27 | python -m pip install --upgrade pip 28 | python -m pip install "Django>=4.2,<4.3" 29 | python -m pip install -r requirements.test.txt 30 | python -m pip install codecov 31 | 32 | - name: Test 33 | run: | 34 | python -m pip install "Django>=4.2,<4.3" 35 | python -m pip install -r requirements.test.txt 36 | python -m pip install codecov 37 | 38 | - name: Run tests and collect coverage 39 | run: | 40 | coverage run manage.py test tests 41 | 42 | - name: Upload coverage to Codecov 43 | uses: codecov/codecov-action@v3 44 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: build docker demo image 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | docker: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | 15 | - name: Prepare build-args 16 | id: build-args 17 | run: | 18 | echo BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` >> $GITHUB_OUTPUT 19 | echo VCS_REF=`git rev-parse --short HEAD` >> $GITHUB_OUTPUT 20 | echo VERSION=`python -c 'from __future__ import print_function; import business_logic; print(business_logic.__version__)'` >> $GITHUB_OUTPUT 21 | 22 | - name: Set up QEMU 23 | uses: docker/setup-qemu-action@v3 24 | 25 | - name: Set up Docker Buildx 26 | uses: docker/setup-buildx-action@v3 27 | 28 | - name: Login to Docker Hub 29 | uses: docker/login-action@v3 30 | with: 31 | username: ${{ secrets.DOCKERHUB_USERNAME }} 32 | password: ${{ secrets.DOCKERHUB_TOKEN }} 33 | 34 | - name: Build and push 35 | uses: docker/build-push-action@v5 36 | with: 37 | build-args: | 38 | VCS_REF=${{ steps.build-args.outputs.VCS_REF }} 39 | BUILD_DATE=${{ steps.build-args.outputs.BUILD_DATE }} 40 | VERSION=${{ steps.build-args.outputs.VERSION }} 41 | context: . 42 | platforms: linux/amd64,linux/arm64 43 | push: true 44 | tags: dgksu/django-business-logic:demo 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '[0-9]+.[0-9]+.[0-9]+' 7 | 8 | jobs: 9 | main: 10 | runs-on: ubuntu-latest 11 | 12 | environment: 13 | name: pypi 14 | url: https://pypi.org/p/django-business-logic 15 | 16 | permissions: 17 | id-token: write 18 | contents: write 19 | 20 | steps: 21 | 22 | - name: Checkout code 23 | uses: actions/checkout@v2 24 | 25 | - name: Set up Python 3.10 26 | uses: actions/setup-python@v4 27 | with: 28 | python-version: '3.10' 29 | 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | python -m pip install wheel 34 | 35 | - name: Build package 36 | run: | 37 | python setup.py sdist bdist_wheel 38 | 39 | - name: Publish package distributions to PyPI 40 | uses: pypa/gh-action-pypi-publish@release/v1 41 | with: 42 | password: ${{ secrets.PYPI_API_TOKEN }} 43 | 44 | - name: Create Release 45 | id: create_release 46 | uses: comnoco/create-release-action@v2.0.5 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | with: 50 | tag_name: ${{ github.ref }} 51 | release_name: Release ${{ github.ref_name }} 52 | body: | 53 | Pypi release: https://pypi.org/project/django-business-logic/${{ github.ref_name }}/ 54 | draft: false 55 | prerelease: false 56 | -------------------------------------------------------------------------------- /.github/workflows/unittests.yml: -------------------------------------------------------------------------------- 1 | name: unittests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - develop 8 | 9 | jobs: 10 | main: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | python: 16 | - "3.8" 17 | - "3.9" 18 | - "3.10" 19 | - "3.11" 20 | django: 21 | - "3.2" 22 | - "4.0" 23 | - "4.1" 24 | - "4.2" 25 | 26 | env: 27 | DJANGO_SETTINGS_MODULE: sites.test.settings 28 | 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v3 32 | 33 | - name: Set up Python ${{ matrix.python }} 34 | uses: actions/setup-python@v4 35 | with: 36 | python-version: ${{ matrix.python }} 37 | 38 | - name: Set up pip 39 | run: | 40 | python -m pip install --upgrade pip 41 | 42 | - name: Install Django ${{ matrix.django }} 43 | run: | 44 | DJANGO_VERSION_MAJOR=$(echo ${{ matrix.django }} | cut -d. -f1) 45 | DJANGO_VERSION_NEXT_MINOR=$(($(echo ${{ matrix.django }} | cut -d. -f2) + 1)) 46 | python -m pip install "Django>=${{ matrix.django }},<$DJANGO_VERSION_MAJOR.$DJANGO_VERSION_NEXT_MINOR" 47 | 48 | - name: Install django-admin-sortable2 for Django 3.2 49 | if: ${{ matrix.django == '3.2' }} 50 | run: | 51 | python -m pip install django-admin-sortable2==1.0.4 52 | 53 | - name: Install dependencies 54 | run: | 55 | python -m pip install -r requirements.test.txt 56 | 57 | - name: Test 58 | run: | 59 | py.test tests 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | /.idea 3 | /venv 4 | /virtualenv* 5 | /.tox 6 | /.cache 7 | *.sqlite3 8 | MANIFEST 9 | build/ 10 | dist/ 11 | *.egg-info 12 | /.coverage 13 | /htmlcov/ 14 | /business_logic/static/ 15 | /old/ 16 | /django-business-logic-* 17 | 18 | tests/reports/*.xml 19 | .python-version 20 | .aider* 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "frontend/d_ts/closure-library"] 2 | path = frontend/d_ts/closure-library 3 | url = https://github.com/teppeis/closure-library.d.ts.git 4 | -------------------------------------------------------------------------------- /.landscape.yml: -------------------------------------------------------------------------------- 1 | doc-warnings: false 2 | test-warnings: false 3 | strictness: veryhigh 4 | max-line-length: 120 5 | uses: 6 | - django 7 | 8 | requirements: 9 | - requirements.txt 10 | 11 | ignore-paths: 12 | - business_logic/static/ 13 | - .idea/ 14 | - .tox/ 15 | - bin/ 16 | - build/ 17 | - dist/ 18 | - frontend/ 19 | - sites/ 20 | - tests/ 21 | - venv/ 22 | - virtualenv/ 23 | 24 | ignore-patterns: 25 | - .*/migrations/.*\.py$ 26 | 27 | python-targets: 28 | - 2 29 | - 3 -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.11" 7 | 8 | sphinx: 9 | configuration: docs/conf.py 10 | 11 | python: 12 | install: 13 | - requirements: requirements.dev.txt 14 | -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | based_on_style = google 3 | column_limit = 120 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-alpine 2 | 3 | ARG VERSION 4 | ARG VCS_REF 5 | ARG BUILD_DATE 6 | 7 | LABEL org.label-schema.schema-version="1.0" \ 8 | org.label-schema.vcs-url="https://github.com/dgk/django-business-logic/" \ 9 | org.label-schema.version="$VERSION" \ 10 | org.label-schema.vcs-ref="$VCS_REF" \ 11 | org.label-schema.build-date="$BUILD_DATE" 12 | 13 | 14 | RUN mkdir /app 15 | WORKDIR app 16 | 17 | RUN apk add --no-cache libxslt && \ 18 | apk add --no-cache --virtual .build-deps g++ python3-dev libffi-dev openssl-dev libxslt-dev git 19 | 20 | ADD . /app 21 | 22 | RUN pip install --no-cache-dir -r requirements.dev.txt && \ 23 | python setup.py install && \ 24 | apk del .build-deps 25 | 26 | RUN python manage.py migrate && \ 27 | python manage.py loaddata sites/dev/fixtures/data.json 28 | 29 | RUN cat sites/dev/templates/ga.html >> business_logic/static/business_logic/index.html 30 | 31 | EXPOSE 8000 32 | ENTRYPOINT ["python", "manage.py"] 33 | CMD ["runserver", "0.0.0.0:8000"] 34 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2009-2015 Dmitry Kuksinsky and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | include README.rst 3 | include LICENSE.txt 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: build 2 | 3 | DOCKER_IMAGE ?= dgksu/django-business-logic 4 | DOCKER_TAG ?= demo 5 | 6 | .PHONY:build 7 | build: 8 | @docker build \ 9 | --build-arg VCS_REF=`git rev-parse --short HEAD` \ 10 | --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ 11 | --build-arg VERSION=`python -c 'from __future__ import print_function; import business_logic; print(business_logic.__version__)'` \ 12 | -t $(DOCKER_IMAGE):$(DOCKER_TAG) . 13 | 14 | 15 | .PHONY:push 16 | push: 17 | @docker push $(DOCKER_IMAGE):$(DOCKER_TAG) 18 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | release: python manage.py migrate && python manage.py loaddata sites/dev/fixtures/data.json 2 | web: gunicorn sites.dev.heroku.wsgi --log-file - 3 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "django-business-logic demo", 3 | "description": "visual programming for django", 4 | "repository": "https://github.com/dgk/django-business-logic", 5 | "logo": "https://raw.githubusercontent.com/wiki/dgk/django-business-logic/media/blockly.png", 6 | "addons": ["heroku-postgresql:hobby-dev"], 7 | "keywords": ["python", "django", "visual-programming", "execution-engine", "blockly", "business-logic", "rapid-development"] 8 | 9 | } 10 | -------------------------------------------------------------------------------- /bin/codeclimate: -------------------------------------------------------------------------------- 1 | docker run \ 2 | --interactive --tty --rm \ 3 | --env CODECLIMATE_CODE="$PWD" \ 4 | --volume "$PWD":/code \ 5 | --volume /var/run/docker.sock:/var/run/docker.sock \ 6 | --volume /tmp/cc:/tmp/cc \ 7 | codeclimate/codeclimate $@ 8 | -------------------------------------------------------------------------------- /bin/pre_compile: -------------------------------------------------------------------------------- 1 | # prepare requirements.txt for heroku 2 | grep -v '^-r' requirements.dev.txt >> requirements.txt 3 | cat requirements.heroku.txt >> requirements.txt 4 | -------------------------------------------------------------------------------- /bin/yapf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | yapf -r -i business_logic tests sites -e '*/migrations/**' -------------------------------------------------------------------------------- /business_logic/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __version__ = '0.6.0' 4 | -------------------------------------------------------------------------------- /business_logic/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BusinessLogicConfig(AppConfig): 5 | name = 'business_logic' 6 | default_auto_field = 'django.db.models.AutoField' 7 | -------------------------------------------------------------------------------- /business_logic/blockly/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /business_logic/blockly/data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | OPERATOR_TABLE = { 4 | 'math_arithmetic': { 5 | '+': 'ADD', 6 | '-': 'MINUS', 7 | '*': 'MULTIPLY', 8 | '/': 'DIVIDE', 9 | '^': 'POWER', 10 | }, 11 | 'logic_compare': { 12 | '==': 'EQ', 13 | '!=': 'NEQ', 14 | '<': 'LT', 15 | '<=': 'LTE', 16 | '>': 'GT', 17 | '>=': 'GTE', 18 | }, 19 | 'logic_operation': { 20 | '&': 'AND', 21 | '|': 'OR', 22 | }, 23 | } 24 | 25 | REVERSE_OPERATOR_TABLE = { 26 | block_type: {blockly: internal for internal, blockly in operators.items() 27 | } for block_type, operators in OPERATOR_TABLE.items() 28 | } 29 | -------------------------------------------------------------------------------- /business_logic/blockly/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class BlocklyXmlParserException(Exception): 5 | pass 6 | 7 | 8 | class BlocklyXmlBuilderException(Exception): 9 | pass 10 | 11 | 12 | class NodeTreeCreatorException(Exception): 13 | pass 14 | -------------------------------------------------------------------------------- /business_logic/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | 5 | class ExceptionHandlingPolicy: 6 | """ 7 | Enumeration of names of exception handling policies 8 | """ 9 | IGNORE = 'IGNORE' 10 | """Ignore exception and continue execution""" 11 | 12 | INTERRUPT = 'INTERRUPT' 13 | """Interrupt execution, this is default behaviour""" 14 | 15 | RAISE = 'RAISE' 16 | """Re-raise exception""" 17 | 18 | 19 | class ContextConfig(object): 20 | """ 21 | Stores configuration of :class:`business_logic.models.Context` 22 | 23 | Args: 24 | kwargs(object): overrides default configuration values 25 | Raises: 26 | TypeError 27 | """ 28 | 29 | defaults = dict( 30 | log=False, 31 | debug=False, 32 | cache=True, 33 | exception_handling_policy=ExceptionHandlingPolicy.INTERRUPT, 34 | ) 35 | """object: default configuration values""" 36 | 37 | def __init__(self, **kwargs): 38 | for k in kwargs.keys(): 39 | if k not in self.defaults: 40 | raise TypeError('Incorrect kwarg {}'.format(k)) 41 | 42 | for k, v in self.defaults.items(): 43 | kwargs.setdefault(k, v) 44 | 45 | for k, v in kwargs.items(): 46 | setattr(self, k, v) 47 | -------------------------------------------------------------------------------- /business_logic/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class InterpretationException(Exception): 5 | """ 6 | Wraps python exception raised during running :func:`business_logic.models.Node.interpret`. 7 | """ 8 | pass 9 | 10 | 11 | class StopInterpretationException(Exception): 12 | """ 13 | Exception for stop interpretation. This feature not implemented. 14 | 15 | See Also: 16 | * :class:`business_logic.models.StopInterpretation` 17 | """ 18 | pass 19 | 20 | 21 | class BreakLoopException(Exception): 22 | """ 23 | Exception for breaking loops. This feature not implemented. 24 | 25 | See Also: 26 | * :class:`business_logic.models.BreakLoop` 27 | """ 28 | pass 29 | -------------------------------------------------------------------------------- /business_logic/fields.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.db import models 4 | 5 | from .validators import validate_field_name 6 | 7 | 8 | class DeepAttributeField(models.CharField): 9 | default_validators = [ 10 | validate_field_name, 11 | ] 12 | -------------------------------------------------------------------------------- /business_logic/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /business_logic/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/migrations/__init__.py -------------------------------------------------------------------------------- /business_logic/models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from .assignment import * 5 | from .constant import * 6 | from .context import * 7 | from .foreach import * 8 | from .frame import * 9 | from .function import * 10 | from .ifstatement import * 11 | from .log import * 12 | from .node import * 13 | from .operator_ import * 14 | from .program import * 15 | from .reference import * 16 | from .stop import * 17 | from .table import * 18 | from .types_ import * 19 | from .variable import * 20 | -------------------------------------------------------------------------------- /business_logic/models/assignment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.utils.translation import gettext_lazy as _ 5 | 6 | from .node import NodeAccessor 7 | 8 | 9 | class Assignment(NodeAccessor): 10 | """ 11 | Assignment value to variable 12 | """ 13 | class Meta: 14 | verbose_name = _('Assignment') 15 | verbose_name_plural = _('Assignments') 16 | 17 | def __str__(self): 18 | return '=' 19 | 20 | def interpret(self, ctx, lhs, rhs): 21 | """ 22 | Interpret assignment 23 | 24 | Args: 25 | ctx(:class:`business_logic.models.Context`): execution context 26 | lhs(:class:`business_logic.models.Variable`): assignment variable 27 | rhs: right hand side value 28 | 29 | Returns: 30 | rhs value 31 | """ 32 | lhs_node = ctx.get_children(self.node)[0] 33 | ctx.set_variable(lhs_node.content_object.definition, rhs) 34 | return rhs 35 | -------------------------------------------------------------------------------- /business_logic/models/constant.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.db import models 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | 8 | class Constant(models.Model): 9 | 'Abstract class for constants storing' 10 | 11 | class Meta: 12 | abstract = True 13 | 14 | def __str__(self): 15 | return str(self.value) 16 | 17 | def interpret(self, ctx): 18 | return self.value 19 | 20 | 21 | class NumberConstant(Constant): 22 | value = models.FloatField(_('Value')) 23 | 24 | class Meta: 25 | verbose_name = _('Float constant') 26 | verbose_name_plural = _('Float constants') 27 | 28 | 29 | class StringConstant(Constant): 30 | value = models.TextField(_('Value'), default='') 31 | 32 | class Meta: 33 | verbose_name = _('String constant') 34 | verbose_name_plural = _('String constants') 35 | 36 | 37 | class BooleanConstant(Constant): 38 | value = models.BooleanField(_('Value')) 39 | 40 | class Meta: 41 | verbose_name = _('Boolean constant') 42 | verbose_name_plural = _('Boolean constants') 43 | 44 | 45 | class DateConstant(Constant): 46 | value = models.DateField(_('Value')) 47 | 48 | class Meta: 49 | verbose_name = _('Date constant') 50 | verbose_name_plural = _('Date constants') 51 | -------------------------------------------------------------------------------- /business_logic/models/foreach.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.db import models 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | 8 | class ForeachStatement(models.Model): 9 | 10 | class Meta: 11 | verbose_name = _('Foreach statement') 12 | verbose_name_plural = _('Foreach statements') 13 | -------------------------------------------------------------------------------- /business_logic/models/frame.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.db import models 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | 8 | class Frame(object): 9 | pass 10 | -------------------------------------------------------------------------------- /business_logic/models/ifstatement.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.utils.translation import gettext_lazy as _ 5 | 6 | from ..utils import pairs 7 | 8 | from .node import NodeAccessor 9 | 10 | 11 | class IfStatement(NodeAccessor): 12 | interpret_children = True 13 | 14 | class Meta: 15 | verbose_name = _('If statement') 16 | verbose_name_plural = _('If statements') 17 | 18 | def interpret(self, ctx): 19 | children = ctx.get_children(self.node) 20 | 21 | for pair in pairs(children): 22 | # last "else" branch 23 | if len(pair) == 1: 24 | return pair[0].interpret(ctx) 25 | 26 | if pair[0].interpret(ctx): 27 | return pair[1].interpret(ctx) 28 | -------------------------------------------------------------------------------- /business_logic/models/reference.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | import re 4 | 5 | from django.contrib.contenttypes.models import ContentType 6 | from django.db import models 7 | from django.utils.translation import gettext_lazy as _ 8 | 9 | from .node import NodeAccessor 10 | 11 | 12 | class ReferenceDescriptor(models.Model): 13 | content_type = models.OneToOneField(ContentType, on_delete=models.CASCADE) 14 | title = models.CharField(max_length=255, null=True, blank=True) 15 | search_fields = models.TextField(null=True, blank=True) 16 | name_field = models.SlugField(max_length=255, null=True, blank=True) 17 | 18 | class Meta: 19 | verbose_name = _('Reference descriptor') 20 | verbose_name_plural = _('Reference descriptors') 21 | 22 | def __str__(self): 23 | return str(self.content_type) 24 | 25 | def get_search_fields(self): 26 | if not self.search_fields: 27 | return [] 28 | 29 | return re.split(r'[^\w_]+', self.search_fields) 30 | 31 | 32 | class ReferenceConstant(NodeAccessor): 33 | 'A special type of constant that uses the value in the first child node of the node that stores it' 34 | interpret_children = True 35 | 36 | class Meta: 37 | verbose_name = _('Reference constant') 38 | verbose_name_plural = _('Reference constants') 39 | 40 | def interpret(self, ctx): 41 | return ctx.get_children(self.node)[0].content_object 42 | -------------------------------------------------------------------------------- /business_logic/models/stop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.db import models 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | from ..exceptions import StopInterpretationException, BreakLoopException 8 | 9 | 10 | class StopInterpretation(models.Model): 11 | 12 | class Meta: 13 | verbose_name = _('Stop instruction') 14 | verbose_name_plural = _('Stop instructions') 15 | 16 | def interpret(self, ctx): 17 | raise StopInterpretationException() 18 | 19 | 20 | class BreakLoop(models.Model): 21 | 22 | class Meta: 23 | verbose_name = _('Break instruction') 24 | verbose_name_plural = _('Break instructions') 25 | 26 | def interpret(self, ctx): 27 | raise BreakLoopException() 28 | -------------------------------------------------------------------------------- /business_logic/models/table.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.db import models 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | 8 | class Table(models.Model): 9 | 10 | class Meta: 11 | verbose_name = _('Table') 12 | verbose_name_plural = _('Tables') 13 | -------------------------------------------------------------------------------- /business_logic/models/types_.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.db import models 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | from .constant import BooleanConstant 8 | from .constant import NumberConstant 9 | from .constant import StringConstant 10 | 11 | CONSTANTS_FOR_TYPES = { 12 | 'bool': BooleanConstant, 13 | 'float': NumberConstant, 14 | 'string': StringConstant, 15 | } 16 | 17 | TYPE_CHOICES = ( 18 | ('bool', _('Boolean')), 19 | ('float', _('Float')), 20 | ('string', _('String')), 21 | ('model', _('Model')), 22 | ) 23 | 24 | DJANGO_FIELDS_FOR_TYPES = { 25 | 'bool': ( 26 | models.BooleanField, 27 | models.NullBooleanField, 28 | ), 29 | 'number': ( 30 | models.FloatField, 31 | models.DecimalField, 32 | models.SmallIntegerField, 33 | models.PositiveSmallIntegerField, 34 | models.IntegerField, 35 | models.PositiveIntegerField, 36 | models.BigIntegerField, 37 | ), 38 | 'string': ( 39 | models.CharField, 40 | models.TextField, 41 | ), 42 | 'model': (models.ForeignKey,), 43 | 'date': (models.DateField,), 44 | 'datetime': (models.DateTimeField,), 45 | } 46 | 47 | TYPES_FOR_DJANGO_FIELDS = {} 48 | 49 | for _type, fields in DJANGO_FIELDS_FOR_TYPES.items(): 50 | for field in fields: 51 | TYPES_FOR_DJANGO_FIELDS[field] = _type 52 | -------------------------------------------------------------------------------- /business_logic/models/variable.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.db import models 4 | from django.utils.translation import gettext_lazy as _ 5 | from django.utils.encoding import force_str 6 | 7 | 8 | class VariableDefinition(models.Model): 9 | name = models.TextField(_('Variable name'), blank=False, null=False) 10 | 11 | class Meta: 12 | verbose_name = _('Variable definition') 13 | verbose_name_plural = _('Variable definitions') 14 | 15 | def __str__(self): 16 | return self.name 17 | 18 | def interpret(self, ctx): 19 | pass 20 | 21 | 22 | class Variable(models.Model): 23 | """ 24 | 25 | """ 26 | definition = models.ForeignKey(VariableDefinition, related_name='variables', on_delete=models.CASCADE) 27 | 28 | class Meta: 29 | verbose_name = _('Variable') 30 | verbose_name_plural = _('Variables') 31 | 32 | class Undefined(object): 33 | 34 | def __nonzero__(self): 35 | """ 36 | Called to implement truth value testing for python 2 37 | :return: always False 38 | """ 39 | return False 40 | 41 | def __bool__(self): 42 | """ 43 | Called to implement truth value testing for python 3 44 | :return: always False 45 | """ 46 | return False 47 | 48 | def __str__(self): 49 | return force_str(_('Undefined')) 50 | 51 | def __str__(self): 52 | return self.definition.name 53 | 54 | def interpret(self, ctx): 55 | return ctx.get_variable(self.definition) 56 | -------------------------------------------------------------------------------- /business_logic/rest/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /business_logic/rest/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.urls import re_path 3 | 4 | from .views import * 5 | 6 | urlpatterns = [ 7 | re_path(r'^$', api_root, name='root'), 8 | re_path(r'^program-interface$', ProgramInterfaceList.as_view(), name='program-interface-list'), 9 | re_path(r'^program-interface/(?P\d+)$', ProgramInterfaceView.as_view(), name='program-interface'), 10 | re_path(r'^program$', ProgramList.as_view(), name='program-list'), 11 | re_path(r'^program/(?P\d+)$', ProgramView.as_view(), name='program'), 12 | re_path(r'^program-version$', ProgramVersionList.as_view(), name='program-version-list'), 13 | re_path(r'^program-version/new$', ProgramVersionCreate.as_view(), name='program-version-create'), 14 | re_path(r'^program-version/(?P\d+)$', ProgramVersionView.as_view(), name='program-version'), 15 | re_path(r'^execution$', ExecutionList.as_view(), name='execution-list'), 16 | re_path(r'^execution/(?P\d+)$', ExecutionView.as_view(), name='execution'), 17 | re_path(r'^execution/(?P\d+)/log$', LogView.as_view(), name='log'), 18 | re_path(r'^reference$', ReferenceDescriptorList.as_view(), name='reference-descriptor-list'), 19 | re_path(r'^reference/(?P[\w.]+)$', ReferenceList.as_view(), name='reference-list'), 20 | re_path(r'^reference/(?P[\w.]+)/(?P\d+)$', ReferenceView.as_view(), name='reference'), 21 | ] 22 | -------------------------------------------------------------------------------- /business_logic/signals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.dispatch import Signal 5 | 6 | block_interpret_enter = Signal() 7 | """Fired on entering to code block interpretation""" 8 | 9 | block_interpret_leave = Signal() 10 | """Fired on leaving code block interpretation""" 11 | 12 | statement_interpret_enter = Signal() 13 | """Fired on entering statement interpretation""" 14 | 15 | statement_interpret_leave = Signal() 16 | """Fired on leaving statement interpretation""" 17 | 18 | interpret_enter = Signal() 19 | """Fired on entering code interpretation""" 20 | 21 | interpret_leave = Signal() 22 | """Fired on leaving code interpretation""" 23 | 24 | interpret_exception = Signal() 25 | """Fired on interpretation exception""" 26 | -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/1x1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/1x1.gif -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/accessible.css: -------------------------------------------------------------------------------- 1 | .blocklyTable { 2 | vertical-align: top; 3 | } 4 | .blocklyTree .blocklyActiveDescendant > label, 5 | .blocklyTree .blocklyActiveDescendant > div > label, 6 | .blocklyActiveDescendant > button, 7 | .blocklyActiveDescendant > input { 8 | outline: 2px dotted #00f; 9 | } 10 | -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/click.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/click.mp3 -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/click.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/click.ogg -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/click.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/click.wav -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/delete.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/delete.mp3 -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/delete.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/delete.ogg -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/delete.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/delete.wav -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/disconnect.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/disconnect.mp3 -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/disconnect.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/disconnect.ogg -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/disconnect.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/disconnect.wav -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/handclosed.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/handclosed.cur -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/handdelete.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/handdelete.cur -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/handopen.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/handopen.cur -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/quote0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/quote0.png -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/quote1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/quote1.png -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/blockly/sprites.png -------------------------------------------------------------------------------- /business_logic/static/business_logic/blockly/sprites.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /business_logic/static/business_logic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Business Logic 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Loading... 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /business_logic/static/business_logic/src/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/src/assets/fonts/icons.eot -------------------------------------------------------------------------------- /business_logic/static/business_logic/src/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/src/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /business_logic/static/business_logic/src/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/src/assets/fonts/icons.woff -------------------------------------------------------------------------------- /business_logic/static/business_logic/src/assets/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/business_logic/static/business_logic/src/assets/fonts/icons.woff2 -------------------------------------------------------------------------------- /business_logic/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.urls import include, re_path 3 | 4 | urlpatterns = [re_path(r'^rest/', include(('business_logic.rest.urls', 'business_logic'), namespace='rest'))] 5 | 6 | urlpatterns = [re_path('', include((urlpatterns, 'business_logic'), namespace='business-logic'))] 7 | -------------------------------------------------------------------------------- /business_logic/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.contrib.contenttypes.models import ContentType 3 | from django.db.models import Q 4 | 5 | 6 | def pairs(iterable): 7 | # split iterable into two element chunks 8 | return [iterable[i:i + 2] for i in range(0, len(iterable), 2)] 9 | 10 | 11 | def get_content_type_id(model): 12 | return ContentType.objects.get_for_model(model).id 13 | 14 | 15 | def get_customer_available_content_types(): 16 | # returns ContentType queryset except content types for 17 | # some django service and this application models 18 | 19 | exclude_choices = Q() 20 | for app_label in [ 21 | 'admin', 22 | 'contenttypes', 23 | 'sessions', 24 | # 25 | 'business_logic', 26 | ]: 27 | exclude_choices |= Q(app_label=app_label) 28 | 29 | return ContentType.objects.exclude(exclude_choices) 30 | -------------------------------------------------------------------------------- /business_logic/validators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | 5 | from django.core.validators import RegexValidator 6 | from django.utils.translation import gettext_lazy as _ 7 | 8 | field_name_re = re.compile(r'^[a-zA-Z][a-zA-Z0-9_.]+\Z') 9 | validate_field_name = RegexValidator( 10 | field_name_re, 11 | _("Enter a valid 'field_name' consisting of letters, numbers, underscores or dots."), 12 | 'invalid' 13 | ) 14 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | django-business-logic-demo: 4 | build: 5 | context: . 6 | dockerfile: Dockerfile 7 | ports: 8 | - 8000:8000 9 | 10 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/architecture/constants.rst: -------------------------------------------------------------------------------- 1 | .. _Constants: 2 | 3 | Constants 4 | ========= 5 | .. autoclass:: business_logic.models.Constant 6 | :members: interpret 7 | 8 | .. autoclass:: business_logic.models.NumberConstant 9 | .. autoclass:: business_logic.models.StringConstant 10 | .. autoclass:: business_logic.models.BooleanConstant 11 | .. autoclass:: business_logic.models.DateConstant 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/architecture/context.rst: -------------------------------------------------------------------------------- 1 | Context 2 | ======= 3 | 4 | 5 | .. autoclass:: business_logic.models.Context 6 | :members: get_variable, set_variable 7 | 8 | .. autoclass:: business_logic.config.ContextConfig 9 | :members: defaults 10 | 11 | .. autoclass:: business_logic.config.ExceptionHandlingPolicy 12 | :members: 13 | -------------------------------------------------------------------------------- /docs/architecture/environment.rst: -------------------------------------------------------------------------------- 1 | Execution environment 2 | ===================== 3 | 4 | .. autoclass:: business_logic.models.ExecutionEnvironment 5 | 6 | 7 | .. image:: ../static/uml/ExecutionEnvironment.svg 8 | -------------------------------------------------------------------------------- /docs/architecture/exceptions.rst: -------------------------------------------------------------------------------- 1 | Exceptions 2 | ========== 3 | .. automodule:: business_logic.exceptions 4 | :members: 5 | -------------------------------------------------------------------------------- /docs/architecture/functions.rst: -------------------------------------------------------------------------------- 1 | Functions 2 | ========= 3 | 4 | Imported functions 5 | ------------------ 6 | .. autoclass:: business_logic.models.PythonModuleFunctionDefinition 7 | :members: 8 | 9 | Editable functions 10 | ------------------ 11 | .. autoclass:: business_logic.models.PythonCodeFunctionDefinition 12 | :members: 13 | 14 | Function libraries 15 | ------------------ 16 | .. autoclass:: business_logic.models.FunctionLibrary 17 | :members: 18 | 19 | .. image:: ../static/uml/Function.svg 20 | -------------------------------------------------------------------------------- /docs/architecture/index.rst: -------------------------------------------------------------------------------- 1 | Architecture 2 | ============ 3 | 4 | 5 | Internally program code is stored as special django models such as 6 | ``NumberConstant`` (see :ref:`Constants`) , ``IfStatement``, ``Assignment`` and so forth. Structure of 7 | syntax tree is held by class :ref:`Node` derived from `treebeard.NS_Node `_. 8 | Operators and operands are linked with ``Node`` objects through 9 | `django contenttypes system `_. 10 | Other details are briefly described in 11 | :ref:`administrative_setup` and 12 | :ref:`invocation_injecting` sections 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | :caption: Detailed: 17 | 18 | program.rst 19 | node.rst 20 | operators.rst 21 | operands.rst 22 | statements.rst 23 | environment.rst 24 | context.rst 25 | functions.rst 26 | logging.rst 27 | signals.rst 28 | exceptions.rst 29 | 30 | -------------------------------------------------------------------------------- /docs/architecture/logging.rst: -------------------------------------------------------------------------------- 1 | Logging 2 | ======= 3 | 4 | .. autoclass:: business_logic.models.Logger 5 | :members: 6 | 7 | .. autoclass:: business_logic.models.LogEntry 8 | :members: 9 | 10 | .. autoclass:: business_logic.models.ExceptionLog 11 | :members: 12 | 13 | .. autoclass:: business_logic.models.Execution 14 | :members: 15 | 16 | .. autoclass:: business_logic.models.ExecutionArgument 17 | :members: -------------------------------------------------------------------------------- /docs/architecture/node.rst: -------------------------------------------------------------------------------- 1 | .. _Node: 2 | 3 | Node 4 | ==== 5 | 6 | .. autoclass:: business_logic.models.Node 7 | :members: clone, pprint, ensure_content_object_saved, add_root, delete, add_child, interpret, is_block, is_statement, is_content_object_interpret_children_itself 8 | 9 | .. autoclass:: business_logic.models.NodeCache 10 | :members: get_children 11 | 12 | .. autoclass:: business_logic.models.NodeCacheHolder 13 | :members: get_children 14 | 15 | .. autoclass:: business_logic.models.NodeVisitor 16 | :members: visit, preorder, postorder 17 | -------------------------------------------------------------------------------- /docs/architecture/operands.rst: -------------------------------------------------------------------------------- 1 | .. _Operands: 2 | 3 | Operands 4 | ======== 5 | .. toctree:: 6 | :maxdepth: 2 7 | :caption: Detailed: 8 | 9 | constants.rst 10 | references.rst 11 | variables.rst 12 | -------------------------------------------------------------------------------- /docs/architecture/operators.rst: -------------------------------------------------------------------------------- 1 | .. _Operators: 2 | 3 | Operators 4 | ========= 5 | 6 | .. autoclass:: business_logic.models.BinaryOperator 7 | :members: 8 | 9 | .. autoclass:: business_logic.models.UnaryOperator 10 | 11 | .. autoclass:: business_logic.models.Assignment 12 | :members: interpret 13 | -------------------------------------------------------------------------------- /docs/architecture/program.rst: -------------------------------------------------------------------------------- 1 | Program entities 2 | ================ 3 | 4 | .. ProgramInterface: 5 | 6 | ProgramInterface 7 | ---------------- 8 | 9 | .. autoclass:: business_logic.models.ProgramInterface 10 | :members: arguments 11 | 12 | .. ProgramArgument: 13 | 14 | ProgramArgument 15 | ~~~~~~~~~~~~~~~~ 16 | 17 | .. autoclass:: business_logic.models.ProgramArgument 18 | 19 | .. ProgramArgumentField: 20 | 21 | ProgramArgumentField 22 | ~~~~~~~~~~~~~~~~~~~~ 23 | 24 | .. autoclass:: business_logic.models.ProgramArgumentField 25 | 26 | 27 | .. Program: 28 | 29 | Program 30 | ------- 31 | 32 | .. autoclass:: business_logic.models.Program 33 | :members: 34 | 35 | .. ProgramVersion: 36 | 37 | ProgramVersion 38 | -------------- 39 | 40 | .. autoclass:: business_logic.models.ProgramVersion 41 | :members: execute, copy 42 | 43 | .. image:: ../static/uml/Program.svg 44 | -------------------------------------------------------------------------------- /docs/architecture/references.rst: -------------------------------------------------------------------------------- 1 | .. _References: 2 | 3 | References 4 | ========== 5 | 6 | .. autoclass:: business_logic.models.ReferenceConstant 7 | .. autoclass:: business_logic.models.ReferenceDescriptor 8 | 9 | -------------------------------------------------------------------------------- /docs/architecture/signals.rst: -------------------------------------------------------------------------------- 1 | .. _Signals: 2 | 3 | Signals 4 | ======= 5 | 6 | .. automodule:: business_logic.signals 7 | :members: 8 | -------------------------------------------------------------------------------- /docs/architecture/statements.rst: -------------------------------------------------------------------------------- 1 | .. _Statements: 2 | 3 | Statements 4 | ========== 5 | 6 | .. autoclass:: business_logic.models.IfStatement 7 | 8 | Not fully implemented: 9 | 10 | .. autoclass:: business_logic.models.StopInterpretation 11 | .. autoclass:: business_logic.models.BreakLoop 12 | 13 | -------------------------------------------------------------------------------- /docs/architecture/variables.rst: -------------------------------------------------------------------------------- 1 | .. _Variables: 2 | 3 | Variables 4 | ========= 5 | 6 | .. autoclass:: business_logic.models.VariableDefinition 7 | :members: 8 | 9 | .. autoclass:: business_logic.models.Variable 10 | :members: definition 11 | 12 | .. autoclass:: business_logic.models.Variable.Undefined 13 | :special-members: 14 | 15 | 16 | .. image:: ../static/uml/Variable.svg 17 | 18 | -------------------------------------------------------------------------------- /docs/caveats.rst: -------------------------------------------------------------------------------- 1 | Caveats 2 | ======= 3 | 4 | 5 | .. warning:: 6 | Please note that this software is still Alpha/Beta quality and is not recommended for production use. 7 | The library is currently in active development and any API may be changed. Migration history for models is not supported now. 8 | Some internal objects are world writable! 9 | 10 | .. note:: 11 | If you want to use this library before it have implemented 12 | authenticating/authorization subsystem you can use nginx/apache/your web server 13 | .htaccess-like rules by limiting access to the ``/business_logic/`` url path. 14 | -------------------------------------------------------------------------------- /docs/credits.rst: -------------------------------------------------------------------------------- 1 | Credits 2 | ======= 3 | 4 | Many thanks to: 5 | 6 | * `contributors `__ of this library 7 | * all folks from Insurance Technologies LLC (`b2bpolis.ru `__, `@b2bpolis `__ ), St.Petersburg, Russia and personally to its CEO `Roman Kurdo `__ 8 | * authors of all used opensource libraries 9 | 10 | -------------------------------------------------------------------------------- /docs/demo.rst: -------------------------------------------------------------------------------- 1 | Demo application 2 | ================ 3 | 4 | Description 5 | ----------- 6 | Demo application is a simple bookshelf with the following models: 7 | 8 | .. automodule:: sites.dev.books.models 9 | :members: 10 | 11 | On each entering to single book view the following python code executed: 12 | 13 | .. code:: python 14 | 15 | # sites/dev/books/views.py 16 | class BookDetail(generic.DetailView): 17 | model = Book 18 | 19 | def get_object(self, queryset=None): 20 | book = super(BookDetail, self).get_object(queryset) 21 | program = Program.objects.get(code='on_book_view') 22 | version = program.versions.order_by('id').last() 23 | version.execute(context=Context(debug=True, log=True), book=book) 24 | book.publisher.save() 25 | 26 | return book 27 | 28 | You can see screenshots now: :ref:`demo_on_book_view_screenshot` and :ref:`demo_on_book_view_log_screenshot` 29 | 30 | Hosted demo 31 | ----------- 32 | Hosted demo site is available here: https://django-business-logic-demo.dev.dgk.su/ . The database is reset every hour. 33 | You can login into django admin interface with username ``test`` and password ``test``. 34 | 35 | 36 | Running using Docker 37 | -------------------- 38 | 39 | You can run demo app locally using docker: 40 | 41 | .. code:: bash 42 | 43 | docker build . -t django-business-logic-demo 44 | docker run --rm -it -p 8000:8000 django-business-logic-demo 45 | 46 | or using docker-compose: 47 | 48 | .. code:: bash 49 | 50 | docker-compose up 51 | 52 | Also you can use prebuilt image: 53 | 54 | .. code:: bash 55 | 56 | docker run --rm -it -p 8000:8000 dgksu/django-business-logic:demo 57 | 58 | Now you can login into django admin interface 59 | http://localhost:8000/admin/ with username ``test`` and password 60 | ``test``. 61 | 62 | 63 | Deployment to Heroku 64 | -------------------- 65 | 66 | You can deploy demo app directly to Heroku to see the app live. Just 67 | click the button below. This will walk you through getting this app up 68 | and running on Heroku in minutes. 69 | 70 | 71 | .. image:: https://www.herokucdn.com/deploy/button.svg 72 | :target: https://heroku.com/deploy?template=https://github.com/dgk/django-business-logic 73 | 74 | 75 | Running using local files 76 | ------------------------- 77 | 78 | See :ref:`backend_development_environment` 79 | -------------------------------------------------------------------------------- /docs/development.rst: -------------------------------------------------------------------------------- 1 | Library Development 2 | ------------------- 3 | 4 | .. _backend_development_environment: 5 | 6 | Backend development environment 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | nox 10 | ^^^^^^^^^^ 11 | 12 | Install `nox `_. 13 | 14 | This tool will help you to setup dev environment and run tests. 15 | 16 | Following command will create a virtual environment and install all dependencies: 17 | 18 | .. code:: bash 19 | 20 | nox -s dev 21 | 22 | Do not forget to activate the virtual environment before run any further commands. 23 | 24 | Installing test data 25 | ^^^^^^^^^^^^^^^^^^^^ 26 | 27 | .. code:: bash 28 | 29 | python manage.py migrate 30 | python manage.py loaddata sites/dev/fixtures/data.json 31 | 32 | Running backend dev server 33 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 34 | 35 | .. code:: bash 36 | 37 | python manage.py runserver 38 | 39 | An instance of django dev server will be listening on 40 | http://localhost:8000/ . Now you can login into django admin interface 41 | http://localhost:8000/admin/ with username ``test`` and password 42 | ``test``. 43 | 44 | Frontend development environment 45 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 46 | 47 | Fronted source files 48 | located under the ``frontend`` folder. 49 | 50 | .. code:: bash 51 | 52 | cd frontend 53 | 54 | Installing dependencies 55 | ^^^^^^^^^^^^^^^^^^^^^^^ 56 | 57 | .. code:: bash 58 | 59 | npm install 60 | 61 | Running webpack dev server 62 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 63 | 64 | .. code:: bash 65 | 66 | npm run server:dev:hmr 67 | 68 | Now webpack dev server will be listening on http://localhost:3000/ . 69 | 70 | Building frontend files 71 | ^^^^^^^^^^^^^^^^^^^^^^^ 72 | 73 | .. code:: bash 74 | 75 | npm run build:prod 76 | 77 | Running tests 78 | ~~~~~~~~~~~~~ 79 | 80 | Running backend tests 81 | ^^^^^^^^^^^^^^^^^^^^^ 82 | 83 | .. code:: bash 84 | 85 | python manage.py test 86 | 87 | Test it all 88 | ^^^^^^^^^^^ 89 | 90 | You need to know at least one command; the one that runs all the tests: 91 | 92 | .. code:: bash 93 | 94 | nox -s unittest 95 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | Welcome to django-business-logic's documentation! 4 | ================================================= 5 | 6 | .. include:: ../README.rst 7 | 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | caveats.rst 14 | demo.rst 15 | screenshots.rst 16 | requirements.rst 17 | installation.rst 18 | configuration.rst 19 | visual-programming.rst 20 | architecture/index.rst 21 | development.rst 22 | roadmap.rst 23 | credits.rst 24 | license.rst 25 | 26 | 27 | .. Indices and tables 28 | ================== 29 | 30 | * :ref:`genindex` 31 | * :ref:`modindex` 32 | * :ref:`search` 33 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Library installation 5 | -------------------- 6 | 7 | There are a few different ways you can install django-business-logic: 8 | 9 | - Use pip: ``pip install -U django-business-logic`` 10 | - Download a zipfile from the 11 | `releases page `__ and install it. 12 | - Checkout the source: 13 | ``git clone git://github.com/dgk/django-business-logic.git`` and 14 | install it yourself by entering ``python setup.py install``. 15 | 16 | Server setup 17 | ------------ 18 | 19 | Edit ``settings.py`` and add following apps to your ``INSTALLED_APPS`` 20 | 21 | .. code:: python 22 | 23 | # settings.py 24 | INSTALLED_APPS = ( 25 | # ... 26 | 'django.contrib.contenttypes', 27 | 28 | 'ace_overlay', # optional, for comfortable python functions editing 29 | 'adminsortable2', 30 | 'nested_admin', 31 | 'polymorphic', 32 | 'rest_framework', # optional, provided browsable API for this library handy development 33 | 'django_filters', # ^^ same 34 | 35 | 'business_logic', 36 | # ... 37 | ) 38 | 39 | 40 | Edit ``urls.py`` and include required urls 41 | 42 | .. code:: python 43 | 44 | # urls.py 45 | urlpatterns = ( 46 | # ... 47 | re_path('^business-logic/', include('business_logic.urls')), 48 | re_path('^nested_admin/', include('nested_admin.urls')), 49 | # ... 50 | ) 51 | 52 | Make migrations 53 | 54 | .. code:: bash 55 | 56 | python manage.py migrate 57 | 58 | Collect static files 59 | 60 | .. code:: bash 61 | 62 | python manage.py collectstatic 63 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | .. include:: ../LICENSE.txt 5 | .. .. literalinclude:: ../LICENSE.txt -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.in: -------------------------------------------------------------------------------- 1 | Sphinx==7.1.2 2 | sphinx_rtd_theme 3 | Markdown<=3.4.4 4 | -------------------------------------------------------------------------------- /docs/requirements.rst: -------------------------------------------------------------------------------- 1 | Requirements 2 | ============ 3 | 4 | This library requires the following: 5 | 6 | * Python (3.8-3.11) 7 | * Django (3.2-4.2) 8 | * django-rest-framework 3.14+ 9 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.11 3 | # by the following command: 4 | # 5 | # pip-compile docs/requirements.in 6 | # 7 | alabaster==0.7.13 8 | # via sphinx 9 | babel==2.12.1 10 | # via sphinx 11 | certifi==2023.7.22 12 | # via requests 13 | charset-normalizer==3.2.0 14 | # via requests 15 | docutils==0.18.1 16 | # via 17 | # sphinx 18 | # sphinx-rtd-theme 19 | idna==3.4 20 | # via requests 21 | imagesize==1.4.1 22 | # via sphinx 23 | jinja2==3.1.2 24 | # via sphinx 25 | markdown==3.4.4 26 | # via -r docs/requirements.in 27 | markupsafe==2.1.3 28 | # via jinja2 29 | packaging==23.1 30 | # via sphinx 31 | pygments==2.16.1 32 | # via sphinx 33 | requests==2.31.0 34 | # via sphinx 35 | snowballstemmer==2.2.0 36 | # via sphinx 37 | sphinx==7.1.2 38 | # via 39 | # -r docs/requirements.in 40 | # sphinx-rtd-theme 41 | # sphinxcontrib-jquery 42 | sphinx-rtd-theme==1.3.0 43 | # via -r docs/requirements.in 44 | sphinxcontrib-applehelp==1.0.4 45 | # via sphinx 46 | sphinxcontrib-devhelp==1.0.2 47 | # via sphinx 48 | sphinxcontrib-htmlhelp==2.0.1 49 | # via sphinx 50 | sphinxcontrib-jquery==4.1 51 | # via sphinx-rtd-theme 52 | sphinxcontrib-jsmath==1.0.1 53 | # via sphinx 54 | sphinxcontrib-qthelp==1.0.3 55 | # via sphinx 56 | sphinxcontrib-serializinghtml==1.1.5 57 | # via sphinx 58 | urllib3==2.0.4 59 | # via requests 60 | -------------------------------------------------------------------------------- /docs/roadmap.rst: -------------------------------------------------------------------------------- 1 | Roadmap 2 | ======= 3 | 4 | - Frontend refactoring 5 | - Lists and Loops 6 | - Implicit strong static typing 7 | - Authorization 8 | - Two-dimensional constant tables (matrices) 9 | - Pure python code generation for execution speedup 10 | - Visually editable functions and its libraries 11 | - Code sharing 12 | 13 | Todo 14 | ---- 15 | 16 | .. todolist:: 17 | -------------------------------------------------------------------------------- /docs/screenshots.rst: -------------------------------------------------------------------------------- 1 | Screenshots 2 | =========== 3 | .. _demo_on_book_view_screenshot: 4 | 5 | Demo code editing 6 | ----------------- 7 | 8 | .. image:: static/screenshots/editing.png 9 | 10 | .. _demo_on_book_view_log_screenshot: 11 | 12 | Demo code execution log 13 | ----------------------- 14 | 15 | .. image:: static/screenshots/log.png 16 | 17 | Sample python function 18 | ---------------------- 19 | .. image:: static/screenshots/python-function.png 20 | -------------------------------------------------------------------------------- /docs/static/gif/animate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for GROUP in `ls *.png | cut -d_ -f1 | sort | uniq` 4 | do 5 | echo $GROUP 6 | ls ${GROUP}_*.png 7 | convert -delay 120 -loop 0 ${GROUP}_*.png ${GROUP}.gif 8 | done 9 | -------------------------------------------------------------------------------- /docs/static/screenshots/editing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/docs/static/screenshots/editing.png -------------------------------------------------------------------------------- /docs/static/screenshots/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/docs/static/screenshots/log.png -------------------------------------------------------------------------------- /docs/static/screenshots/python-function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/docs/static/screenshots/python-function.png -------------------------------------------------------------------------------- /docs/visual-programming.rst: -------------------------------------------------------------------------------- 1 | Visual programming 2 | ================== 3 | After setup you can go to web editor interface 4 | (http://localhost:8000/static/business_logic/index.html), choose program 5 | interface, program, create and start editing your first program version. 6 | 7 | -------------------------------------------------------------------------------- /docs/watch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | if [ $# -eq 0 ] 4 | then 5 | CMD='make html' 6 | else 7 | CMD='rm -rf build && make html' 8 | fi 9 | THRESHOLD=30 10 | DATE=1 11 | 12 | inotifywait --monitor --recursive --event modify --event create --exclude '.*\.*pyc$' --exclude __pycache__ --exclude build . ../business_logic | while read FILE 13 | do 14 | echo $FILE 15 | CURDATE=`date +%s` 16 | if [ $(($CURDATE - $THRESHOLD)) -gt $DATE ] 17 | then 18 | DATE=$CURDATE 19 | bash -c "$CMD" 20 | else 21 | echo already built 22 | fi 23 | done 24 | 25 | 26 | #inotifywait --monitor --recursive --event modify --event create --exclude build . ../business_logic | while read noop; do bash -c "$CMD"; done 27 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # @AngularClass 2 | # http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | insert_final_newline = false 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /frontend/.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Read and contribute to the Wiki 2 | 3 | Make sure you read the [Wiki](https://github.com/AngularClass/angular2-webpack-starter/wiki). 4 | 5 | ## Submitting Pull Requests 6 | 7 | If you're changing the structure of the repository please create an issue first. 8 | 9 | ## Submitting bug reports 10 | 11 | Make sure you are on latest changes and that you ran this command `npm run clean:install` after updating your local repository. If you can, please provide more information about your environment such as browser, operating system, node version, and npm version. 12 | -------------------------------------------------------------------------------- /frontend/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Note: for support questions, please use one of these channels:** [Chat: AngularClass.slack](http://angularclass.com/member-join/) or [Twitter: @AngularClass](https://twitter.com/AngularClass) 2 | 3 | * **I'm submitting a ...** 4 | [ ] bug report 5 | [ ] feature request 6 | [ ] question about the decisions made in the repository 7 | 8 | * **Do you want to request a *feature* or report a *bug*?** 9 | 10 | 11 | 12 | * **What is the current behavior?** 13 | 14 | 15 | 16 | * **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem** via 17 | https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5). 18 | 19 | 20 | 21 | * **What is the expected behavior?** 22 | 23 | 24 | 25 | * **What is the motivation / use case for changing the behavior?** 26 | 27 | 28 | 29 | * **Please tell us about your environment:** 30 | 31 | - Angular version: 2.0.0-beta.X 32 | - Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] 33 | 34 | 35 | 36 | * **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc) 37 | -------------------------------------------------------------------------------- /frontend/.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) 2 | 3 | 4 | 5 | * **What is the current behavior?** (You can also link to an open issue here) 6 | 7 | 8 | 9 | * **What is the new behavior (if this is a feature change)?** 10 | 11 | 12 | 13 | * **Other information**: 14 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # @AngularClass 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | 24 | # Users Environment Variables 25 | .lock-wscript 26 | 27 | # OS generated files # 28 | .DS_Store 29 | ehthumbs.db 30 | Icon? 31 | Thumbs.db 32 | 33 | # Node Files # 34 | /node_modules 35 | /bower_components 36 | npm-debug.log 37 | 38 | # Coverage # 39 | /coverage/ 40 | 41 | # Typing # 42 | /src/typings/tsd/ 43 | /typings/ 44 | /tsd_typings/ 45 | 46 | # Dist # 47 | /dist 48 | /public/__build__/ 49 | /src/*/__build__/ 50 | /__build__/** 51 | /public/dist/ 52 | /src/*/dist/ 53 | /dist/** 54 | .webpack.json 55 | 56 | # Doc # 57 | /doc/ 58 | 59 | # IDE # 60 | .idea/ 61 | *.swp 62 | 63 | 64 | ../old/ 65 | -------------------------------------------------------------------------------- /frontend/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /frontend/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "5" 5 | - "node" 6 | services: 7 | - docker 8 | before_install: 9 | - export CHROME_BIN=chromium-browser 10 | - export DISPLAY=:99.0 11 | - sh -e /etc/init.d/xvfb start 12 | - sleep 3 13 | # Updating NPM to relevant version >= 3 on Node.JS LTS 14 | - npm i -g npm@^3 15 | script: 16 | - npm test 17 | - npm run build:docker 18 | sudo: required 19 | -------------------------------------------------------------------------------- /frontend/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Chrome against localhost, with sourcemaps", 6 | "type": "chrome", 7 | "request": "launch", 8 | "url": "http://localhost:3000/*", 9 | "runtimeArgs": [ 10 | "--disable-web-security", 11 | "--user-data-dir", 12 | "--remote-debugging-port=9222" 13 | ], 14 | "sourceMaps": true, 15 | "webRoot": "${workspaceRoot}" 16 | }, 17 | { 18 | "name": "Attach to Chrome, with sourcemaps", 19 | "type": "chrome", 20 | "request": "attach", 21 | "url": "http://localhost:3000/*", 22 | "port": 9222, 23 | "sourceMaps": true, 24 | "webRoot": "${workspaceRoot}" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /frontend/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib/" 3 | } 4 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | # Builds a Docker to deliver dist/ 2 | FROM nginx:1.23.4-bullseye 3 | COPY dist/ /usr/share/nginx/html -------------------------------------------------------------------------------- /frontend/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 AngularClass LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # django-business-logic 2 | ### (frontend part) 3 | 4 | ### Installation 5 | 6 | requires [Node.js](https://nodejs.org/) v4+ and npm v3+ 7 | Install the dependencies and devDependencies 8 | 9 | ```sh 10 | $ npm install 11 | ``` 12 | 13 | ### webpack server 14 | ```bash 15 | # development 16 | $ npm run server:dev:hmr 17 | # production 18 | npm run build:prod 19 | npm run server:prod 20 | ``` 21 | 22 | ### build files 23 | ```bash 24 | # development 25 | npm run build:dev 26 | # production 27 | npm run build:prod 28 | ``` 29 | 30 | ### hot module replacement 31 | ```bash 32 | npm run server:dev:hmr 33 | ``` 34 | 35 | ### watch and build files 36 | ```bash 37 | npm run watch 38 | ``` 39 | 40 | ### run tests 41 | ```bash 42 | npm run test 43 | ``` 44 | 45 | ### watch and run our tests 46 | ```bash 47 | npm run watch:test 48 | ``` 49 | 50 | ### run end-to-end tests 51 | ```bash 52 | # make sure you have your server running in another terminal 53 | npm run e2e 54 | ``` 55 | 56 | ### run webdriver (for end-to-end) 57 | ```bash 58 | npm run webdriver:update 59 | npm run webdriver:start 60 | ``` 61 | 62 | ### run Protractor (for end-to-end) 63 | ```bash 64 | npm run webdriver:start 65 | # in another terminal 66 | npm run e2e:live 67 | ``` 68 | 69 | ### build Docker 70 | ```bash 71 | npm run build:docker 72 | ``` 73 | 74 | ### Tech 75 | The project use following frameworks, libs and repositories: 76 | - [Angular2](https://angular.io/) 77 | - [ngrx](https://github.com/ngrx/ngrx.github.io) - Reactive Extensions for Angular 2 78 | - [angular2-webpack-starter](https://github.com/AngularClass/angular2-webpack-starter) 79 | - [closure-library.d.ts](https://github.com/teppeis/closure-library.d.ts) 80 | 81 | 82 | -------------------------------------------------------------------------------- /frontend/config/github-deploy/index.js: -------------------------------------------------------------------------------- 1 | const helpers = require('../helpers'); 2 | const execSync = require('child_process').execSync; 3 | 4 | const REPO_NAME_RE = /Push {2}URL: https:\/\/github\.com\/.*\/(.*)\.git/; 5 | 6 | function getWebpackConfigModule() { 7 | if (helpers.hasProcessFlag('github-dev')) { 8 | return require('../webpack.dev.js'); 9 | } else if (helpers.hasProcessFlag('github-prod')) { 10 | return require('../webpack.prod.js'); 11 | } else { 12 | throw new Error('Invalid compile option.'); 13 | } 14 | } 15 | 16 | function getRepoName(remoteName) { 17 | remoteName = remoteName || 'origin'; 18 | 19 | var stdout = execSync('git remote show ' + remoteName), 20 | match = REPO_NAME_RE.exec(stdout); 21 | 22 | if (!match) { 23 | throw new Error('Could not find a repository on remote ' + remoteName); 24 | } else { 25 | return match[1]; 26 | } 27 | } 28 | 29 | function stripTrailing(str, char) { 30 | 31 | if (str[0] === char) { 32 | str = str.substr(1); 33 | } 34 | 35 | if (str.substr(-1) === char) { 36 | str = str.substr(0, str.length - 1); 37 | } 38 | 39 | return str; 40 | } 41 | 42 | /** 43 | * Given a string remove trailing slashes and adds 1 slash at the end of the string. 44 | * 45 | * Example: 46 | * safeUrl('/value/') 47 | * // 'value/' 48 | * 49 | * @param url 50 | * @returns {string} 51 | */ 52 | function safeUrl(url) { 53 | const stripped = stripTrailing(url || '', '/'); 54 | return stripped ? stripped + '/' : ''; 55 | } 56 | 57 | exports.getWebpackConfigModule = getWebpackConfigModule; 58 | exports.getRepoName = getRepoName; 59 | exports.safeUrl = safeUrl; 60 | -------------------------------------------------------------------------------- /frontend/config/head-config.common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration for head elements added during the creation of index.html. 3 | * 4 | * All href attributes are added the publicPath (if exists) by default. 5 | * You can explicitly hint to prefix a publicPath by setting a boolean value to a key that has 6 | * the same name as the attribute you want to operate on, but prefix with = 7 | * 8 | * Example: 9 | * { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true }, 10 | * Will prefix the publicPath to content. 11 | * 12 | * { rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png', '=href': false }, 13 | * Will not prefix the publicPath on href (href attributes are added by default 14 | * 15 | */ 16 | module.exports = { 17 | link: [ 18 | { rel: 'icon', type: 'image/png', href: '/assets/icon/favicon.ico' } 19 | ], 20 | meta: [ 21 | { name: 'msapplication-TileColor', content: '#00bcd4' }, 22 | { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true }, 23 | { name: 'theme-color', content: '#00bcd4' } 24 | ] 25 | }; 26 | -------------------------------------------------------------------------------- /frontend/config/helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | var path = require('path'); 5 | 6 | // Helper functions 7 | var ROOT = path.resolve(__dirname, '..'); 8 | 9 | function hasProcessFlag(flag) { 10 | return process.argv.join('').indexOf(flag) > -1; 11 | } 12 | 13 | function isWebpackDevServer() { 14 | return process.argv[1] && !! (/webpack-dev-server/.exec(process.argv[1])); 15 | } 16 | 17 | function root(args) { 18 | args = Array.prototype.slice.call(arguments, 0); 19 | return path.join.apply(path, [ROOT].concat(args)); 20 | } 21 | 22 | exports.hasProcessFlag = hasProcessFlag; 23 | exports.isWebpackDevServer = isWebpackDevServer; 24 | exports.root = root; 25 | -------------------------------------------------------------------------------- /frontend/config/modules/angular2-hmr-prod.js: -------------------------------------------------------------------------------- 1 | exports.HmrState = function() { 2 | 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/config/protractor.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | require('ts-node/register'); 6 | var helpers = require('./helpers'); 7 | 8 | exports.config = { 9 | baseUrl: 'http://localhost:3000/', 10 | 11 | // use `npm run e2e` 12 | specs: [ 13 | helpers.root('src/**/**.e2e.ts'), 14 | helpers.root('src/**/*.e2e.ts') 15 | ], 16 | exclude: [], 17 | 18 | framework: 'jasmine2', 19 | 20 | allScriptsTimeout: 110000, 21 | 22 | jasmineNodeOpts: { 23 | showTiming: true, 24 | showColors: true, 25 | isVerbose: false, 26 | includeStackTrace: false, 27 | defaultTimeoutInterval: 400000 28 | }, 29 | directConnect: true, 30 | 31 | capabilities: { 32 | 'browserName': 'chrome', 33 | 'chromeOptions': { 34 | 'args': ['show-fps-counter=true'] 35 | } 36 | }, 37 | 38 | onPrepare: function() { 39 | browser.ignoreSynchronization = true; 40 | }, 41 | 42 | /** 43 | * Angular 2 configuration 44 | * 45 | * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching 46 | * `rootEl` 47 | */ 48 | useAllAngular2AppRoots: true 49 | }; 50 | -------------------------------------------------------------------------------- /frontend/config/spec-bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | /* 6 | * When testing with webpack and ES6, we have to do some extra 7 | * things to get testing to work right. Because we are gonna write tests 8 | * in ES6 too, we have to compile those as well. That's handled in 9 | * karma.conf.js with the karma-webpack plugin. This is the entry 10 | * file for webpack test. Just like webpack will create a bundle.js 11 | * file for our client, when we run test, it will compile and bundle them 12 | * all here! Crazy huh. So we need to do some setup 13 | */ 14 | Error.stackTraceLimit = Infinity; 15 | 16 | require('core-js/es6'); 17 | require('core-js/es7/reflect'); 18 | 19 | // Typescript emit helpers polyfill 20 | require('ts-helpers'); 21 | 22 | require('zone.js/dist/zone'); 23 | require('zone.js/dist/long-stack-trace-zone'); 24 | require('zone.js/dist/proxy'); // since zone.js 0.6.15 25 | require('zone.js/dist/sync-test'); 26 | require('zone.js/dist/jasmine-patch'); // put here since zone.js 0.6.14 27 | require('zone.js/dist/async-test'); 28 | require('zone.js/dist/fake-async-test'); 29 | 30 | // RxJS 31 | require('rxjs/Rx'); 32 | 33 | require('script!imports?module=>undefined!blockly/blockly_compressed.js'); 34 | require('script!imports?module=>undefined!blockly/blocks_compressed.js'); 35 | require('script!imports?module=>undefined!blockly/msg/js/ru.js'); 36 | 37 | var testing = require('@angular/core/testing'); 38 | var browser = require('@angular/platform-browser-dynamic/testing'); 39 | 40 | testing.TestBed.initTestEnvironment( 41 | browser.BrowserDynamicTestingModule, 42 | browser.platformBrowserDynamicTesting() 43 | ); 44 | 45 | /* 46 | * Ok, this is kinda crazy. We can use the context method on 47 | * require that webpack created in order to tell webpack 48 | * what files we actually want to require or import. 49 | * Below, context will be a function/object with file names as keys. 50 | * Using that regex we are saying look in ../src then find 51 | * any file that ends with spec.ts and get its path. By passing in true 52 | * we say do this recursively 53 | */ 54 | var testContext = require.context('../src', true, /\.spec\.ts/); 55 | 56 | /* 57 | * get all the files, for each file, call the context function 58 | * that will require the file and load it up here. Context will 59 | * loop and require those spec files here 60 | */ 61 | function requireAll(requireContext) { 62 | return requireContext.keys().map(requireContext); 63 | } 64 | 65 | // requires and returns all modules that match 66 | var modules = requireAll(testContext); 67 | -------------------------------------------------------------------------------- /frontend/config/webpack.github-deploy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | const helpers = require('./helpers'); 5 | const ghDeploy = require('./github-deploy'); 6 | const webpackMerge = require('webpack-merge'); // used to merge webpack configs 7 | const ghpages = require('gh-pages'); 8 | const webpackConfig = ghDeploy.getWebpackConfigModule(); // the settings that are common to prod and dev 9 | 10 | 11 | /** 12 | * Webpack Constants 13 | */ 14 | const GIT_REMOTE_NAME = 'origin'; 15 | const COMMIT_MESSAGE = 'Updates'; 16 | const GH_REPO_NAME = ghDeploy.getRepoName(GIT_REMOTE_NAME); 17 | 18 | const METADATA = webpackMerge(webpackConfig.metadata, { 19 | /** 20 | * Prefixing the REPO name to the baseUrl for router support. 21 | * This also means all resource URIs (CSS/Images/JS) will have this prefix added by the browser 22 | * unless they are absolute (start with '/'). We will handle it via `output.publicPath` 23 | */ 24 | baseUrl: '/' + GH_REPO_NAME + '/' + ghDeploy.safeUrl(webpackConfig.metadata.baseUrl) 25 | }); 26 | 27 | module.exports = webpackMerge(webpackConfig, { 28 | /** 29 | * Merged metadata from webpack.common.js for index.html 30 | * 31 | * See: (custom attribute) 32 | */ 33 | metadata: METADATA, 34 | 35 | 36 | output: { 37 | /** 38 | * The public path is set to the REPO name. 39 | * 40 | * `HtmlElementsPlugin` will add it to all resources url's created by it. 41 | * `HtmlWebpackPlugin` will add it to all webpack bundels/chunks. 42 | * 43 | * In theory publicPath shouldn't be used since the browser should automatically prefix the 44 | * `baseUrl` into all URLs, however this is not the case when the URL is absolute (start with /) 45 | * 46 | * It's important to prefix & suffix the repo name with a slash (/). 47 | * Prefixing so every resource will be absolute (otherwise it will be url.com/repoName/repoName... 48 | * Suffixing since chunks will not do it automatically (testes against about page) 49 | */ 50 | publicPath: '/' + GH_REPO_NAME + '/' + ghDeploy.safeUrl(webpackConfig.output.publicPath) 51 | }, 52 | 53 | plugins: [ 54 | function() { 55 | this.plugin('done', function(stats) { 56 | console.log('Starting deployment to GitHub.'); 57 | 58 | const logger = function (msg) { 59 | console.log(msg); 60 | }; 61 | 62 | const options = { 63 | logger: logger, 64 | remote: GIT_REMOTE_NAME, 65 | message: COMMIT_MESSAGE 66 | }; 67 | 68 | ghpages.publish(webpackConfig.output.path, options, function(err) { 69 | if (err) { 70 | console.log('GitHub deployment done. STATUS: ERROR.'); 71 | throw err; 72 | } else { 73 | console.log('GitHub deployment done. STATUS: SUCCESS.'); 74 | } 75 | }); 76 | }); 77 | } 78 | ] 79 | }); 80 | -------------------------------------------------------------------------------- /frontend/d_ts/semantic.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace protractor{ 2 | 3 | interface ElementFinder{ 4 | dropdown(): any; 5 | modal(action: string): any; 6 | } 7 | 8 | } 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/karma.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | // Look in ./config for karma.conf.js 6 | module.exports = require('./config/karma.conf.js'); 7 | -------------------------------------------------------------------------------- /frontend/protractor.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | // look in ./config for protractor.conf.js 6 | exports.config = require('./config/protractor.conf.js').config; 7 | -------------------------------------------------------------------------------- /frontend/semantic.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": "semantic/", 3 | "paths": { 4 | "source": { 5 | "config": "src/theme.config", 6 | "definitions": "src/definitions/", 7 | "site": "src/site/", 8 | "themes": "src/themes/" 9 | }, 10 | "output": { 11 | "packaged": "dist/", 12 | "uncompressed": "dist/components/", 13 | "compressed": "dist/components/", 14 | "themes": "dist/themes/" 15 | }, 16 | "clean": "dist/" 17 | }, 18 | "permission": false, 19 | "autoInstall": true, 20 | "rtl": false, 21 | "version": "2.2.11" 22 | } -------------------------------------------------------------------------------- /frontend/src/app/actions/execution.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | 3 | export const ActionTypes = { 4 | LOAD: '[Execution] load executions', 5 | SET_CURRENT_ID: '[Execution] set current execution', 6 | LOAD_EXECUTION_DETAIL: '[Execution] load execution detail', 7 | LOAD_EXECUTION_LOG:'[Execution] load log for execution' 8 | }; 9 | 10 | export class LoadAction implements Action { 11 | type = ActionTypes.LOAD; 12 | 13 | constructor(public payload: any) { } 14 | } 15 | 16 | export class SetCurrentAction implements Action { 17 | type = ActionTypes.SET_CURRENT_ID; 18 | 19 | constructor(public payload: number) { } 20 | } 21 | 22 | export class LoadLogAction implements Action { 23 | type = ActionTypes.LOAD_EXECUTION_LOG; 24 | 25 | constructor(public payload: any) { } 26 | } 27 | 28 | export class LoadDetailAction implements Action { 29 | type = ActionTypes.LOAD_EXECUTION_DETAIL; 30 | 31 | constructor(public payload: any) { } 32 | } 33 | 34 | export type Actions 35 | = LoadAction | SetCurrentAction | LoadLogAction | LoadDetailAction; 36 | -------------------------------------------------------------------------------- /frontend/src/app/actions/info.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | 3 | export const ActionTypes = { 4 | SET_LOADED: '[Info] all you need is loaded', 5 | SET_LOADING: '[Info] loading ...', 6 | SET_SAVING: '[Info] saving ...', 7 | SET_SAVED: '[Info] saved!', 8 | SET_STEP: '[Info] set step' 9 | }; 10 | 11 | export class SetLoadedAction implements Action { 12 | type = ActionTypes.SET_LOADED; 13 | 14 | constructor(public payload?: any) { } 15 | } 16 | 17 | export class SetLoadingAction implements Action { 18 | type = ActionTypes.SET_LOADING; 19 | 20 | constructor(public payload?: any) { } 21 | } 22 | 23 | export class SetStepAction implements Action { 24 | type = ActionTypes.SET_STEP; 25 | 26 | constructor(public payload: string) { } 27 | } 28 | 29 | export class SetSavingAction implements Action{ 30 | type = ActionTypes.SET_SAVING; 31 | 32 | constructor(public payload?: string) { } 33 | } 34 | 35 | export class SetSavedAction implements Action{ 36 | type = ActionTypes.SET_SAVED; 37 | 38 | constructor(public payload?: string) { } 39 | } 40 | 41 | export type Actions 42 | = SetLoadedAction | SetLoadingAction | SetStepAction | SetSavingAction | SetSavedAction; 43 | -------------------------------------------------------------------------------- /frontend/src/app/actions/prInterfaceList.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | 3 | export const ActionTypes = { 4 | LOAD: '[Program Interfaces] load interfaces', 5 | SET_CURRENT_ID: '[Program Interfaces] set current interface', 6 | LOAD_INTERFACE_DETAIL:'[Program Interfaces] load interface detail' 7 | }; 8 | 9 | export class LoadAction implements Action { 10 | type = ActionTypes.LOAD; 11 | 12 | constructor(public payload: any) { } 13 | } 14 | 15 | export class SetCurrentAction implements Action { 16 | type = ActionTypes.SET_CURRENT_ID; 17 | 18 | constructor(public payload: number) { } 19 | } 20 | 21 | export class LoadDetailAction implements Action { 22 | type = ActionTypes.LOAD_INTERFACE_DETAIL; 23 | 24 | constructor(public payload: any) { } 25 | } 26 | 27 | export type Actions 28 | = LoadAction | SetCurrentAction | LoadDetailAction; 29 | -------------------------------------------------------------------------------- /frontend/src/app/actions/programList.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | 3 | export const ActionTypes = { 4 | LOAD: '[Programs] load programs', 5 | SET_CURRENT: '[Programs] set current program id', 6 | LOAD_DETAIL: '[Programs] load detail for program', 7 | }; 8 | 9 | export class LoadAction implements Action { 10 | type = ActionTypes.LOAD; 11 | 12 | constructor(public payload: any) { } 13 | } 14 | 15 | export class SetCurrentAction implements Action { 16 | type = ActionTypes.SET_CURRENT; 17 | 18 | constructor(public payload: number) { } 19 | } 20 | 21 | export class LoadDetailAction implements Action { 22 | type = ActionTypes.LOAD_DETAIL; 23 | 24 | constructor(public payload: any) { } 25 | } 26 | 27 | export type Actions 28 | = LoadAction | SetCurrentAction | LoadDetailAction; 29 | -------------------------------------------------------------------------------- /frontend/src/app/actions/referenceList.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | 3 | export const ActionTypes = { 4 | LOAD: '[References] load references', 5 | // SET_CURRENT: '[Programs] set current program id', 6 | LOAD_DETAIL: '[References] load detail for reference', 7 | }; 8 | 9 | export class LoadAction implements Action { 10 | type = ActionTypes.LOAD; 11 | 12 | constructor(public payload: any) { } 13 | } 14 | 15 | export class LoadDetailAction implements Action { 16 | type = ActionTypes.LOAD_DETAIL; 17 | 18 | constructor(public payload: any) { } 19 | } 20 | 21 | // export class SetCurrentAction implements Action { 22 | // type = ActionTypes.SET_CURRENT; 23 | // 24 | // constructor(public payload: number) { } 25 | // } 26 | // 27 | // export class LoadDetailAction implements Action { 28 | // type = ActionTypes.LOAD_DETAIL; 29 | // 30 | // constructor(public payload: any) { } 31 | // } 32 | 33 | export type Actions 34 | = LoadAction | LoadDetailAction; 35 | -------------------------------------------------------------------------------- /frontend/src/app/actions/versionList.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | 3 | export const ActionTypes = { 4 | LOAD: '[Versions] load Versions', 5 | SET_CURRENT: '[Versions] set current Version', 6 | LOAD_DETAIL:'[Versions] load Version detail', 7 | SAVE:'[Versions] save current version', 8 | CREATE:'[Versions] create new version' 9 | }; 10 | 11 | export class LoadAction implements Action { 12 | type = ActionTypes.LOAD; 13 | 14 | constructor(public payload: any) { } 15 | } 16 | 17 | export class SetCurrentAction implements Action { 18 | type = ActionTypes.SET_CURRENT; 19 | 20 | constructor(public payload: number) { } 21 | } 22 | 23 | export class LoadDetailAction implements Action { 24 | type = ActionTypes.LOAD_DETAIL; 25 | 26 | constructor(public payload: any) { } 27 | } 28 | 29 | export class createAction implements Action { 30 | type = ActionTypes.CREATE; 31 | 32 | constructor(public payload: any) { } 33 | } 34 | 35 | export class saveAction implements Action { 36 | type = ActionTypes.SAVE; 37 | 38 | constructor(public payload: any) { } 39 | } 40 | 41 | export type Actions 42 | = LoadAction | SetCurrentAction | LoadDetailAction | createAction | saveAction; 43 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | html, body{ 2 | height: 100%; 3 | font-family: 'Segoe UI', 'Helvetica Neue', Arial, Helvetica, sans-serif 4 | } 5 | 6 | span.active { 7 | background-color: gray; 8 | } 9 | 10 | body { 11 | background: #f5f5f5; 12 | } 13 | 14 | .md-list-item { 15 | cursor: pointer; 16 | } 17 | 18 | .ui.positive.buttons .active.button, .ui.positive.buttons .active.button:active, .ui.positive.active.button, .ui.positive.button .active.button:active 19 | { 20 | background-color: #009688!important; 21 | } 22 | 23 | .ui.positive.buttons .button, .ui.positive.button 24 | { 25 | background-color: #009688!important; 26 | } 27 | 28 | .blocklyTreeRow { 29 | padding-top: 0.75rem !important; 30 | padding-bottom: 2rem !important; 31 | border-left-width: 12px !important; 32 | } 33 | .blocklyTreeRoot div div div div .blocklyTreeRow { 34 | border-left-width: 18px !important; 35 | padding-top: 0px !important; 36 | padding-bottom: 1.6rem !important; 37 | } 38 | 39 | 40 | .btn-group { 41 | position: absolute; 42 | top: 10px; 43 | right: 10px; 44 | } 45 | 46 | .btn-group-item { 47 | position: relative; 48 | float: left; 49 | line-height: 20px; 50 | 51 | display: inline-block; 52 | padding: 6px 12px; 53 | font-size: 12px; 54 | font-weight: 600; 55 | color: #333; 56 | white-space: nowrap; 57 | vertical-align: middle; 58 | cursor: pointer; 59 | -webkit-user-select: none; 60 | -moz-user-select: none; 61 | -ms-user-select: none; 62 | user-select: none; 63 | background-color: #eee; 64 | background-image: -webkit-linear-gradient(#fcfcfc, #eee); 65 | background-image: linear-gradient(#fcfcfc, #eee); 66 | border: 1px solid #d5d5d5; 67 | } 68 | 69 | .btn-group-item:focus{ 70 | outline: none!important; 71 | } 72 | 73 | .btn-group-item:hover{ 74 | background-image: linear-gradient(#eee, #ddd); 75 | } 76 | 77 | .btn-group-item:first-child { 78 | border-top-left-radius: 3px; 79 | border-bottom-left-radius: 3px; 80 | } 81 | 82 | .btn-group-item:last-child { 83 | border-right-width: 1px; 84 | border-top-right-radius: 3px; 85 | border-bottom-right-radius: 3px; 86 | } 87 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewEncapsulation, Inject} from '@angular/core'; 2 | import {BreadcrumbComponent} from './components/breadcrumb.component'; 3 | 4 | import { Store } from '@ngrx/store'; 5 | import * as fromRoot from './reducers'; 6 | import {Observable} from "rxjs"; 7 | 8 | import * as actionsInfo from "./actions/info"; 9 | import {FetchService} from "./services/fetch.service"; 10 | import {ActivatedRoute} from "@angular/router"; 11 | 12 | 13 | @Component({ 14 | selector: 'app', 15 | encapsulation: ViewEncapsulation.None, 16 | styleUrls: [ 17 | './css/semantic.css', 18 | './app.component.css' 19 | ], 20 | template: ` 21 |
22 | 23 | 24 | 25 |
26 | ` 27 | }) 28 | export class App { 29 | private step: any; 30 | 31 | constructor( 32 | private store: Store, 33 | private fetch: FetchService, 34 | private route: ActivatedRoute 35 | ) { 36 | this.step = this.store.let(fromRoot.getStep); 37 | 38 | this.step.subscribe(step => { 39 | this.fetch.fetchAllWeNeed(step); 40 | }); 41 | } 42 | 43 | ngOnInit() { 44 | } 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /frontend/src/app/app.resolver.ts: -------------------------------------------------------------------------------- 1 | import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs/Observable'; 4 | import 'rxjs/add/observable/of'; 5 | 6 | @Injectable() 7 | export class DataResolver implements Resolve { 8 | constructor() { 9 | 10 | } 11 | resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { 12 | return Observable.of({ res: 'I am data'}); 13 | } 14 | } 15 | 16 | // an array of services to resolve routes with data 17 | export const APP_RESOLVER_PROVIDERS = [ 18 | DataResolver 19 | ]; 20 | -------------------------------------------------------------------------------- /frontend/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from '@angular/router'; 2 | import { NoContentComponent } from './components/no-content/no-content.component'; 3 | 4 | import { DataResolver } from './app.resolver'; 5 | import {EditorComponent} from "./components/editor/editor.component"; 6 | import {HomePage} from "./pages/HomePage"; 7 | import {InterfaceListPage} from "./pages/InterfaceListPage"; 8 | import {ProgramListPage} from "./pages/ProgramListPage"; 9 | import {VersionListPage} from "./pages/VersionListPage"; 10 | import {EditorPage} from "./pages/EditorPage"; 11 | import {ExecutionListPage} from "./pages/ExecutionListPage"; 12 | import {ReadonlyEditorPage} from "./pages/ReadonlyEditorPage"; 13 | 14 | 15 | export const ROUTES: Routes = [ 16 | { path: '', component: HomePage }, 17 | { 18 | path: 'execution', 19 | children: [ 20 | { 21 | path: '', 22 | component: ExecutionListPage 23 | }, 24 | { 25 | path: ':executionID', 26 | component: ReadonlyEditorPage 27 | } 28 | ] 29 | }, 30 | { 31 | path: 'interface', 32 | children: [ 33 | { 34 | path: '', 35 | component: InterfaceListPage 36 | }, 37 | { 38 | path: ':interfaceID', 39 | children: [ 40 | { 41 | path: '', 42 | redirectTo: 'program', 43 | pathMatch: 'full' 44 | }, 45 | { 46 | path: 'program', 47 | children: [ 48 | { 49 | path: '', 50 | component: ProgramListPage 51 | }, 52 | { 53 | path: ':programID', 54 | children:[ 55 | { 56 | path: '', 57 | redirectTo: 'version', 58 | pathMatch: 'full' 59 | 60 | }, 61 | { 62 | path:'version', 63 | children: [ 64 | { 65 | path: '', 66 | component: VersionListPage 67 | }, 68 | { 69 | path: ':versionID', 70 | component: EditorPage 71 | } 72 | ] 73 | 74 | } 75 | ] 76 | 77 | } 78 | ] 79 | } 80 | 81 | ] 82 | } 83 | ] 84 | }, 85 | { path: '**', component: NoContentComponent }, 86 | ]; 87 | -------------------------------------------------------------------------------- /frontend/src/app/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | export type InternalStateType = { 4 | [key: string]: any 5 | }; 6 | 7 | @Injectable() 8 | export class AppState { 9 | _state: InternalStateType = { }; 10 | 11 | constructor() { 12 | 13 | } 14 | 15 | // already return a clone of the current state 16 | get state() { 17 | return this._state = this._clone(this._state); 18 | } 19 | // never allow mutation 20 | set state(value) { 21 | throw new Error('do not mutate the `.state` directly'); 22 | } 23 | 24 | 25 | get(prop?: any) { 26 | // use our state getter for the clone 27 | const state = this.state; 28 | return state.hasOwnProperty(prop) ? state[prop] : state; 29 | } 30 | 31 | set(prop: string, value: any) { 32 | // internally mutate our state 33 | return this._state[prop] = value; 34 | } 35 | 36 | 37 | private _clone(object: InternalStateType) { 38 | // simple object clone 39 | return JSON.parse(JSON.stringify( object )); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/app/blocks/consts/blockTypes.ts: -------------------------------------------------------------------------------- 1 | import {colorPeach, colorBlue, colorMint, colorRed, colorGrey} from "./colors"; 2 | 3 | const block_reference = { 4 | title: 'business_logic_reference', 5 | color: colorBlue 6 | }; 7 | 8 | const block_field_get = { 9 | title: 'business_logic_argument_field_get', 10 | color: colorMint 11 | }; 12 | 13 | const block_field_set = { 14 | title: 'business_logic_argument_field_set', 15 | color: colorMint 16 | }; 17 | 18 | const block_function = { 19 | title: 'business_logic_function', 20 | color: colorGrey 21 | }; 22 | 23 | const block_date = { 24 | title: 'business_logic_date', 25 | color: colorPeach 26 | }; 27 | 28 | export {block_reference, block_function, block_field_get, block_field_set, block_date}; 29 | -------------------------------------------------------------------------------- /frontend/src/app/blocks/consts/colors.ts: -------------------------------------------------------------------------------- 1 | const colorPeach = '#efa360'; 2 | const colorBlue = '#0078d7'; 3 | const colorMint = '#35bdb2'; 4 | const colorRed = '#922239'; 5 | const colorGrey = '#50536a'; 6 | 7 | const colorActive = '#3ca682'; 8 | 9 | export {colorPeach, colorBlue, colorMint, colorRed, colorGrey, colorActive}; 10 | -------------------------------------------------------------------------------- /frontend/src/app/blocks/fields/argument_field.ts: -------------------------------------------------------------------------------- 1 | import {stateService} from "../../services/state.service"; 2 | import find = require("lodash/find"); 3 | 4 | export class ArgumentField extends Blockly.FieldDropdown{ 5 | state: stateService; 6 | 7 | menuGenerator_ = () => { 8 | let args = this.state.getArguments(); 9 | let options = []; 10 | 11 | args.forEach( (arg) => { 12 | let arg_name = arg["name"]; 13 | arg.fields.forEach((field) => { 14 | options.push([field["verbose_name"], arg_name + '.' + field["name"]]); 15 | }); 16 | }); 17 | 18 | return options; 19 | }; 20 | 21 | constructor(text: string, _state: stateService){ 22 | super([['', '']]); 23 | this.state = _state; 24 | } 25 | 26 | setValue(newValue: string){ 27 | if (newValue === null || newValue === this.value_) { 28 | return; // No change if null. 29 | } 30 | if (this.sourceBlock_ && Blockly.Events.isEnabled()) { 31 | Blockly.Events.fire(new Blockly.Events.Change( 32 | this.sourceBlock_, 'field', this.name, this.value_, newValue)); 33 | } 34 | this.value_ = newValue; 35 | 36 | if(this.state){ 37 | let args = this.state.getArguments(); 38 | 39 | if(args){ 40 | let ind: number = this.getValue().indexOf("."); 41 | let arg_name: string = this.getValue().substr(0, ind); 42 | let field_name: string = this.getValue().substr(ind+1, this.getValue().length); 43 | 44 | let arg: any = find(args, (arg) => {return arg["name"] == arg_name;}); 45 | 46 | let result: any = find(arg.fields, (field) => {return field["name"] == field_name}); 47 | 48 | if(result){ 49 | this.setText( result["verbose_name"] ); 50 | }else{ 51 | this.setText( this.getValue() ); 52 | } 53 | } 54 | 55 | } 56 | 57 | 58 | } 59 | 60 | getValue(){ 61 | return this.value_; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /frontend/src/app/blocks/fields/func_label_field.ts: -------------------------------------------------------------------------------- 1 | export class FunctionLabelField extends Blockly.FieldLabel { 2 | 3 | EDITABLE = true; 4 | 5 | constructor(){ 6 | super(''); 7 | } 8 | 9 | setValue(newValue: string){ 10 | if (newValue === null || newValue === this.value_) { 11 | return; // No change if null. 12 | } 13 | if (this.sourceBlock_ && Blockly.Events.isEnabled()) { 14 | Blockly.Events.fire(new Blockly.Events.Change( 15 | this.sourceBlock_, 'field', this.name, this.value_, newValue)); 16 | } 17 | this.value_ = newValue; 18 | 19 | this.setText(this.getValue()); 20 | 21 | } 22 | 23 | getValue(){ 24 | return this.value_; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/app/blocks/fields/ref_dropdown_field.ts: -------------------------------------------------------------------------------- 1 | import {stateService} from "../../services/state.service"; 2 | import {FetchService} from "../../services/fetch.service"; 3 | import find = require("lodash/find"); 4 | 5 | export class ReferenceDropdownField extends Blockly.FieldDropdown { 6 | state: stateService; 7 | fetch: FetchService; 8 | 9 | menuGenerator_ = () => { 10 | return this.options; 11 | }; 12 | 13 | options: any = []; 14 | 15 | constructor(_state: stateService, _fetch: FetchService){ 16 | super([ ['', ''] ]); 17 | this.state = _state; 18 | this.fetch = _fetch; 19 | } 20 | 21 | setValue(newValue: string){ 22 | if (newValue === null || newValue === this.value_) { 23 | return; // No change if null. 24 | } 25 | if (this.sourceBlock_ && Blockly.Events.isEnabled()) { 26 | Blockly.Events.fire(new Blockly.Events.Change( 27 | this.sourceBlock_, 'field', this.name, this.value_, newValue)); 28 | } 29 | this.value_ = newValue; 30 | 31 | if(this.sourceBlock_ != null){ 32 | let referenceName = this.sourceBlock_.inputList[0].fieldRow[0].getValue(); 33 | 34 | //TODO: don't fetch if exist 35 | this.fetch.loadReferenceDetail(referenceName).subscribe(() => { 36 | let ref = this.state.getState()["references"].details[referenceName]; 37 | 38 | this.options = []; 39 | ref.results.forEach((opt) => { 40 | this.options.push([ ''+opt.name, ''+opt.id ]); 41 | }); 42 | 43 | if(this.getValue() == '-1') 44 | return this.setText("Choose value"); 45 | 46 | let options = this.getOptions_(); 47 | 48 | for(let i = 0; i < options.length; i++){ 49 | if(options[i][1] == newValue) 50 | return this.setText(options[i][0]); 51 | } 52 | 53 | }); 54 | 55 | } 56 | 57 | } 58 | 59 | getValue(){ 60 | return this.value_; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /frontend/src/app/blocks/fields/ref_label_field.ts: -------------------------------------------------------------------------------- 1 | import {stateService} from "../../services/state.service"; 2 | import find = require("lodash/find"); 3 | 4 | export class ReferenceLabelField extends Blockly.FieldLabel { 5 | state: stateService; 6 | 7 | EDITABLE = true; 8 | 9 | constructor(_state: stateService){ 10 | super(''); 11 | this.state = _state; 12 | } 13 | 14 | setValue(newValue: string){ 15 | if (newValue === null || newValue === this.value_) { 16 | return; // No change if null. 17 | } 18 | if (this.sourceBlock_ && Blockly.Events.isEnabled()) { 19 | Blockly.Events.fire(new Blockly.Events.Change( 20 | this.sourceBlock_, 'field', this.name, this.value_, newValue)); 21 | } 22 | this.value_ = newValue; 23 | 24 | if(this.state){ 25 | let references = this.state.getState()["references"].entities; 26 | let ref = find(references, ref => { 27 | return ref["name"] == this.getValue(); 28 | }); 29 | 30 | if(ref){ 31 | this.setText(ref["verbose_name"]); 32 | }else{ 33 | this.setText(this.getValue()); 34 | } 35 | 36 | //TODO: set Tooltip 37 | // this.setTooltip(" ["+this.getValue()+"]"); 38 | }else{ 39 | this.setText(this.getValue()); 40 | } 41 | } 42 | 43 | getValue(){ 44 | return this.value_; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend/src/app/components/blockly/blockly.component.spec.ts: -------------------------------------------------------------------------------- 1 | // import { 2 | // inject, 3 | // TestBed, 4 | // ComponentFixture, 5 | // async 6 | // } from '@angular/core/testing'; 7 | // import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; 8 | // import { By } from '@angular/platform-browser'; 9 | // 10 | // 11 | // import { BlocklyComponent } from './blockly.component'; 12 | // 13 | // import {MockService} from "./blocks/mock.service"; 14 | // import { BlocksService } from "./blocks/blocks.service"; 15 | // import { Router, ActivatedRoute, Params } from '@angular/router'; 16 | // 17 | // require('script!imports?module=>undefined!blockly/blockly_compressed.js'); 18 | // require('script!imports?module=>undefined!blockly/blocks_compressed.js'); 19 | // require('script!imports?module=>undefined!blockly/msg/js/ru.js'); 20 | // 21 | // describe('blockly component', () => { 22 | // let workspace: any; 23 | // let comp: BlocklyComponent; 24 | // let fixture: ComponentFixture; 25 | // 26 | // // provide our implementations or mocks to the dependency injector 27 | // beforeEach( async(() => { 28 | // TestBed.configureTestingModule({ 29 | // declarations: [ BlocklyComponent ], 30 | // providers: [ 31 | // // {provide: BackendService, useClass: MockService}, 32 | // {provide: ActivatedRoute, useValue: ''}, 33 | // {provide: Router, useValue: ''}, 34 | // BlocksService 35 | // ], 36 | // schemas:[ NO_ERRORS_SCHEMA ] 37 | // }); 38 | // 39 | // workspace = new Blockly.Workspace(); 40 | // fixture = TestBed.createComponent(BlocklyComponent); 41 | // comp = fixture.componentInstance; 42 | // 43 | // }) ); 44 | // 45 | // // it('swap services', () => { 46 | // // expect(TestBed.get(Router)).toEqual(''); 47 | // // expect(TestBed.get(ActivatedRoute)).toEqual(''); 48 | // // }); 49 | // 50 | // }); 51 | -------------------------------------------------------------------------------- /frontend/src/app/components/helpcard.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | ViewChild, 5 | } from '@angular/core'; 6 | import {Store, State} from "@ngrx/store"; 7 | import {Observable} from "rxjs"; 8 | import {stateService} from "../services/state.service"; 9 | 10 | 11 | @Component({ 12 | selector: 'help-card', 13 | template: ` 14 |
15 |
16 |
Helper
17 |
18 |
19 | 20 |

Previous value :

21 |
22 |
23 |
24 |
25 | {{previous_value}} 26 |
27 |
28 |
29 |
30 |

Current value :

31 |
32 |
33 |
34 |
35 | {{current_value}} 36 |
37 |
38 |
39 |
40 | 41 |
42 |
`, 43 | styleUrls: ['../css/helpcard.component.css'], 44 | providers: [] 45 | }) 46 | 47 | 48 | export class HelpCardComponent { 49 | @Input() previous_value: any; 50 | @Input() current_value: any; 51 | } 52 | -------------------------------------------------------------------------------- /frontend/src/app/components/list.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input, Output, EventEmitter} from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: 'block-list', 6 | template: ` 7 |
8 |
9 | 10 |
11 | {{item.title || item.id}} 12 |
{{item.description}}
13 | 14 |
15 | Launched: {{item.start_time | date: 'dd.MM.yyyy HH:mm:ss'}} 16 | run time: {{ calculateDelta(item.finish_time, item.start_time) }} (seconds) 17 |
18 | 19 |
20 | Updated: {{item.modification_time | date: 'dd.MM.yyyy HH:mm:ss'}} 21 |
22 | 23 |
24 |
25 |
26 | 27 | `, 28 | styleUrls: ['../css/list.component.css'] 29 | }) 30 | export class ListComponent { 31 | 32 | @Input() list: any; 33 | @Input() list_icon: string; 34 | @Output() select = new EventEmitter(); 35 | 36 | onSelect(item){ 37 | this.select.emit(item); 38 | } 39 | 40 | calculateDelta(finish: string, start: string): any{ 41 | return Math.abs(+new Date(finish) - (+new Date(start)) ) / 1000; 42 | } 43 | 44 | ngOnDestroy(){ 45 | this.list = []; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /frontend/src/app/components/modal.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | Output, 5 | EventEmitter, 6 | } from '@angular/core'; 7 | import { 8 | Validators 9 | } from '@angular/forms'; 10 | import {PostService} from "../services/post.service"; 11 | 12 | @Component({ 13 | selector: 'modal', 14 | template: ` 15 | ` 45 | 46 | }) 47 | 48 | export class ModalComponent{ 49 | version: any = {}; 50 | 51 | @Input() title: any; 52 | @Input() description: any; 53 | @Input() content: any; 54 | @Input() header: any; 55 | 56 | @Input() title_value: any; 57 | @Input() description_value: any; 58 | 59 | @Output() submit = new EventEmitter(); 60 | 61 | constructor() { 62 | 63 | } 64 | 65 | onSubmit(): void { 66 | this.submit.emit({ 67 | title: this.title_value, 68 | description: this.description_value 69 | }); 70 | } 71 | 72 | show(){ 73 | $("#modal").modal('show'); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /frontend/src/app/components/no-content/no-content.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, NgModule } from '@angular/core'; 2 | import { Router, ActivatedRoute, Params } from '@angular/router'; 3 | import { AppState } from '../../app.service'; 4 | 5 | @Component({ 6 | selector: 'no-content', 7 | template: `No Content!` 8 | }) 9 | 10 | export class NoContentComponent{ 11 | private programs; 12 | 13 | constructor( 14 | private route: ActivatedRoute, 15 | private router: Router){ 16 | 17 | } 18 | 19 | ngOnInit() { 20 | 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/app/css/breadcrumb.component.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::before, 3 | *::after { 4 | box-sizing: border-box; 5 | } 6 | 7 | .md { 8 | /*position: absolute;*/ 9 | /*left: 50%;*/ 10 | /*top: 50%;*/ 11 | /*transform: translate(-50%, -50%);*/ 12 | width: 100%; 13 | } 14 | /* ------------------------- Separate line ------------------------- */ 15 | .breadcrumb { 16 | text-align: center; 17 | display: inline-block; 18 | box-shadow: 0 2px 5px rgba(0,0,0,0.25); 19 | overflow: hidden; 20 | border-radius: 5px; 21 | counter-reset: flag; 22 | margin-top: 10px; 23 | } 24 | 25 | .breadcrumb > a { 26 | text-decoration: none; 27 | outline: none; 28 | display: block; 29 | float: left; 30 | font-size: 16px; 31 | line-height: 36px; 32 | color: #fff; 33 | padding: 0 10px 0 60px; 34 | position: relative; 35 | } 36 | 37 | .breadcrumb > a:first-child { 38 | padding-left: 46px; 39 | border-radius: 5px 0 0 5px; 40 | } 41 | 42 | .breadcrumb > a:first-child::before { 43 | left: 14px; 44 | } 45 | 46 | .breadcrumb > a:last-child { 47 | border-radius: 0 5px 5px 0; 48 | padding-right: 20px; 49 | } 50 | 51 | .breadcrumb > a:last-child::after { 52 | content: none; 53 | } 54 | 55 | .breadcrumb > a::before { 56 | content: counter(flag); 57 | counter-increment: flag; 58 | border-radius: 100%; 59 | width: 20px; 60 | height: 20px; 61 | line-height: 20px; 62 | margin: 8px 0; 63 | position: absolute; 64 | top: 0; 65 | left: 30px; 66 | font-weight: bold; 67 | } 68 | 69 | .breadcrumb > a::after { 70 | content: ''; 71 | position: absolute; 72 | top: 0; 73 | right: -18px; 74 | width: 36px; 75 | height: 36px; 76 | transform: scale(0.707) rotate(45deg); 77 | z-index: 1; 78 | box-shadow: 2px -2px 0 2px #343434; 79 | border-radius: 0 5px 0 50px; 80 | } 81 | 82 | .flat a, 83 | .flat a::after { 84 | background: #e0e0e0; 85 | color: #393939; 86 | transition: all 0.5s; 87 | } 88 | 89 | .flat a::before { 90 | background: #fff; 91 | box-shadow: 0 0 0 1px #393939; 92 | } 93 | 94 | .flat a:hover, 95 | .flat a.active, 96 | .flat a:hover::after, 97 | .flat a.active::after { 98 | background: #343434; 99 | } 100 | 101 | .flat a.active:hover { 102 | background: #393939; 103 | } 104 | 105 | .flat a:hover, 106 | .flat a.active { 107 | color: #fff; 108 | } 109 | 110 | .flat a:hover::before, 111 | .flat a.active::before { 112 | color: #393939; 113 | } 114 | 115 | .breadcrumbs{ 116 | padding-top: 15px; 117 | padding-left: 10px; 118 | } 119 | 120 | .notActiveLink { 121 | /*background: #e0e0e0!important;*/ 122 | /*color: #505050 !important;*/ 123 | /*font-style: oblique;*/ 124 | font-weight: bold; 125 | } 126 | 127 | .notActiveLink > a { 128 | color: #505050 !important; 129 | } 130 | -------------------------------------------------------------------------------- /frontend/src/app/css/helpcard.component.css: -------------------------------------------------------------------------------- 1 | .card{ 2 | position: absolute;right: 2rem;bottom: 1rem; 3 | box-shadow: 2px 2px 2px 0 rgba(0,0,0,0.16),0 0 0 1px rgba(0,0,0,0.08); 4 | } 5 | 6 | .card.inverted{ 7 | background: #1B1C1D; 8 | } 9 | 10 | .card.inverted > .content > .header{ 11 | color: #fff!important; 12 | } 13 | 14 | 15 | /*.ui.feed > .event > .content .summary{*/ 16 | /*color: #fff!important;*/ 17 | /*}*/ 18 | -------------------------------------------------------------------------------- /frontend/src/app/css/list.component.css: -------------------------------------------------------------------------------- 1 | .color_blue { 2 | color: #4183C4 !important 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/app/environment.ts: -------------------------------------------------------------------------------- 1 | 2 | // Angular 2 3 | // rc2 workaround 4 | import { enableDebugTools, disableDebugTools } from '@angular/platform-browser'; 5 | import { enableProdMode, ApplicationRef } from '@angular/core'; 6 | // Environment Providers 7 | let PROVIDERS: any[] = [ 8 | // common env directives 9 | ]; 10 | 11 | // Angular debug tools in the dev console 12 | // https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md 13 | let _decorateModuleRef = function identity(value: T): T { return value; }; 14 | 15 | if ('production' === ENV) { 16 | // Production 17 | disableDebugTools(); 18 | enableProdMode(); 19 | 20 | PROVIDERS = [ 21 | ...PROVIDERS, 22 | // custom providers in production 23 | ]; 24 | 25 | } else { 26 | 27 | _decorateModuleRef = (modRef: any) => { 28 | const appRef = modRef.injector.get(ApplicationRef); 29 | const cmpRef = appRef.components[0]; 30 | 31 | let _ng = (window).ng; 32 | enableDebugTools(cmpRef); 33 | (window).ng.probe = _ng.probe; 34 | (window).ng.coreTokens = _ng.coreTokens; 35 | return modRef; 36 | }; 37 | 38 | // Development 39 | PROVIDERS = [ 40 | ...PROVIDERS, 41 | // custom providers in development 42 | ]; 43 | 44 | } 45 | 46 | export const decorateModuleRef = _decorateModuleRef; 47 | 48 | export const ENV_PROVIDERS = [ 49 | ...PROVIDERS 50 | ]; 51 | -------------------------------------------------------------------------------- /frontend/src/app/index.ts: -------------------------------------------------------------------------------- 1 | // App 2 | export * from './app.module'; 3 | -------------------------------------------------------------------------------- /frontend/src/app/pages/ExecutionListPage.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation } from '@angular/core'; 2 | import {Router, ActivatedRoute} from "@angular/router"; 3 | 4 | import { Store } from '@ngrx/store'; 5 | import * as fromRoot from '../reducers'; 6 | 7 | import {Observable} from "rxjs"; 8 | import {RestService} from "../services/rest.service"; 9 | 10 | import * as actionsInfo from "../actions/info"; 11 | import * as actions from "../actions/execution"; 12 | 13 | @Component({ 14 | selector: 'execution-list-page', 15 | template: `` 16 | }) 17 | export class ExecutionListPage { 18 | list: any; 19 | icon = "asterisk"; 20 | 21 | constructor( 22 | private route: ActivatedRoute, 23 | private router: Router, 24 | private store: Store, 25 | private rest: RestService) { 26 | 27 | this.list = this.store.let(fromRoot.getExecutions); 28 | 29 | } 30 | 31 | ngOnInit() { 32 | this.store.dispatch(new actionsInfo.SetStepAction("ExecutionList")); 33 | } 34 | 35 | onSelect(item){ 36 | this.store.dispatch(new actions.SetCurrentAction(item["id"])); 37 | this.router.navigate([item["id"]], { relativeTo: this.route }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /frontend/src/app/pages/HomePage.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation } from '@angular/core'; 2 | import {Router, ActivatedRoute} from "@angular/router"; 3 | import * as actionsInfo from "../actions/info"; 4 | import {Store} from "@ngrx/store"; 5 | import * as fromRoot from '../reducers'; 6 | 7 | @Component({ 8 | selector: 'home-page', 9 | template: ` 10 | 11 |
12 |
13 |
14 |
Interfaces
15 |
16 |
17 | List of program interfaces. 18 |
19 |
20 |
21 |
22 |
23 |
Execution
24 |
25 |
26 | List of calculation logs. 27 |
28 |
29 |
30 |
31 | 32 | 33 | `, 34 | styles: [` 35 | .card{ 36 | cursor: pointer 37 | }`] 38 | }) 39 | export class HomePage { 40 | list: any = [ 41 | { 42 | title: 'Interfaces', 43 | description: '', 44 | link: './interface' 45 | }, 46 | { 47 | title: 'Execution', 48 | description: '', 49 | link: './execution' 50 | } 51 | ]; 52 | 53 | constructor( 54 | private route: ActivatedRoute, 55 | private router: Router, 56 | private store: Store) { 57 | } 58 | 59 | ngOnInit() { 60 | this.store.dispatch(new actionsInfo.SetStepAction("Home")); 61 | } 62 | 63 | onSelect(item){ 64 | this.router.navigate([ item["link"] ], { relativeTo: this.route }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /frontend/src/app/pages/InterfaceListPage.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation } from '@angular/core'; 2 | import {Router, ActivatedRoute} from "@angular/router"; 3 | 4 | import { Store } from '@ngrx/store'; 5 | import * as fromRoot from '../reducers'; 6 | 7 | import {Observable} from "rxjs"; 8 | import {RestService} from "../services/rest.service"; 9 | 10 | import * as actions from "../actions/prInterfaceList"; 11 | import * as actionsInfo from "../actions/info"; 12 | 13 | @Component({ 14 | selector: 'interface-list-page', 15 | template: `` 16 | }) 17 | export class InterfaceListPage { 18 | list: any; 19 | icon = 'folder open'; 20 | 21 | constructor( 22 | private route: ActivatedRoute, 23 | private router: Router, 24 | private store: Store, 25 | private rest: RestService) { 26 | 27 | this.list = this.store.let(fromRoot.getInterfaces); 28 | 29 | } 30 | 31 | ngOnInit() { 32 | this.store.dispatch(new actionsInfo.SetStepAction("InterfaceList")); 33 | } 34 | 35 | onSelect(item){ 36 | this.store.dispatch(new actions.SetCurrentAction(item["id"])); 37 | this.router.navigate([item["id"]], { relativeTo: this.route }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /frontend/src/app/pages/ProgramListPage.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation } from '@angular/core'; 2 | import {Router, ActivatedRoute} from "@angular/router"; 3 | 4 | import { Store } from '@ngrx/store'; 5 | import * as fromRoot from '../reducers'; 6 | 7 | import * as actionsInfo from "../actions/info"; 8 | 9 | import {Observable} from "rxjs"; 10 | import {RestService} from "../services/rest.service"; 11 | import * as actionsProgram from "../actions/programList"; 12 | import * as actionsInterface from "../actions/prInterfaceList"; 13 | 14 | 15 | @Component({ 16 | selector: 'program-list-page', 17 | template: `` 18 | }) 19 | export class ProgramListPage { 20 | list: any; 21 | icon = 'folder open'; 22 | 23 | private interfaceID: number; 24 | 25 | constructor( 26 | private route: ActivatedRoute, 27 | private router: Router, 28 | private store: Store, 29 | private rest: RestService) { 30 | 31 | this.list = this.store.let(fromRoot.getPrograms); 32 | 33 | } 34 | 35 | ngOnInit() { 36 | this.store.dispatch(new actionsInfo.SetStepAction("ProgramList")); 37 | 38 | this.route["params"].subscribe(params => { 39 | this.store.dispatch(new actionsInterface.SetCurrentAction(+params["interfaceID"])); 40 | }); 41 | } 42 | 43 | onSelect(item){ 44 | this.store.dispatch(new actionsProgram.SetCurrentAction(item["id"])); 45 | this.router.navigate([item["id"]], { relativeTo: this.route }); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /frontend/src/app/pages/ReadonlyEditorPage.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation } from '@angular/core'; 2 | import {Router, ActivatedRoute} from "@angular/router"; 3 | 4 | import { Store } from '@ngrx/store'; 5 | import * as fromRoot from '../reducers'; 6 | 7 | import * as actionsInfo from '../actions/info'; 8 | import * as actionsExecution from '../actions/execution'; 9 | 10 | @Component({ 11 | selector: 'readonly-editor-page', 12 | template: ` 13 | 14 | ` 15 | }) 16 | export class ReadonlyEditorPage { 17 | version: any; 18 | 19 | constructor( 20 | private route: ActivatedRoute, 21 | private router: Router, 22 | private store: Store) { 23 | 24 | // this.version = this.store.let(fromRoot.getCurrentVersion); 25 | } 26 | 27 | ngOnInit() { 28 | this.store.dispatch(new actionsInfo.SetLoadingAction()); 29 | this.store.dispatch(new actionsInfo.SetStepAction("ReadonlyEditor")); 30 | 31 | this.route["params"].subscribe(params => { 32 | this.store.dispatch(new actionsExecution.SetCurrentAction(+params["executionID"])); 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/app/reducers/executionList.reducer.ts: -------------------------------------------------------------------------------- 1 | import '@ngrx/core/add/operator/select'; 2 | import 'rxjs/add/operator/map'; 3 | import 'rxjs/add/operator/let'; 4 | import { Observable } from 'rxjs/Observable'; 5 | import { combineLatest } from 'rxjs/observable/combineLatest'; 6 | 7 | import * as execution from '../actions/execution'; 8 | import * as find from 'lodash/find'; 9 | 10 | 11 | export interface State { 12 | count: number, 13 | next: any, 14 | previous: any, 15 | entities: any[], 16 | currentID: number, 17 | details: any, 18 | logs: any 19 | }; 20 | 21 | const initialState: State = { 22 | count: 0, 23 | next: 0, 24 | previous: 0, 25 | entities: [], 26 | currentID: null, 27 | details: {}, 28 | logs: {} 29 | }; 30 | 31 | export function reducer(state = initialState, action: execution.Actions): State { 32 | switch (action.type) { 33 | 34 | case execution.ActionTypes.LOAD: { 35 | 36 | return Object.assign({}, state, { 37 | count: action.payload.count, 38 | entities: action.payload.results, 39 | currentID: state.currentID, 40 | logs: Object.assign({}, state.logs) 41 | }); 42 | } 43 | 44 | case execution.ActionTypes.SET_CURRENT_ID: { 45 | let new_state = Object.assign({}, state); 46 | new_state["currentID"] = action.payload; 47 | return new_state; 48 | } 49 | 50 | case execution.ActionTypes.LOAD_EXECUTION_DETAIL: { 51 | let new_state = Object.assign({}, state); 52 | new_state.details = Object.assign({}, state.details, { 53 | [ action.payload.id ]: action.payload 54 | }); 55 | return new_state; 56 | } 57 | 58 | case execution.ActionTypes.LOAD_EXECUTION_LOG: { 59 | let payload = action.payload; 60 | 61 | let new_state = Object.assign({}, state); 62 | new_state.logs = Object.assign({}, state.logs, { 63 | [ payload.id ]: payload.data 64 | }); 65 | 66 | return new_state; 67 | 68 | } 69 | 70 | default: { 71 | return state; 72 | } 73 | } 74 | } 75 | 76 | export function getList(state$: Observable) { 77 | return state$.select(state => state.entities); 78 | } 79 | 80 | export function getDetails(state$: Observable) { 81 | return state$.select(state => state.details); 82 | } 83 | 84 | export function getCurrentID(state$: Observable) { 85 | return state$.select(state => state.currentID); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /frontend/src/app/reducers/info.reducer.ts: -------------------------------------------------------------------------------- 1 | import '@ngrx/core/add/operator/select'; 2 | import 'rxjs/add/operator/map'; 3 | import 'rxjs/add/operator/let'; 4 | import { Observable } from 'rxjs/Observable'; 5 | import { combineLatest } from 'rxjs/observable/combineLatest'; 6 | 7 | import * as Info from '../actions/info'; 8 | import * as find from 'lodash/find'; 9 | 10 | export interface State { 11 | loaded: boolean, 12 | saving: boolean, 13 | step: string 14 | }; 15 | 16 | const initialState: State = { 17 | loaded: false, 18 | saving: false, 19 | step: "Home" 20 | }; 21 | 22 | export function reducer(state = initialState, action: Info.Actions): State { 23 | switch (action.type) { 24 | 25 | case Info.ActionTypes.SET_LOADED: { 26 | return Object.assign({}, state , { 27 | loaded: true 28 | }); 29 | } 30 | 31 | case Info.ActionTypes.SET_LOADING: { 32 | return Object.assign({}, state , { 33 | loaded: false 34 | }); 35 | } 36 | 37 | case Info.ActionTypes.SET_STEP: { 38 | return Object.assign({}, state , { 39 | loaded: false, 40 | step: action.payload 41 | }); 42 | } 43 | 44 | case Info.ActionTypes.SET_SAVING: { 45 | return Object.assign({}, state , { 46 | saving: true 47 | }); 48 | } 49 | 50 | case Info.ActionTypes.SET_SAVED: { 51 | return Object.assign({}, state , { 52 | saving: false 53 | }); 54 | } 55 | 56 | default: { 57 | return state; 58 | } 59 | } 60 | } 61 | 62 | export function getLoading(state$: Observable) { 63 | return state$.select(state => state.loaded); 64 | } 65 | 66 | export function getStep(state$: Observable) { 67 | return state$.select(state => state.step); 68 | } 69 | -------------------------------------------------------------------------------- /frontend/src/app/reducers/programList.reducer.ts: -------------------------------------------------------------------------------- 1 | import '@ngrx/core/add/operator/select'; 2 | import 'rxjs/add/operator/map'; 3 | import 'rxjs/add/operator/let'; 4 | import { Observable } from 'rxjs/Observable'; 5 | import { combineLatest } from 'rxjs/observable/combineLatest'; 6 | 7 | import * as programList from '../actions/programList'; 8 | 9 | import * as find from 'lodash/find'; 10 | 11 | 12 | export interface State { 13 | count: number, 14 | next: any, 15 | previous: any, 16 | entities: any[], 17 | currentID: number, 18 | details: any 19 | }; 20 | 21 | const initialState: State = { 22 | count: 0, 23 | next: 0, 24 | previous: 0, 25 | entities: [], 26 | currentID: null, 27 | details: {} 28 | }; 29 | 30 | export function reducer(state = initialState, action: programList.Actions): State { 31 | switch (action.type) { 32 | 33 | case programList.ActionTypes.LOAD: { 34 | let copy_payload = Object.assign({}, action.payload); 35 | 36 | return { 37 | count: action.payload.count, 38 | next: 0, 39 | previous: 0, 40 | entities: action.payload.results, 41 | currentID: null, 42 | details: Object.assign({}, state.details) 43 | }; 44 | } 45 | 46 | case programList.ActionTypes.SET_CURRENT: { 47 | let new_state = Object.assign({}, state); 48 | new_state["currentID"] = action.payload; 49 | return new_state; 50 | } 51 | 52 | case programList.ActionTypes.LOAD_DETAIL: { 53 | let payload = action.payload; 54 | 55 | let program = find(state.entities, (entity) => entity.title == payload.title); 56 | let id; 57 | if(program){ 58 | id = program["id"]; 59 | }else{ 60 | id = state.currentID; 61 | } 62 | 63 | let new_state = Object.assign({}, state); 64 | new_state.details = Object.assign({}, state.details, { 65 | [ id ]: payload 66 | }); 67 | 68 | return new_state; 69 | 70 | } 71 | 72 | default: { 73 | return state; 74 | } 75 | } 76 | } 77 | 78 | export function getList(state$: Observable) { 79 | return state$.select(state => state.entities); 80 | } 81 | 82 | export function getCurrentID(state$: Observable) { 83 | return state$.select(state => state.currentID); 84 | } 85 | 86 | export function getDetails(state$: Observable) { 87 | return state$.select(state => state.details); 88 | } 89 | 90 | export function getCurrentTitle(state$: Observable) { 91 | return combineLatest( 92 | state$.let(getDetails), 93 | state$.let(getCurrentID) 94 | ).map(([ details, currentID ]) => details[+currentID] && details[+currentID]["title"]); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /frontend/src/app/reducers/referenceList.reducer.ts: -------------------------------------------------------------------------------- 1 | import '@ngrx/core/add/operator/select'; 2 | import 'rxjs/add/operator/map'; 3 | import 'rxjs/add/operator/let'; 4 | import { Observable } from 'rxjs/Observable'; 5 | import { combineLatest } from 'rxjs/observable/combineLatest'; 6 | 7 | import * as referenceList from '../actions/referenceList'; 8 | 9 | import * as find from 'lodash/find'; 10 | 11 | 12 | export interface State { 13 | entities: any[], 14 | details: any 15 | }; 16 | 17 | const initialState: State = { 18 | entities: [], 19 | details: {} 20 | }; 21 | 22 | export function reducer(state = initialState, action: referenceList.Actions): State { 23 | switch (action.type) { 24 | 25 | case referenceList.ActionTypes.LOAD: { 26 | return { 27 | entities: action.payload, 28 | details: Object.assign({}, state.details) 29 | }; 30 | } 31 | 32 | case referenceList.ActionTypes.LOAD_DETAIL:{ 33 | let payload = action.payload; 34 | 35 | let new_state = Object.assign({}, state); 36 | new_state.details = Object.assign({}, state.details, { 37 | [ payload.name ]: payload.data 38 | }); 39 | 40 | return new_state; 41 | } 42 | 43 | default: { 44 | return state; 45 | } 46 | } 47 | } 48 | 49 | export function getList(state$: Observable) { 50 | return state$.select(state => state.entities); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /frontend/src/app/services/breadcrumb.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import {stateService} from "./state.service"; 3 | import {Observable} from "rxjs"; 4 | import {Store} from "@ngrx/store"; 5 | import * as fromRoot from '../reducers'; 6 | import {wrapIntoObservable} from "@angular/router/src/utils/collection"; 7 | 8 | @Injectable() 9 | export class BreadcrumbService { 10 | private breadcrumbs: string[]; 11 | private redirects: string[]; 12 | private regexp: RegExp; 13 | 14 | constructor(private state: stateService, private store: Store) { 15 | 16 | } 17 | 18 | update(routes:any, url: string): any { 19 | this.redirects = []; 20 | this.breadcrumbs = []; 21 | 22 | if(url){ 23 | this.findRedirects(routes); 24 | this.regexp = new RegExp("(" + this.redirects.join('|') + ")", 'i'); 25 | 26 | 27 | if(url == '/'){ 28 | this.breadcrumbs.push(url); 29 | }else this.generateBreadcrumbTrail(url); 30 | } 31 | 32 | return this.breadcrumbs; 33 | } 34 | 35 | findRedirects(routes: any){ 36 | for (let route in routes) { 37 | if (routes.hasOwnProperty(route)) { 38 | if(routes[route]['redirectTo']){ 39 | this.redirects.push(routes[route]['redirectTo']); 40 | } 41 | if(routes[route]['children']){ 42 | this.findRedirects(routes[route]['children']); 43 | } 44 | } 45 | 46 | } 47 | } 48 | 49 | generateBreadcrumbTrail(url: string){ 50 | 51 | if( ! this.regexp.test( url.substr( url.lastIndexOf('/')+1, url.length) ) ){ 52 | this.breadcrumbs.unshift(url); 53 | } 54 | 55 | if (url.lastIndexOf('/') > 0) { 56 | this.generateBreadcrumbTrail(url.substr(0, url.lastIndexOf('/'))); 57 | }else if(url.lastIndexOf('/') == 0) { this.breadcrumbs.unshift('/'); } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /frontend/src/app/services/post.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { RestService } from './rest.service'; 4 | import {Observable} from "rxjs"; 5 | 6 | import {Store} from "@ngrx/store"; 7 | import * as fromRoot from '../reducers'; 8 | 9 | import * as actionsInterfaceList from "../actions/prInterfaceList"; 10 | import * as actionsProgramList from "../actions/programList"; 11 | import * as actionsVersionList from "../actions/versionList"; 12 | import * as actionsReferenceList from "../actions/referenceList"; 13 | import * as actionsExecution from "../actions/execution"; 14 | import * as actionsInfo from "../actions/info"; 15 | import {ActivatedRoute} from "@angular/router"; 16 | import {stateService} from "./state.service"; 17 | 18 | @Injectable() 19 | export class PostService { 20 | 21 | constructor( 22 | private rest: RestService, 23 | private state: stateService 24 | ){ 25 | 26 | } 27 | 28 | saveVersion(version){ 29 | return this.rest.post('/business-logic/rest/program-version/new', version); 30 | } 31 | 32 | putVersion(version){ 33 | return this.rest.put(`/business-logic/rest/program-version/${version.id}`, version); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/app/services/rest.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Http, Response, Headers, RequestOptions, URLSearchParams} from '@angular/http'; 3 | import 'rxjs/Rx'; 4 | 5 | @Injectable() 6 | export class RestService{ 7 | 8 | constructor(private http: Http) { 9 | 10 | } 11 | 12 | get(url: string){ 13 | let headers = this.getHeaders(); 14 | let options = new RequestOptions({headers: headers}); 15 | 16 | return this.http.get(url, options) 17 | .map( ( response: Response ) => { return response.json() } ); 18 | } 19 | 20 | getWithSearchParams(url:string, params: any){ 21 | let headers = this.getHeaders(); 22 | let urlSearchParams = new URLSearchParams(); 23 | 24 | params.forEach( (param) => { 25 | urlSearchParams.append(param[0], param[1]); 26 | }); 27 | 28 | let options = new RequestOptions({headers: headers, search: urlSearchParams}); 29 | return this.http.get(url, options) 30 | .map( ( response: Response ) => { return response.json() } ); 31 | } 32 | 33 | post(url: string, obj: any){ 34 | let csrftoken = this.getCookie('csrftoken'); 35 | let headers = this.getHeaders(); 36 | if( csrftoken != undefined ) headers.append('X-CSRFToken', csrftoken); 37 | 38 | let options = new RequestOptions({headers: headers}); 39 | 40 | return this.http.post(url, JSON.stringify(obj), options) 41 | .map( ( response: Response ) => { return response.json() } ); 42 | } 43 | 44 | put(url: string, obj: any){ 45 | let csrftoken = this.getCookie('csrftoken'); 46 | let headers = this.getHeaders(); 47 | if( csrftoken != undefined ) headers.append('X-CSRFToken', csrftoken); 48 | 49 | return this.http 50 | .put(url, JSON.stringify(obj), {headers: headers}) 51 | .map( ( response: Response ) => { return response.json() } ); 52 | 53 | } 54 | 55 | getHeaders(): Headers{ 56 | return new Headers({'Content-Type': 'application/json'}); 57 | } 58 | 59 | getCookie(name) { 60 | let matches = document.cookie.match(new RegExp( 61 | "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" 62 | )); 63 | return matches ? decodeURIComponent(matches[1]) : undefined; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /frontend/src/app/services/state.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import {Store, State} from "@ngrx/store"; 4 | import * as fromRoot from '../reducers'; 5 | import find = require("lodash/find"); 6 | 7 | @Injectable() 8 | export class stateService{ 9 | 10 | constructor(private store: Store){ 11 | 12 | } 13 | 14 | getState(){ 15 | let state: fromRoot.State; 16 | 17 | this.store.take(1).subscribe(s => state = s); 18 | return state; 19 | } 20 | 21 | getArguments(){ 22 | let state = this.getState(); 23 | return state["prInterfaces"].details[state["prInterfaces"].currentID]["arguments"]; 24 | } 25 | 26 | getEnv(){ 27 | //TODO: return version_env or program_env or interface_env 28 | let state = this.getState(); 29 | let version_env = state["versions"].details[state["versions"].currentID]["environment"]; 30 | let program_env = state["programs"].details[state["programs"].currentID]["environment"]; 31 | let interface_env = state["prInterfaces"].details[state["prInterfaces"].currentID]["environment"]; 32 | 33 | return version_env || program_env || interface_env; 34 | } 35 | 36 | getCurrentVersion(){ 37 | return this.getState()["versions"]["details"][this.getState()["versions"]["currentID"]]; 38 | } 39 | 40 | getCurrentPrInterface(){ 41 | return this.getState()["prInterfaces"]["details"][this.getState()["prInterfaces"]["currentID"]]; 42 | } 43 | 44 | getCurrentProgram(){ 45 | return this.getState()["programs"]["details"][this.getState()["programs"]["currentID"]]; 46 | } 47 | 48 | getCurrentExecution(){ 49 | return this.getState()["executions"]["details"][this.getState()["executions"]["currentID"]]; 50 | } 51 | 52 | getFunction(func_name: string){ 53 | let func; 54 | 55 | let env = this.getEnv(); 56 | env['libraries'].forEach((lib) => { 57 | func = find(lib["functions"], (func) => { 58 | return func["title"] == func_name; 59 | }); 60 | }); 61 | 62 | return func; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /frontend/src/app/services/xmlGenerator.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from "@angular/core"; 2 | import {block_reference, block_function, block_field_get, block_field_set, block_date} from "../blocks/consts/blockTypes"; 3 | import {stateService} from "./state.service"; 4 | import * as isNil from "lodash/isNil"; 5 | 6 | @Injectable() 7 | export class xmlGenerator{ 8 | 9 | constructor(private _state: stateService){ 10 | 11 | } 12 | 13 | forReferences(references): string{ 14 | let xml = ``; 15 | 16 | references.entities.forEach((ref) => { 17 | xml += ` 18 | ${ref.name} 19 | -1 20 | `; 21 | }); 22 | 23 | xml += ``; 24 | 25 | return xml; 26 | } 27 | 28 | forArguments(args): string{ 29 | let xml = ``; 30 | 31 | args.forEach((arg) => { 32 | xml += ``; 33 | 34 | xml += ` 35 | ${arg.name}.${arg.fields[0].name} 36 | `; 37 | arg.fields.forEach((field) => { 38 | xml += ` 39 | ${arg.name}.${field.name} 40 | `; 41 | }); 42 | xml += ``; 43 | }); 44 | 45 | xml += ``; 46 | 47 | return xml; 48 | } 49 | 50 | forFunctions(): string { 51 | let environment = this._state.getEnv(); 52 | 53 | if(isNil(environment)) return ''; 54 | 55 | let xml = ``; 56 | 57 | environment['libraries'].forEach((lib) => { 58 | xml += ``; 59 | 60 | lib['functions'].forEach((func) => { 61 | xml += ` 62 | 63 | ${func.title} 64 | 65 | `; 66 | }); 67 | 68 | xml += ``; 69 | }); 70 | 71 | xml += ``; 72 | 73 | return xml; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /frontend/src/assets/css/.gitkeep: -------------------------------------------------------------------------------- 1 | @AngularClass 2 | -------------------------------------------------------------------------------- /frontend/src/assets/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "value": "AngularClass" 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/fonts/icons.eot -------------------------------------------------------------------------------- /frontend/src/assets/fonts/icons.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/fonts/icons.otf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/fonts/icons.woff -------------------------------------------------------------------------------- /frontend/src/assets/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/fonts/icons.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | PatrickJS -- @gdi2290 12 | AngularClass -- @AngularClass 13 | 14 | # TECHNOLOGY COLOPHON 15 | 16 | HTML5, CSS3 17 | Angular2, TypeScript, Webpack 18 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /frontend/src/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/icon/favicon.ico -------------------------------------------------------------------------------- /frontend/src/assets/images/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/images/flags.png -------------------------------------------------------------------------------- /frontend/src/assets/img/angular-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/img/angular-logo.png -------------------------------------------------------------------------------- /frontend/src/assets/img/angularclass-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/img/angularclass-avatar.png -------------------------------------------------------------------------------- /frontend/src/assets/img/angularclass-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/frontend/src/assets/img/angularclass-logo.png -------------------------------------------------------------------------------- /frontend/src/assets/mock-data/mock-data.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"res": "data"} 3 | ] 4 | -------------------------------------------------------------------------------- /frontend/src/assets/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /frontend/src/assets/service-worker.js: -------------------------------------------------------------------------------- 1 | // This file is intentionally without code. 2 | -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= webpackConfig.metadata.title %> 10 | 11 | 12 | 13 | <% if (webpackConfig.htmlElements.headTags) { %> 14 | 15 | <%= webpackConfig.htmlElements.headTags %> 16 | <% } %> 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Loading... 28 | 29 | 30 | <% if (webpackConfig.metadata.isDevServer && webpackConfig.metadata.HMR !== true) { %> 31 | 32 | 33 | <% } %> 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /frontend/src/main.browser.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Angular bootstraping 3 | */ 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | import { decorateModuleRef } from './app/environment'; 6 | import { bootloader } from '@angularclass/hmr'; 7 | /* 8 | * App Module 9 | * our top level module that holds all of our components 10 | */ 11 | import { AppModule } from './app'; 12 | 13 | /* 14 | * Bootstrap our Angular app with a top level NgModule 15 | */ 16 | export function main(): Promise { 17 | return platformBrowserDynamic() 18 | .bootstrapModule(AppModule) 19 | .then(decorateModuleRef) 20 | .catch(err => console.error(err)); 21 | } 22 | 23 | // needed for hmr 24 | // in prod this is replace for document ready 25 | bootloader(main); 26 | -------------------------------------------------------------------------------- /frontend/src/polyfills.browser.ts: -------------------------------------------------------------------------------- 1 | // TODO(gdi2290): switch to DLLs 2 | 3 | // Polyfills 4 | 5 | // import 'ie-shim'; // Internet Explorer 9 support 6 | 7 | 8 | // import 'core-js/es6'; 9 | // Added parts of es6 which are necessary for your project or your browser support requirements. 10 | import 'core-js/es6/symbol'; 11 | import 'core-js/es6/object'; 12 | import 'core-js/es6/function'; 13 | import 'core-js/es6/parse-int'; 14 | import 'core-js/es6/parse-float'; 15 | import 'core-js/es6/number'; 16 | import 'core-js/es6/math'; 17 | import 'core-js/es6/string'; 18 | import 'core-js/es6/date'; 19 | import 'core-js/es6/array'; 20 | import 'core-js/es6/regexp'; 21 | import 'core-js/es6/map'; 22 | import 'core-js/es6/set'; 23 | import 'core-js/es6/weak-map'; 24 | import 'core-js/es6/weak-set'; 25 | import 'core-js/es6/typed'; 26 | import 'core-js/es6/reflect'; 27 | // see issue https://github.com/AngularClass/angular2-webpack-starter/issues/709 28 | // import 'core-js/es6/promise'; 29 | 30 | import 'core-js/es7/reflect'; 31 | import 'zone.js/dist/zone'; 32 | 33 | // Typescript emit helpers polyfill 34 | import 'ts-helpers'; 35 | 36 | if ('production' === ENV) { 37 | // Production 38 | 39 | 40 | } else { 41 | // Development 42 | 43 | Error.stackTraceLimit = Infinity; 44 | 45 | require('zone.js/dist/long-stack-trace-zone'); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /frontend/src/tests/app.e2e.ts: -------------------------------------------------------------------------------- 1 | describe('App', () => { 2 | 3 | beforeEach(() => { 4 | browser.get('/'); 5 | }); 6 | 7 | 8 | it('should have a title', () => { 9 | let subject = browser.getTitle(); 10 | let result = 'Angular2 Webpack Starter by @gdi2290 from @AngularClass'; 11 | expect(subject).toEqual(result); 12 | }); 13 | 14 | it('should have header', () => { 15 | let subject = element(by.css('h1')).isPresent(); 16 | let result = true; 17 | expect(subject).toEqual(result); 18 | }); 19 | 20 | it('should have ', () => { 21 | let subject = element(by.css('app home')).isPresent(); 22 | let result = true; 23 | expect(subject).toEqual(result); 24 | }); 25 | 26 | it('should have buttons', () => { 27 | let subject = element(by.css('button')).getText(); 28 | let result = 'Submit Value'; 29 | expect(subject).toEqual(result); 30 | }); 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /frontend/src/tests/app.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | inject, 3 | TestBed 4 | } from '@angular/core/testing'; 5 | 6 | // Load the implementations that should be tested 7 | import { App } from '../app/app.component'; 8 | import { AppState } from '../app/app.service'; 9 | 10 | describe('App', () => { 11 | // provide our implementations or mocks to the dependency injector 12 | beforeEach(() => TestBed.configureTestingModule({ 13 | providers: [ 14 | AppState, 15 | App 16 | ]})); 17 | 18 | it('should have a url', inject([ App ], (app: App) => { 19 | expect(app.url).toEqual('https://twitter.com/AngularClass'); 20 | })); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /frontend/src/tests/mock.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable } from 'rxjs/Observable'; 3 | import {Environment} from '../app/models/environment'; 4 | 5 | @Injectable() 6 | export class MockService { 7 | 8 | private arguments: Observable; 9 | public environment = new Environment({ 10 | description: "description", 11 | libraries: [ 12 | { 13 | title: "BookLibrary", 14 | functions: [ 15 | { 16 | title: "Get Book from the shelf", 17 | description: "This is function!", 18 | is_returns_value: false, 19 | arguments: [ 20 | { name: "shelf", verbose_name: "shelff", data_type: "number" }, 21 | { name: "count", verbose_name: "countt", data_type: "number" } 22 | ] 23 | } 24 | ] 25 | } 26 | ] 27 | }); 28 | 29 | constructor(){ 30 | this.arguments = new Observable(observer => { 31 | observer.next([ 32 | { 33 | "fields": [ 34 | { 35 | "verbose_name": "Book title", 36 | "content_type": null, 37 | "name": "title", 38 | "data_type": "string" 39 | } 40 | ], 41 | "verbose_name": "Book", 42 | "content_type": { 43 | "name": "books.Book", 44 | "verbose_name": "Book", 45 | "id": 37 46 | }, 47 | "name": "book" 48 | } 49 | ]); 50 | }); 51 | 52 | 53 | } 54 | 55 | test(){ 56 | return "This is MockService!"; 57 | } 58 | 59 | getFieldList(){ 60 | return [['Book title', 'book.title']]; 61 | } 62 | 63 | getVerboseNameForField(value: string){ 64 | if(value == 'book.title'){ 65 | return 'Book title'; 66 | }else return "I dont know"; 67 | } 68 | 69 | getEnvironment(){ 70 | return this.environment; 71 | } 72 | 73 | getFunction(func_name: string){ 74 | 75 | return this.environment.libraries[0].functions[0]; 76 | } 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /frontend/src/vendor.browser.ts: -------------------------------------------------------------------------------- 1 | // For vendors for example jQuery, Lodash, angular2-jwt just import them here unless you plan on 2 | // chunking vendors files for async loading. You would need to import the async loaded vendors 3 | // at the entry point of the async loaded file. Also see custom-typings.d.ts as you also need to 4 | // run `typings install x` where `x` is your module 5 | 6 | // TODO(gdi2290): switch to DLLs 7 | 8 | // Angular 2 9 | import '@angular/platform-browser'; 10 | import '@angular/platform-browser-dynamic'; 11 | import '@angular/core'; 12 | import '@angular/common'; 13 | import '@angular/forms'; 14 | import '@angular/http'; 15 | import '@angular/router'; 16 | 17 | // AngularClass 18 | import '@angularclass/hmr'; 19 | 20 | 21 | // RxJS 22 | import 'rxjs/add/operator/map'; 23 | import 'rxjs/add/operator/mergeMap'; 24 | 25 | require('script!imports?module=>undefined!../blockly/blockly_compressed.js'); 26 | require('script!imports?module=>undefined!blockly/blocks_compressed.js'); 27 | require('script!imports?module=>undefined!blockly/msg/js/en.js'); 28 | 29 | require('script!jquery/dist/jquery.js'); 30 | require('script!semantic-ui/dist/semantic.js'); 31 | 32 | if ('production' === ENV) { 33 | // Production 34 | 35 | 36 | } else { 37 | // Development 38 | 39 | } 40 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "sourceMap": true, 10 | "noEmitHelpers": true, 11 | "strictNullChecks": false, 12 | "baseUrl": "./src", 13 | "paths": { 14 | }, 15 | "lib": [ 16 | "dom", 17 | "es6" 18 | ], 19 | "types": [ 20 | "hammerjs", 21 | "jasmine", 22 | "node", 23 | "protractor", 24 | "selenium-webdriver", 25 | "source-map", 26 | "uglify-js", 27 | "webpack", 28 | "lodash" 29 | ] 30 | }, 31 | "exclude": [ 32 | "node_modules", 33 | "dist" 34 | ], 35 | "awesomeTypescriptLoaderOptions": { 36 | "forkChecker": true, 37 | "useWebpackText": true 38 | }, 39 | "compileOnSave": false, 40 | "buildOnSave": false, 41 | "atom": { "rewriteTsconfig": false } 42 | } 43 | -------------------------------------------------------------------------------- /frontend/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "modules", 3 | "out": "doc", 4 | "theme": "default", 5 | "ignoreCompilerErrors": "true", 6 | "experimentalDecorators": "true", 7 | "emitDecoratorMetadata": "true", 8 | "target": "ES5", 9 | "moduleResolution": "node", 10 | "preserveConstEnums": "true", 11 | "stripInternal": "true", 12 | "suppressExcessPropertyErrors": "true", 13 | "suppressImplicitAnyIndexErrors": "true", 14 | "module": "commonjs" 15 | } 16 | -------------------------------------------------------------------------------- /frontend/webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | // Look in ./config folder for webpack.dev.js 6 | switch (process.env.NODE_ENV) { 7 | case 'prod': 8 | case 'production': 9 | module.exports = require('./config/webpack.prod')({env: 'production'}); 10 | break; 11 | case 'test': 12 | case 'testing': 13 | module.exports = require('./config/webpack.test')({env: 'test'}); 14 | break; 15 | case 'dev': 16 | case 'development': 17 | default: 18 | module.exports = require('./config/webpack.dev')({env: 'development'}); 19 | } 20 | -------------------------------------------------------------------------------- /makemigration.sh: -------------------------------------------------------------------------------- 1 | rm sites/dev/db.sqlite3 2 | rm business_logic/migrations/0* 3 | python manage.py makemigrations business_logic 4 | python manage.py migrate 5 | python manage.py loaddata sites/dev/fixtures/data.json 6 | 7 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | 6 | if __name__ == "__main__": 7 | os.environ.setdefault( 8 | "DJANGO_SETTINGS_MODULE", "sites.dev.heroku.settings" 9 | if 'DYNO' in os.environ 10 | else "sites.dev.settings" 11 | ) 12 | 13 | from django.core.management import execute_from_command_line 14 | 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /noxfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import nox 3 | import pathlib 4 | 5 | PYTHON_VERSIONS = [ 6 | '3.8', 7 | '3.9', 8 | '3.10', 9 | '3.11', 10 | ] 11 | 12 | DEFAULT_PYTHON_VERSION = '3.11' 13 | 14 | DJANGO_VERSIONS = [ 15 | '3.2', 16 | '4.0', 17 | '4.1', 18 | '4.2', 19 | ] 20 | 21 | 22 | @nox.session 23 | def dev(session: nox.Session) -> None: 24 | venv_dir = pathlib.Path('./.venv').resolve() 25 | 26 | session.install('virtualenv') 27 | session.run('virtualenv', '-p', DEFAULT_PYTHON_VERSION, os.fsdecode(venv_dir), silent=True) 28 | 29 | python = os.fsdecode(venv_dir.joinpath('bin/python')) 30 | session.run(python, '-m', 'pip', 'install', '-r', 'requirements.dev.txt', external=True) 31 | 32 | 33 | @nox.session(python=PYTHON_VERSIONS, tags=['unittests']) 34 | @nox.parametrize('django', DJANGO_VERSIONS) 35 | def unittest(session, django): 36 | major, minor = map(lambda x: int(x), django.split('.')) 37 | session.install(f'Django >= {django}, < {major}.{minor + 1}') 38 | 39 | if django == '3.2': 40 | session.install('django-admin-sortable2==1.0.4') 41 | 42 | session.install('-r', 'requirements.test.txt') 43 | 44 | session.env['DJANGO_SETTINGS_MODULE'] = 'sites.test.settings' 45 | session.run('py.test', 'tests') 46 | 47 | 48 | @nox.session(reuse_venv=True, tags=['formatting']) 49 | def flake8(session): 50 | session.install('flake8') 51 | session.run('flake8', 'business_logic', 'tests', 'sites', '--exclude=*/migrations/*') 52 | -------------------------------------------------------------------------------- /requirements.dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements.test.txt 2 | -r docs/requirements.txt 3 | Django>=3,<5 4 | django-bootstrap3==23.4 5 | flake8 6 | pluggy>=0.12.0 7 | nox 8 | twine 9 | wheel 10 | yapf 11 | -------------------------------------------------------------------------------- /requirements.heroku.txt: -------------------------------------------------------------------------------- 1 | dj-database-url 2 | gunicorn 3 | psycopg2 4 | whitenoise 5 | -------------------------------------------------------------------------------- /requirements.test.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | coverage==7.3.0 3 | pip>=21.0 4 | pytest-cov==4.1.0 5 | pytest-django==4.5.2 6 | pytest==7.4.0 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django-ace-overlay==0.8.0 2 | #django-admin-sortable2==2.1.9 3 | django-admin-sortable2>=1.0.4 4 | django-filter==23.2 5 | django-nested-admin==4.0.2 6 | django-polymorphic==3.1.0 7 | django-treebeard==4.7 8 | djangorestframework>=3.14.0,<3.15.0 9 | lxml<5.0 10 | -------------------------------------------------------------------------------- /resetdb.sh: -------------------------------------------------------------------------------- 1 | rm sites/dev/db.sqlite3 2 | python manage.py migrate 3 | python manage.py loaddata sites/dev/fixtures/data.json 4 | 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | [bdist_wheel] 4 | universal = 1 5 | [pycodestyle] 6 | max-line-length = 120 7 | [pep8] 8 | max-line-length = 120 9 | [flake8] 10 | exclude = business_logic/migrations,frontend,.tox,virtualenv,venv,build,dist 11 | max-line-length = 120 12 | # max_complexity = 10 13 | # some issues will be fixed later 14 | ignore = 15 | # module imported but unused 16 | F401, 17 | # ‘from module import *’ used; unable to detect undefined names 18 | F403, 19 | # name may be undefined, or defined from star imports: module 20 | F405, 21 | # local variable name is assigned to but never used 22 | F841, 23 | -------------------------------------------------------------------------------- /sites/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /sites/dev/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/sites/dev/__init__.py -------------------------------------------------------------------------------- /sites/dev/books/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /sites/dev/books/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.contrib import admin 4 | 5 | from .models import * 6 | 7 | admin.site.register(Author) 8 | admin.site.register(Book) 9 | admin.site.register(Publisher) 10 | -------------------------------------------------------------------------------- /sites/dev/books/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BooksConfig(AppConfig): 5 | name = 'sites.dev.books' 6 | default_auto_field = 'django.db.models.AutoField' 7 | -------------------------------------------------------------------------------- /sites/dev/books/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.8 on 2018-01-01 23:45 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Author', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('first_name', models.CharField(max_length=30, verbose_name=b'First name')), 22 | ('last_name', models.CharField(max_length=40, verbose_name=b'Last name')), 23 | ('rank', models.IntegerField(default=0, verbose_name=b'Rank')), 24 | ], 25 | options={ 26 | 'verbose_name': 'Author', 27 | 'verbose_name_plural': 'Authors', 28 | }, 29 | ), 30 | migrations.CreateModel( 31 | name='Book', 32 | fields=[ 33 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 34 | ('title', models.CharField(max_length=100, verbose_name=b'Title')), 35 | ('publication_date', models.DateField(verbose_name=b'Publication date')), 36 | ('price', models.FloatField(verbose_name=b'Price')), 37 | ('authors', models.ManyToManyField(to='books.Author', verbose_name=b'Authors')), 38 | ], 39 | options={ 40 | 'verbose_name': 'Book', 41 | 'verbose_name_plural': 'Books', 42 | }, 43 | ), 44 | migrations.CreateModel( 45 | name='Publisher', 46 | fields=[ 47 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 48 | ('name', models.CharField(max_length=30, verbose_name=b'Publisher name')), 49 | ('rank', models.IntegerField(default=0, verbose_name=b'Publisher rank')), 50 | ], 51 | options={ 52 | 'verbose_name': 'Publisher', 53 | 'verbose_name_plural': 'Publishers', 54 | }, 55 | ), 56 | migrations.AddField( 57 | model_name='book', 58 | name='publisher', 59 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='books', to='books.Publisher', verbose_name=b'Publisher'), 60 | ), 61 | ] 62 | -------------------------------------------------------------------------------- /sites/dev/books/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/sites/dev/books/migrations/__init__.py -------------------------------------------------------------------------------- /sites/dev/books/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.urls import reverse 3 | from django.db import models 4 | 5 | 6 | class Publisher(models.Model): 7 | name = models.CharField(max_length=30, verbose_name='Publisher name') 8 | rank = models.IntegerField(default=0, verbose_name='Publisher rank') 9 | 10 | class Meta: 11 | verbose_name = 'Publisher' 12 | verbose_name_plural = 'Publishers' 13 | ordering = ('id',) 14 | 15 | def __str__(self): 16 | return self.name 17 | 18 | def get_absolute_url(self): 19 | return reverse('publisher-detail', kwargs={'pk': self.pk}) 20 | 21 | 22 | class Author(models.Model): 23 | first_name = models.CharField(max_length=30, verbose_name='First name') 24 | last_name = models.CharField(max_length=40, verbose_name='Last name') 25 | rank = models.IntegerField(default=0, verbose_name='Rank') 26 | 27 | class Meta: 28 | verbose_name = 'Author' 29 | verbose_name_plural = 'Authors' 30 | ordering = ('id',) 31 | 32 | def __str__(self): 33 | return u'%s %s' % (self.first_name, self.last_name) 34 | 35 | def get_absolute_url(self): 36 | return reverse('author-detail', kwargs={'pk': self.pk}) 37 | 38 | 39 | class Book(models.Model): 40 | title = models.CharField(max_length=100, verbose_name='Title') 41 | authors = models.ManyToManyField(Author, verbose_name='Authors') 42 | publisher = models.ForeignKey(Publisher, related_name='books', verbose_name='Publisher', on_delete=models.CASCADE) 43 | publication_date = models.DateField(verbose_name='Publication date') 44 | price = models.FloatField(verbose_name='Price') 45 | 46 | class Meta: 47 | verbose_name = 'Book' 48 | verbose_name_plural = 'Books' 49 | ordering = ('id',) 50 | 51 | def __str__(self): 52 | return self.title 53 | 54 | def get_absolute_url(self): 55 | return reverse('book-detail', kwargs={'pk': self.pk}) 56 | -------------------------------------------------------------------------------- /sites/dev/books/static/books/favicon-book.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/sites/dev/books/static/books/favicon-book.ico -------------------------------------------------------------------------------- /sites/dev/books/static/books/theme.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | padding-bottom: 30px; 4 | } 5 | 6 | .theme-dropdown .dropdown-menu { 7 | position: static; 8 | display: block; 9 | margin-bottom: 20px; 10 | } 11 | 12 | .theme-showcase > p > .btn { 13 | margin: 5px 0; 14 | } 15 | 16 | .theme-showcase .navbar .container { 17 | width: auto; 18 | } 19 | -------------------------------------------------------------------------------- /sites/dev/books/templates/books/author_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'books/base.html' %} 2 | {% block content %} 3 |

Authors

4 |
    5 | {% for object in object_list %} 6 |
  • {{ object.first_name }} {{ object.last_name }} {{ object.rank }}
  • 7 | {% endfor %} 8 |
9 | {% endblock %} -------------------------------------------------------------------------------- /sites/dev/books/templates/books/book_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'books/base.html' %} 2 | {% block content %} 3 |

{{ object }}

4 | 5 |
6 |
7 |

Publisher

8 |
9 | 12 |
13 | 14 | 15 | {% endblock %} -------------------------------------------------------------------------------- /sites/dev/books/templates/books/book_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'books/base.html' %} 2 | {% block content %} 3 |

Books

4 | 9 | {% endblock %} -------------------------------------------------------------------------------- /sites/dev/books/templates/books/form.html: -------------------------------------------------------------------------------- 1 | {% extends 'books/base.html' %} 2 | {% load bootstrap3 %} 3 | 4 | {% block content %} 5 |
6 | {% csrf_token %} 7 | {% bootstrap_form form %} 8 | {% buttons %} 9 | 12 | {% endbuttons %} 13 |
14 | {% endblock %} 15 | 16 | -------------------------------------------------------------------------------- /sites/dev/books/templates/books/publisher_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'books/base.html' %} 2 | {% block content %} 3 |

{{ object }}

4 | 5 |
6 |
7 |

Rank

8 |
9 |
10 | {{ object.rank }} 11 |
12 |
13 |
14 |
15 |

Books

16 |
17 |
18 |
    19 | {% for book in object.books.all %} 20 |
  • {{ book }}
  • 21 | {% endfor %} 22 |
23 |
24 |
25 | 26 | {% endblock %} -------------------------------------------------------------------------------- /sites/dev/books/templates/books/publisher_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'books/base.html' %} 2 | {% block content %} 3 |

Publishers

4 | 9 | {% endblock %} -------------------------------------------------------------------------------- /sites/dev/books/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.urls import re_path 3 | 4 | from .views import * 5 | urlpatterns = [ 6 | re_path(r'author$', AuthorList.as_view()), 7 | re_path(r'author/add$', AuthorCreate.as_view()), 8 | # 9 | re_path(r'publisher$', PublisherList.as_view()), 10 | re_path(r'publisher/(?P\d+)$', PublisherDetail.as_view(), name='publisher-detail'), 11 | re_path(r'publisher/add$', PublisherCreate.as_view()), 12 | # 13 | re_path(r'book$', BookList.as_view()), 14 | re_path(r'book/(?P\d+)$', BookDetail.as_view(), name='book-detail'), 15 | re_path(r'book/add$', BookCreate.as_view()), 16 | ] 17 | -------------------------------------------------------------------------------- /sites/dev/books/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.views import generic 3 | 4 | from business_logic.models import Program, Context 5 | 6 | from .models import * 7 | 8 | 9 | class AuthorCreate(generic.CreateView): 10 | model = Author 11 | template_name = 'books/form.html' 12 | success_url = '/books/author' 13 | fields = ( 14 | 'first_name', 15 | 'last_name', 16 | 'email', 17 | ) 18 | 19 | 20 | class PublisherCreate(generic.CreateView): 21 | model = Publisher 22 | template_name = 'books/form.html' 23 | success_url = '/books/publisher' 24 | fields = ('name',) 25 | 26 | 27 | class BookCreate(generic.CreateView): 28 | model = Book 29 | template_name = 'books/form.html' 30 | success_url = '/books/book' 31 | fields = ( 32 | 'title', 33 | 'authors', 34 | 'publisher', 35 | 'publication_date', 36 | 'price', 37 | ) 38 | 39 | 40 | class AuthorList(generic.ListView): 41 | model = Author 42 | 43 | 44 | class BookList(generic.ListView): 45 | model = Book 46 | 47 | 48 | class PublisherList(generic.ListView): 49 | model = Publisher 50 | 51 | 52 | class PublisherDetail(generic.DetailView): 53 | model = Publisher 54 | 55 | 56 | class BookDetail(generic.DetailView): 57 | model = Book 58 | 59 | def get_object(self, queryset=None): 60 | book = super(BookDetail, self).get_object(queryset) 61 | program = Program.objects.get(code='on_book_view') 62 | version = program.versions.order_by('id').last() 63 | version.execute(context=Context(debug=True, log=True), book=book) 64 | book.publisher.save() 65 | 66 | return book 67 | -------------------------------------------------------------------------------- /sites/dev/fixtures/dumpdata.sh: -------------------------------------------------------------------------------- 1 | python manage.py dumpdata --natural-foreign --indent 4 \ 2 | -e sessions -e contenttypes -e admin.logentry -e auth.permission \ 3 | -e business_logic.logentry -e business_logic.execution -e business_logic.executionargument \ 4 | > sites/dev/fixtures/data.json 5 | 6 | -------------------------------------------------------------------------------- /sites/dev/heroku/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/sites/dev/heroku/__init__.py -------------------------------------------------------------------------------- /sites/dev/heroku/settings.py: -------------------------------------------------------------------------------- 1 | import dj_database_url 2 | from ..settings import * 3 | 4 | DATABASES = {'default': dj_database_url.config()} 5 | ALLOWED_HOSTS = ['*'] 6 | PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) 7 | 8 | STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles') 9 | STATIC_URL = '/static/' 10 | 11 | MIDDLEWARE += ( 12 | 'whitenoise.middleware.WhiteNoiseMiddleware', 13 | ) 14 | -------------------------------------------------------------------------------- /sites/dev/heroku/wsgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | from django.core.wsgi import get_wsgi_application 3 | 4 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sites.dev.heroku.settings") 5 | 6 | application = get_wsgi_application() 7 | -------------------------------------------------------------------------------- /sites/dev/settings.py: -------------------------------------------------------------------------------- 1 | from ..settings import * 2 | 3 | SITE_DIR = os.path.dirname(os.path.abspath(__file__)) 4 | 5 | DEBUG = True 6 | ALLOWED_HOSTS = ['*'] 7 | 8 | INSTALLED_APPS += [ 9 | # 'django_extensions', 10 | 11 | 'bootstrap3', 12 | 'sites.dev.books', 13 | ] 14 | 15 | ROOT_URLCONF = 'sites.dev.urls' 16 | WSGI_APPLICATION = 'sites.dev.wsgi.application' 17 | 18 | DATABASES = { 19 | 'default': { 20 | 'ENGINE': 'django.db.backends.sqlite3', 21 | 'NAME': os.environ.get('SQLITE_DB_PATH', os.path.join(os.path.dirname(__file__), 'db.sqlite3')), 22 | } 23 | } 24 | 25 | STATIC_URL = '/static/' 26 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') 27 | STATICFILES_FINDERS = [ 28 | 'sites.dev.utils.staticfiles.finders.AppDirectoriesIndexFinder', 29 | 'django.contrib.staticfiles.finders.FileSystemFinder', 30 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 31 | ] 32 | 33 | MEDIA_URL = '/media/' 34 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 35 | 36 | TEMPLATES = [ 37 | { 38 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 39 | 'DIRS': [ 40 | os.path.join(SITE_DIR, 'templates'), 41 | ], 42 | 'APP_DIRS': True, 43 | 'OPTIONS': { 44 | 'context_processors': [ 45 | 'django.template.context_processors.debug', 46 | 'django.template.context_processors.request', 47 | 'django.contrib.auth.context_processors.auth', 48 | 'django.contrib.messages.context_processors.messages', 49 | ], 50 | }, 51 | }, 52 | ] 53 | 54 | if 'test' in sys.argv[1:] or 'jenkins' in sys.argv[1:]: 55 | from ..test.settings import * 56 | -------------------------------------------------------------------------------- /sites/dev/templates/admin/base.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/base.html' %} 2 | {% load static %} 3 | {% block extrahead %} 4 | {{ block.super }} 5 | {% include "ga.html" %} 6 | {% endblock %} -------------------------------------------------------------------------------- /sites/dev/templates/admin/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/login.html' %} 2 | {% block branding %} 3 | Django administration for django-business-logic demo 4 | {% endblock %} 5 | 6 | {% block usertools %} 7 |
8 | use test:test to login 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /sites/dev/templates/ga.html: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /sites/dev/templates/rest_framework/api.html: -------------------------------------------------------------------------------- 1 | {% extends "rest_framework/base.html" %} 2 | {% block script %} 3 | {{ block.super }} 4 | {% include "ga.html" %} 5 | {% endblock %} -------------------------------------------------------------------------------- /sites/dev/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include 2 | from django.urls import re_path 3 | from django.contrib import admin 4 | from django.contrib.staticfiles.views import serve as staticfiles_serve 5 | from django.http.response import Http404 6 | from django.shortcuts import redirect 7 | from django.views.defaults import page_not_found 8 | 9 | urlpatterns = [ 10 | re_path(r'^admin/', admin.site.urls), 11 | re_path(r'^nested_admin/', include('nested_admin.urls')), 12 | 13 | re_path(r'^business-logic/', include('business_logic.urls')), 14 | re_path(r'^business-logic/', lambda request: page_not_found(request, Http404())), 15 | re_path(r'^books/', include('sites.dev.books.urls')), 16 | # url('^(?Pstatic/.*)$', staticfiles_serve), 17 | re_path(r'', lambda x: redirect('/books/book')), 18 | ] 19 | -------------------------------------------------------------------------------- /sites/dev/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/sites/dev/utils/__init__.py -------------------------------------------------------------------------------- /sites/dev/utils/staticfiles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/sites/dev/utils/staticfiles/__init__.py -------------------------------------------------------------------------------- /sites/dev/utils/staticfiles/finders.py: -------------------------------------------------------------------------------- 1 | from django.contrib.staticfiles.finders import AppDirectoriesFinder 2 | 3 | 4 | class AppDirectoriesIndexFinder(AppDirectoriesFinder): 5 | 6 | def find_in_app(self, app, path): 7 | path += '/index.html' 8 | return super(AppDirectoriesIndexFinder, self).find_in_app(app, path) 9 | -------------------------------------------------------------------------------- /sites/dev/wsgi.py: -------------------------------------------------------------------------------- 1 | from django.core.wsgi import get_wsgi_application 2 | 3 | application = get_wsgi_application() 4 | -------------------------------------------------------------------------------- /sites/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import django 5 | 6 | SITE_DIR = os.path.dirname(os.path.abspath(__file__)) 7 | BASE_DIR = os.path.join(SITE_DIR, os.pardir) 8 | 9 | SECRET_KEY = 'ygw^xlknx$7$2-jbpzmaw!d%qe0uw+ozm&q@)7t82m0v(q(&)(' 10 | 11 | DEBUG = True 12 | 13 | ALLOWED_HOSTS = ['*'] 14 | 15 | INSTALLED_APPS = [ 16 | 'django.contrib.admin', 17 | 'django.contrib.auth', 18 | 'django.contrib.contenttypes', 19 | 'django.contrib.sessions', 20 | 'django.contrib.messages', 21 | 'django.contrib.staticfiles', 22 | # 23 | 'rest_framework', 24 | 'django_filters', 25 | 'nested_admin', 26 | 'polymorphic', 27 | 'ace_overlay', 28 | 'adminsortable2', 29 | # 30 | 'business_logic', 31 | ] 32 | 33 | MIDDLEWARE = ( 34 | 'django.contrib.sessions.middleware.SessionMiddleware', 35 | 'django.middleware.common.CommonMiddleware', 36 | 'django.middleware.csrf.CsrfViewMiddleware', 37 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 38 | 'django.contrib.messages.middleware.MessageMiddleware', 39 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 40 | 'django.middleware.security.SecurityMiddleware', 41 | ) 42 | 43 | TEMPLATES = [ 44 | { 45 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 46 | 'DIRS': [ 47 | os.path.join(SITE_DIR, 'templates'), 48 | ], 49 | 'APP_DIRS': True, 50 | 'OPTIONS': { 51 | 'context_processors': [ 52 | 'django.template.context_processors.debug', 53 | 'django.template.context_processors.request', 54 | 'django.contrib.auth.context_processors.auth', 55 | 'django.contrib.messages.context_processors.messages', 56 | ], 57 | }, 58 | }, 59 | ] 60 | 61 | # Internationalization 62 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 63 | 64 | LANGUAGE_CODE = 'en-us' 65 | 66 | locale_dir = os.path.join(BASE_DIR, 'business_logic/locale') 67 | LANGUAGES = [(x, x) for x in os.listdir(locale_dir) if os.path.isdir(os.path.join(locale_dir, x))] 68 | LANGUAGES += [('en', 'en')] 69 | 70 | TIME_ZONE = 'UTC' 71 | 72 | # USE_I18N = True 73 | 74 | # USE_L10N = True 75 | 76 | USE_TZ = True 77 | 78 | 79 | # http://stackoverflow.com/a/28560805/138063 80 | class DisableMigrations(object): 81 | 82 | def __contains__(self, item): 83 | return True 84 | 85 | def __getitem__(self, item): 86 | if django.VERSION[1] < 9: 87 | # django < 1.9 88 | return "notmigrations" 89 | return None 90 | -------------------------------------------------------------------------------- /sites/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgk/django-business-logic/54c2e0fb20539cbe265f674737c006d3ace212a7/sites/test/__init__.py -------------------------------------------------------------------------------- /sites/test/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from ..settings import * 4 | 5 | ROOT_URLCONF = 'sites.test.urls' 6 | 7 | INSTALLED_APPS += ['tests.test_app'] 8 | 9 | DATABASES = { 10 | 'default': { 11 | 'ENGINE': 'django.db.backends.sqlite3', 12 | 'NAME': ':memory:', 13 | }, 14 | } 15 | 16 | STATIC_URL = '/admin-static/' 17 | -------------------------------------------------------------------------------- /sites/test/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import include, re_path 2 | from django.contrib import admin 3 | 4 | urlpatterns = [ 5 | re_path('^admin/', admin.site.urls), 6 | re_path('^nested_admin/', include('nested_admin.urls')), 7 | re_path('^business-logic/', include('business_logic.urls')), 8 | ] 9 | -------------------------------------------------------------------------------- /sites/test/wsgi.py: -------------------------------------------------------------------------------- 1 | from django.core.wsgi import get_wsgi_application 2 | 3 | application = get_wsgi_application() 4 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/blockly/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/blockly/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from io import StringIO 4 | 5 | from lxml import etree 6 | 7 | from ..common import * 8 | -------------------------------------------------------------------------------- /tests/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import unittest 4 | import datetime 5 | 6 | from decimal import Decimal 7 | from pprint import pprint 8 | 9 | from django.contrib.auth.models import User 10 | from django.urls import reverse 11 | from django.test import TestCase, Client 12 | from django.utils import timezone 13 | 14 | from business_logic.blockly.build import * 15 | from business_logic.blockly.create import * 16 | from business_logic.blockly.exceptions import * 17 | from business_logic.blockly.parse import * 18 | 19 | from business_logic.models import * 20 | from business_logic.utils import * 21 | from business_logic.config import * 22 | 23 | from .test_app.models import * 24 | from .utils import * 25 | 26 | 27 | class ProgramTestBase(TestCase): 28 | field_list = ( 29 | 'int_value', 30 | 'string_value', 31 | 'decimal_value', 32 | 'date_value', 33 | 'datetime_value', 34 | 'foreign_value', 35 | 'foreign_value.int_value', 36 | 'foreign_value.string_value', 37 | ) 38 | 39 | def setUp(self): 40 | self.program_interface = program_interface = ProgramInterface.objects.create(code='test') 41 | 42 | self.argument = ProgramArgument.objects.create( 43 | program_interface=self.program_interface, 44 | content_type=ContentType.objects.get_for_model(Model), 45 | name='test_model') 46 | 47 | self.fields = self.create_argument_fields(self.argument) 48 | 49 | self.program = program = Program.objects.create(program_interface=program_interface, title='test', code='test') 50 | self.program_version = ProgramVersion.objects.create(program=program, entry_point=self.create_entry_point()) 51 | 52 | self.test_model = Model.objects.create() 53 | 54 | def create_argument_fields(self, argument): 55 | fields = {} 56 | for field in self.field_list: 57 | fields[field] = ProgramArgumentField.objects.create( 58 | name=field, 59 | program_argument=argument, 60 | ) 61 | 62 | return fields 63 | 64 | def create_entry_point(self): 65 | return get_test_tree() 66 | -------------------------------------------------------------------------------- /tests/rest/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/rest/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import json 4 | 5 | 6 | from ..common import * 7 | 8 | 9 | class JSONClient(Client): 10 | 11 | def post(self, path, data={}, content_type='application/json', follow=False, **extra): 12 | ret = super(Client, self).post( 13 | path, data=data, content_type=content_type, follow=follow, HTTP_X_REQUESTED_WITH='XMLHttpRequest', **extra) 14 | return ret 15 | 16 | def put(self, path, data={}, content_type='application/json', follow=False, **extra): 17 | ret = super(Client, self).put( 18 | path, data=data, content_type=content_type, follow=follow, HTTP_X_REQUESTED_WITH='XMLHttpRequest', **extra) 19 | return ret 20 | 21 | def delete(self, path, data={}, content_type='application/json', follow=False, **extra): 22 | ret = super(Client, self).delete( 23 | path, data=data, content_type=content_type, follow=follow, HTTP_X_REQUESTED_WITH='XMLHttpRequest', **extra) 24 | return ret 25 | 26 | 27 | def response_json(response): 28 | return json.loads(response.content.decode('utf-8')) 29 | 30 | 31 | class ProgramRestTestBase(ProgramTestBase): 32 | 33 | def setUp(self): 34 | super(ProgramRestTestBase, self).setUp() 35 | self.client = JSONClient() 36 | -------------------------------------------------------------------------------- /tests/rest/test_log.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .common import * 3 | 4 | 5 | class ExecutionRestTest(ProgramRestTestBase): 6 | 7 | def setUp(self): 8 | super(ExecutionRestTest, self).setUp() 9 | context = Context(log=True, debug=True) 10 | self.program_version.execute(test_model=self.test_model, context=context) 11 | 12 | def test_execution_list_empty(self): 13 | Execution.objects.all().delete() 14 | url = reverse('business-logic:rest:execution-list') 15 | response = self.client.get(url) 16 | self.assertEqual(200, response.status_code, response.content) 17 | _json = response_json(response) 18 | self.assertEqual([], _json['results']) 19 | self.assertEqual(0, _json['count']) 20 | 21 | def test_execution_list(self): 22 | url = reverse('business-logic:rest:execution-list') 23 | response = self.client.get(url) 24 | self.assertEqual(200, response.status_code, response.content) 25 | _json = response_json(response) 26 | self.assertEqual(1, _json['count']) 27 | 28 | result = _json['results'][0] 29 | self.assertEqual(sorted(['id', 'finish_time', 'start_time', 'program_version']), sorted(result.keys())) 30 | 31 | def test_execution_item(self): 32 | url = reverse('business-logic:rest:execution', kwargs=dict(pk=Execution.objects.get().id)) 33 | response = self.client.get(url) 34 | self.assertEqual(200, response.status_code, response.content) 35 | _json = response_json(response) 36 | self.assertIn('arguments', _json.keys()) 37 | argument = _json['arguments'][0] 38 | self.assertEqual('test_model', argument['name']) 39 | self.assertEqual(Model._meta.verbose_name, argument['verbose_name']) 40 | content_type = argument['content_type'] 41 | self.assertEqual('test_app.Model', content_type['name']) 42 | self.assertEqual(ContentType.objects.get_for_model(Model).id, content_type['id']) 43 | 44 | self.assertEqual(self.test_model.id, argument['object_id']) 45 | 46 | 47 | class LogRestTest(ProgramRestTestBase): 48 | 49 | def setUp(self): 50 | super(LogRestTest, self).setUp() 51 | self.context = Context(log=True, debug=True) 52 | self.program_version.execute(test_model=self.test_model, context=self.context) 53 | 54 | def test_log(self): 55 | url = reverse('business-logic:rest:log', kwargs=dict(execution__id=Execution.objects.get().id)) 56 | response = self.client.get(url) 57 | self.assertEqual(200, response.status_code, response.content) 58 | _json = response_json(response) 59 | 60 | self.assertEqual(self.context.execution.log.node.id, _json['node']) 61 | 62 | for log_entry in (_json, _json['children'][0]): 63 | self.assertNotIn('id', log_entry) 64 | self.assertEqual( 65 | sorted(['node', 'previous_value', 'current_value', 'exception', 'children']), sorted(log_entry.keys())) 66 | 67 | self.assertIsInstance(log_entry['children'], list) 68 | -------------------------------------------------------------------------------- /tests/rest/test_urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .common import * 3 | 4 | 5 | class RestTestCase(TestCase): 6 | 7 | def test_urls(self): 8 | url = reverse('business-logic:rest:root') 9 | -------------------------------------------------------------------------------- /tests/test_app/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TestAppConfig(AppConfig): 5 | name = 'tests.test_app' 6 | default_auto_field = 'django.db.models.AutoField' 7 | -------------------------------------------------------------------------------- /tests/test_app/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from decimal import Decimal 5 | 6 | from django.db import models 7 | 8 | 9 | class Model(models.Model): 10 | int_value = models.PositiveIntegerField('Integer value', default=1) 11 | decimal_value = models.DecimalField('Decimal value', max_digits=10, decimal_places=4, default=Decimal('2.0')) 12 | string_value = models.CharField(max_length=255) 13 | date_value = models.DateField(null=True, blank=True) 14 | datetime_value = models.DateTimeField(null=True, blank=True) 15 | foreign_value = models.ForeignKey('RelatedModel', null=True, on_delete=models.CASCADE) 16 | 17 | class Meta: 18 | ordering = ('id',) 19 | verbose_name = 'Test Model' 20 | verbose_name_plural = 'Test Models' 21 | 22 | 23 | class RelatedModel(models.Model): 24 | int_value = models.PositiveIntegerField('Integer value', default=2) 25 | string_value = models.CharField('string value', max_length=255, default='foreign_value.string_value') 26 | 27 | class Meta: 28 | ordering = ('id',) 29 | verbose_name = 'Test Related Model' 30 | verbose_name_plural = 'Test Related Models' 31 | -------------------------------------------------------------------------------- /tests/test_assignment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from .common import * 5 | 6 | 7 | class AssignmentTest(TestCase): 8 | 9 | def test_assignment(self): 10 | context = Context() 11 | 12 | root = Node.add_root() 13 | 14 | var_def = VariableDefinition(name='K1') 15 | root.add_child(content_object=var_def) 16 | root = Node.objects.get(id=root.id) 17 | 18 | assignment_node = root.add_child(content_object=Assignment()) 19 | var = Variable(definition=var_def) 20 | var_node = assignment_node.add_child(content_object=var) 21 | tree_1plus2mul3(parent=assignment_node) 22 | 23 | root = Node.objects.get(id=root.id) 24 | 25 | result = root.interpret(context) 26 | var_value = context.get_variable(var_def) 27 | self.assertEqual(7, var_value) 28 | 29 | def test_var_assignment(self): 30 | context = Context() 31 | 32 | root = Node.add_root() 33 | 34 | var_def1 = VariableDefinition(name='K1') 35 | root.add_child(content_object=var_def1) 36 | root = Node.objects.get(id=root.id) 37 | 38 | var_def2 = VariableDefinition(name='K2') 39 | root.add_child(content_object=var_def2) 40 | root = Node.objects.get(id=root.id) 41 | 42 | assignment_node = root.add_child(content_object=Assignment()) 43 | var1 = Variable(definition=var_def1) 44 | var_node = assignment_node.add_child(content_object=var1) 45 | tree_1plus2mul3(parent=assignment_node) 46 | 47 | root = Node.objects.get(id=root.id) 48 | 49 | assignment_node = root.add_child(content_object=Assignment()) 50 | var2 = Variable(definition=var_def2) 51 | var_node = assignment_node.add_child(content_object=var2) 52 | var_node = assignment_node.add_child(content_object=var1) 53 | 54 | root = Node.objects.get(id=root.id) 55 | result = root.interpret(context) 56 | var_value = context.get_variable(var_def2) 57 | self.assertEqual(7, var_value) 58 | -------------------------------------------------------------------------------- /tests/test_constant.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from .common import * 5 | 6 | 7 | class ConstantTest(TestCase): 8 | 9 | def test_init(self): 10 | integer_const = NumberConstant(value=33) 11 | self.assertEqual(33, integer_const.value) 12 | 13 | def test_str(self): 14 | integer_const = NumberConstant(value=33) 15 | self.assertEqual(str(33), str(integer_const)) 16 | 17 | def test_interpret(self): 18 | integer_const = NumberConstant(value=33) 19 | context = Context() 20 | self.assertEqual(33, integer_const.interpret(context)) 21 | -------------------------------------------------------------------------------- /tests/test_context.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from django.test import TestCase 5 | from django.conf import settings 6 | from django.db import connection 7 | 8 | from .common import * 9 | 10 | 11 | class ContextTest(TestCase): 12 | 13 | def test_default_init(self): 14 | context = Context() 15 | self.assertFalse(context.config.log) 16 | 17 | def test_init(self): 18 | context = Context(log=True) 19 | self.assertTrue(context.config.log) 20 | 21 | def test_init_args_check(self): 22 | context = Context(log=True) 23 | self.assertRaises(TypeError, Context, wtf=True) 24 | -------------------------------------------------------------------------------- /tests/test_frame.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from .common import * 5 | 6 | 7 | class FrameTest(TestCase): 8 | 9 | def test_context_init(self): 10 | context = Context() 11 | self.assertTrue(context.frame is None) 12 | self.assertEqual(context.frames, []) 13 | 14 | def test_single_expression(self): 15 | root = tree_1plus2mul3() 16 | context = Context() 17 | 18 | def on_interpret_leave(**kwargs): 19 | node = kwargs['node'] 20 | if node == root: 21 | self.assertEqual(1, len(context.frames)) 22 | self.assertTrue(isinstance(context.frame, Frame)) 23 | self.assertTrue(context.frames) 24 | self.assertEqual(len(context.frames), 1) 25 | self.assertTrue(context.frame is context.frames[0]) 26 | 27 | signals.interpret_leave.connect(on_interpret_leave) 28 | root.interpret(context) 29 | self.assertFalse(context.frame) 30 | 31 | def test_switch_frames(self): 32 | # root + node1 + node1_1 33 | # + node2 + node2_1 34 | root = Node.add_root() 35 | node1 = root.add_child() 36 | root = Node.objects.get(id=root.id) 37 | node2 = root.add_child() 38 | root = Node.objects.get(id=root.id) 39 | node1_1 = tree_1plus2mul3(parent=node1) 40 | node2_1 = symmetric_tree(operator='*', value=5, count=4, parent=node2) 41 | context = Context() 42 | 43 | def on_interpret_enter(**kwargs): 44 | node = kwargs['node'] 45 | if node == root: 46 | self.assertEqual(1, len(context.frames)) 47 | elif node == node1: 48 | self.assertEqual(2, len(context.frames)) 49 | elif node == node2: 50 | self.assertEqual(2, len(context.frames)) 51 | elif node == node1_1: 52 | self.assertEqual(2, len(context.frames)) 53 | elif node == node2_1: 54 | self.assertEqual(2, len(context.frames)) 55 | 56 | def on_interpret_leave(**kwargs): 57 | node = kwargs['node'] 58 | if node == root: 59 | self.assertEqual(1, len(context.frames)) 60 | elif node == node1: 61 | self.assertEqual(2, len(context.frames)) 62 | elif node == node2: 63 | self.assertEqual(2, len(context.frames)) 64 | elif node == node1_1: 65 | self.assertEqual(2, len(context.frames)) 66 | elif node == node2_1: 67 | self.assertEqual(2, len(context.frames)) 68 | 69 | signals.block_interpret_enter.connect(on_interpret_enter) 70 | signals.interpret_leave.connect(on_interpret_leave) 71 | root.interpret(context) 72 | self.assertFalse(context.frame) 73 | self.assertFalse(context.frames) 74 | -------------------------------------------------------------------------------- /tests/test_ifstatement.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from .common import * 5 | 6 | 7 | class IfStatementTest(TestCase): 8 | 9 | def test_interpret_if(self): 10 | root, var_defs = create_if_statement(2) 11 | context = Context() 12 | context.set_variable(var_defs['IfCondition'], True) 13 | root.interpret(context) 14 | self.assertTrue(context.get_variable(var_defs['IfCondition'])) 15 | self.assertTrue(context.get_variable(var_defs['IfEnter'])) 16 | self.assertFalse(context.get_variable(var_defs['ElseEnter'])) 17 | 18 | def test_interpret_if_else(self): 19 | root, var_defs = create_if_statement(3) 20 | context = Context() 21 | root.interpret(context) 22 | self.assertFalse(context.get_variable(var_defs['IfCondition'])) 23 | self.assertFalse(context.get_variable(var_defs['IfEnter'])) 24 | self.assertTrue(context.get_variable(var_defs['ElseEnter'])) 25 | 26 | def test_interpret_elif(self): 27 | root, var_defs = create_if_statement(4) 28 | context = Context() 29 | context.set_variable(var_defs['ElseIfCondition1'], True) 30 | root.interpret(context) 31 | self.assertFalse(context.get_variable(var_defs['IfCondition'])) 32 | self.assertFalse(context.get_variable(var_defs['IfEnter'])) 33 | self.assertFalse(context.get_variable(var_defs['ElseEnter'])) 34 | self.assertTrue(context.get_variable(var_defs['ElseIfEnter1'])) 35 | 36 | def test_interpret_elif_2(self): 37 | root, var_defs = create_if_statement(6) 38 | context = Context() 39 | context.set_variable(var_defs['ElseIfCondition2'], True) 40 | root.interpret(context) 41 | self.assertFalse(context.get_variable(var_defs['IfCondition'])) 42 | self.assertFalse(context.get_variable(var_defs['IfEnter'])) 43 | self.assertFalse(context.get_variable(var_defs['ElseEnter'])) 44 | self.assertFalse(context.get_variable(var_defs['ElseIfEnter1'])) 45 | self.assertTrue(context.get_variable(var_defs['ElseIfEnter2'])) 46 | -------------------------------------------------------------------------------- /tests/test_reference.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .common import * 4 | 5 | 6 | class ReferenceDescriptorTest(TestCase): 7 | 8 | def setUp(self): 9 | self.content_type = ContentType.objects.get_for_model(Model) 10 | 11 | def test_reference_descriptor_search_fields_empty(self): 12 | reference_descriptor = ReferenceDescriptor.objects.create(content_type=self.content_type) 13 | self.assertEqual([], reference_descriptor.get_search_fields()) 14 | 15 | def test_reference_descriptor_search_fields_split(self): 16 | reference_descriptor = ReferenceDescriptor.objects.create( 17 | content_type=self.content_type, search_fields='xxx, yyy zzz; aa_bb__cc') 18 | self.assertEqual(['xxx', 'yyy', 'zzz', 'aa_bb__cc'], reference_descriptor.get_search_fields()) 19 | 20 | 21 | # see also tests.test_program.ProgramTest.test_program_version_execute_set_reference_variable 22 | 23 | 24 | class ReferenceConstantTest(TestCase): 25 | 26 | def test_interpret(self): 27 | constant = ReferenceConstant.objects.create() 28 | root = Node.add_root(content_object=constant) 29 | test_model = Model.objects.create() 30 | root.add_child(content_object=test_model) 31 | context = Context() 32 | self.assertEqual(test_model, constant.interpret(context)) 33 | 34 | def test_operator_eq_equals(self): 35 | root = Node.add_root(content_object=BinaryOperator(operator='==')) 36 | 37 | constant1 = ReferenceConstant.objects.create() 38 | test_model1 = Model.objects.create() 39 | constant1_node = root.add_child(content_object=constant1) 40 | constant1_node.add_child(content_object=test_model1) 41 | 42 | root = Node.objects.get(id=root.id) 43 | 44 | constant2 = ReferenceConstant.objects.create() 45 | test_model2 = Model.objects.create() 46 | constant2_node = root.add_child(content_object=constant2) 47 | constant2_node.add_child(content_object=test_model1) 48 | 49 | root = Node.objects.get(id=root.id) 50 | 51 | context = Context(log=True) 52 | self.assertTrue(root.interpret(context)) 53 | 54 | def test_operator_eq_not_equals(self): 55 | root = Node.add_root(content_object=BinaryOperator(operator='==')) 56 | 57 | constant1 = ReferenceConstant.objects.create() 58 | test_model1 = Model.objects.create() 59 | constant1_node = root.add_child(content_object=constant1) 60 | constant1_node.add_child(content_object=test_model1) 61 | 62 | root = Node.objects.get(id=root.id) 63 | 64 | constant2 = ReferenceConstant.objects.create() 65 | test_model2 = Model.objects.create() 66 | constant2_node = root.add_child(content_object=constant2) 67 | constant2_node.add_child(content_object=test_model2) 68 | 69 | root = Node.objects.get(id=root.id) 70 | 71 | context = Context(log=True) 72 | self.assertFalse(root.interpret(context)) 73 | -------------------------------------------------------------------------------- /tests/test_signals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from .common import * 5 | 6 | 7 | class SignalCatchException(Exception): 8 | pass 9 | 10 | 11 | def raise_on_enter(**kwargs): 12 | raise SignalCatchException(kwargs) 13 | 14 | 15 | class SignalsTest(TestCase): 16 | 17 | def tearDown(self): 18 | signals.interpret_enter.disconnect(raise_on_enter) 19 | signals.interpret_leave.disconnect(raise_on_enter) 20 | signals.block_interpret_enter.disconnect(raise_on_enter) 21 | signals.block_interpret_leave.disconnect(raise_on_enter) 22 | 23 | def test_interpret_enter(self): 24 | node = Node.add_root() 25 | context = Context() 26 | 27 | signals.interpret_enter.connect(raise_on_enter) 28 | self.assertRaises(SignalCatchException, node.interpret, context) 29 | 30 | def test_interpret_leave(self): 31 | node = Node.add_root() 32 | context = Context() 33 | 34 | signals.interpret_leave.connect(raise_on_enter) 35 | self.assertRaises(SignalCatchException, node.interpret, context) 36 | 37 | def test_block_interpret_enter(self): 38 | node = Node.add_root() 39 | context = Context() 40 | 41 | signals.block_interpret_enter.connect(raise_on_enter) 42 | self.assertRaises(SignalCatchException, node.interpret, context) 43 | 44 | def test_block_interpret_leave(self): 45 | node = Node.add_root() 46 | context = Context() 47 | 48 | signals.block_interpret_leave.connect(raise_on_enter) 49 | self.assertRaises(SignalCatchException, node.interpret, context) 50 | -------------------------------------------------------------------------------- /tests/test_stop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | from .common import * 5 | 6 | 7 | class StopTest(TestCase): 8 | 9 | def test_block(self): 10 | root = Node.add_root() 11 | node1 = root.add_child(content_object=StopInterpretation()) 12 | node2 = variable_assign_value(parent=root) 13 | root = Node.objects.get(id=root.id) 14 | context = Context() 15 | root.interpret(context) 16 | self.assertIsInstance(context.get_variable(VariableDefinition.objects.get(name='A')), Variable.Undefined) 17 | 18 | def test_nesting(self): 19 | root = Node.add_root() 20 | node1 = root.add_child() 21 | node1.add_child(content_object=StopInterpretation()) 22 | root = Node.objects.get(id=root.id) 23 | node2 = root.add_child() 24 | variable_assign_value(parent=node2) 25 | root = Node.objects.get(id=root.id) 26 | context = Context() 27 | root.interpret(context) 28 | self.assertIsInstance(context.get_variable(VariableDefinition.objects.get(name='A')), Variable.Undefined) 29 | --------------------------------------------------------------------------------