You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by gj...@apache.org on 2018/06/04 13:50:35 UTC

svn commit: r1832850 - in /bloodhound/branches/bh_core_experimental: ./ bh_core/ trackers/ trackers/fixtures/

Author: gjm
Date: Mon Jun  4 13:50:34 2018
New Revision: 1832850

URL: http://svn.apache.org/viewvc?rev=1832850&view=rev
Log:
add branch for new bh_core experiment

Added:
    bloodhound/branches/bh_core_experimental/
    bloodhound/branches/bh_core_experimental/Pipfile
    bloodhound/branches/bh_core_experimental/Pipfile.lock
    bloodhound/branches/bh_core_experimental/README.md
    bloodhound/branches/bh_core_experimental/bh_core/
    bloodhound/branches/bh_core_experimental/bh_core/__init__.py
    bloodhound/branches/bh_core_experimental/bh_core/settings.py
    bloodhound/branches/bh_core_experimental/bh_core/urls.py
    bloodhound/branches/bh_core_experimental/bh_core/wsgi.py
    bloodhound/branches/bh_core_experimental/functional_tests.py
    bloodhound/branches/bh_core_experimental/manage.py   (with props)
    bloodhound/branches/bh_core_experimental/pytest.ini
    bloodhound/branches/bh_core_experimental/trackers/
    bloodhound/branches/bh_core_experimental/trackers/__init__.py
    bloodhound/branches/bh_core_experimental/trackers/admin.py
    bloodhound/branches/bh_core_experimental/trackers/apps.py
    bloodhound/branches/bh_core_experimental/trackers/fixtures/
    bloodhound/branches/bh_core_experimental/trackers/fixtures/empty.yml
    bloodhound/branches/bh_core_experimental/trackers/models.py
    bloodhound/branches/bh_core_experimental/trackers/tests.py
    bloodhound/branches/bh_core_experimental/trackers/urls.py
    bloodhound/branches/bh_core_experimental/trackers/views.py

Added: bloodhound/branches/bh_core_experimental/Pipfile
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/Pipfile?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/Pipfile (added)
+++ bloodhound/branches/bh_core_experimental/Pipfile Mon Jun  4 13:50:34 2018
@@ -0,0 +1,16 @@
+[[source]]
+url = "https://pypi.python.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[dev-packages]
+selenium = "*"
+pytest-django = "*"
+PyYAML = "*"
+
+[packages]
+django = ">=2.0.0"
+pyyaml = "*"
+
+[requires]
+python_version = "3.6"

Added: bloodhound/branches/bh_core_experimental/Pipfile.lock
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/Pipfile.lock?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/Pipfile.lock (added)
+++ bloodhound/branches/bh_core_experimental/Pipfile.lock Mon Jun  4 13:50:34 2018
@@ -0,0 +1,144 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "4e791c7fd7b1d7f8749e94bcf56e4f78fb6514b5cf0d3174fe26d91f92cb672d"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.6"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.python.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "django": {
+            "hashes": [
+                "sha256:3eb25c99df1523446ec2dc1b00e25eb2ecbdf42c9d8b0b8b32a204a8db9011f8",
+                "sha256:69ff89fa3c3a8337015478a1a0744f52a9fef5d12c1efa01a01f99bcce9bf10c"
+            ],
+            "index": "pypi",
+            "version": "==2.0.6"
+        },
+        "pytz": {
+            "hashes": [
+                "sha256:65ae0c8101309c45772196b21b74c46b2e5d11b6275c45d251b150d5da334555",
+                "sha256:c06425302f2cf668f1bba7a0a03f3c1d34d4ebeef2c72003da308b3947c7f749"
+            ],
+            "version": "==2018.4"
+        },
+        "pyyaml": {
+            "hashes": [
+                "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8",
+                "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736",
+                "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f",
+                "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608",
+                "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8",
+                "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab",
+                "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7",
+                "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3",
+                "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1",
+                "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6",
+                "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8",
+                "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4",
+                "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca",
+                "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269"
+            ],
+            "index": "pypi",
+            "version": "==3.12"
+        }
+    },
+    "develop": {
+        "atomicwrites": {
+            "hashes": [
+                "sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585",
+                "sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6"
+            ],
+            "version": "==1.1.5"
+        },
+        "attrs": {
+            "hashes": [
+                "sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265",
+                "sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b"
+            ],
+            "version": "==18.1.0"
+        },
+        "more-itertools": {
+            "hashes": [
+                "sha256:2b6b9893337bfd9166bee6a62c2b0c9fe7735dcf85948b387ec8cba30e85d8e8",
+                "sha256:6703844a52d3588f951883005efcf555e49566a48afd4db4e965d69b883980d3",
+                "sha256:a18d870ef2ffca2b8463c0070ad17b5978056f403fb64e3f15fe62a52db21cc0"
+            ],
+            "version": "==4.2.0"
+        },
+        "pluggy": {
+            "hashes": [
+                "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff",
+                "sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c",
+                "sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5"
+            ],
+            "version": "==0.6.0"
+        },
+        "py": {
+            "hashes": [
+                "sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881",
+                "sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a"
+            ],
+            "version": "==1.5.3"
+        },
+        "pytest": {
+            "hashes": [
+                "sha256:39555d023af3200d004d09e51b4dd9fdd828baa863cded3fd6ba2f29f757ae2d",
+                "sha256:c76e93f3145a44812955e8d46cdd302d8a45fbfc7bf22be24fe231f9d8d8853a"
+            ],
+            "version": "==3.6.0"
+        },
+        "pytest-django": {
+            "hashes": [
+                "sha256:534505e0261cc566279032d9d887f844235342806fd63a6925689670fa1b29d7",
+                "sha256:7501942093db2250a32a4e36826edfc542347bb9b26c78ed0649cdcfd49e5789"
+            ],
+            "index": "pypi",
+            "version": "==3.2.1"
+        },
+        "pyyaml": {
+            "hashes": [
+                "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8",
+                "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736",
+                "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f",
+                "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608",
+                "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8",
+                "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab",
+                "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7",
+                "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3",
+                "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1",
+                "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6",
+                "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8",
+                "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4",
+                "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca",
+                "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269"
+            ],
+            "index": "pypi",
+            "version": "==3.12"
+        },
+        "selenium": {
+            "hashes": [
+                "sha256:1372101ad23798462038481f92ba1c7fab8385c788b05da6b44318f10ea52422",
+                "sha256:b8a2630fd858636c894960726ca3c94d8277e516ea3a9d81614fb819a5844764"
+            ],
+            "index": "pypi",
+            "version": "==3.12.0"
+        },
+        "six": {
+            "hashes": [
+                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
+                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
+            ],
+            "version": "==1.11.0"
+        }
+    }
+}

Added: bloodhound/branches/bh_core_experimental/README.md
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/README.md?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/README.md (added)
+++ bloodhound/branches/bh_core_experimental/README.md Mon Jun  4 13:50:34 2018
@@ -0,0 +1,91 @@
+# New Bloodhound
+
+## Requirements
+
+Bloodhound uses pipenv for development process.
+
+If you have pip installed already, installation can be a simple as
+
+```
+pip install --user pipenv
+```
+
+For more information on installing and usage of pipenv, see
+https://docs.pipenv.org/.
+
+Once pipenv is installed, the remaining job of installing should be as simple
+as
+
+```
+pipenv install
+```
+
+If this doesn't work, it should be done from the same directory as the
+`Pipenv` file.
+
+Though possibly annoying, the commands in this file will assume the use of
+`pipenv` but not that the pipenv shell has been activated.
+
+## Setup
+
+The basic setup steps to get running are:
+
+```
+pipenv run python manage.py makemigrations trackers
+pipenv run python manage.py migrate
+```
+
+The above will do the basic database setup.
+
+Note that currently models are in flux and, for the moment, no support should
+be expected for migrations as models change. This will change when basic
+models gain stability.
+
+## Running the development server:
+
+```
+pipenv run python manage.py runserver
+```
+
+## Unit Tests
+
+Unit tests are currently being written with the standard unittest framework.
+This may be replaced with pytest.
+
+The tests may be run with the following command:
+
+```
+pipenv run python manage.py test
+```
+
+Fixtures for tests when required can be generated with:
+
+```
+pipenv python manage.py dumpdata bh-core --format=yaml --indent=2 > bh-core/fixtures/[fixture-name].yaml
+```
+
+## Integration Tests
+
+Selenium tests currently require that Firefox is installed and `geckodriver` is
+also on the path. One way to do this is (example for 64bit linux distributions):
+
+```
+BIN_LOCATION="$HOME/.local/bin"
+PLATFORM_EXT="linux64.tar.gz"
+TMP_DIR=/tmp
+LATEST=$(wget -O - https://github.com/mozilla/geckodriver/releases/latest 2>&1 | awk 'match($0, /geckodriver-(v.*)-'"$PLATFORM_EXT"'/, a) {print a[1]; exit}')
+wget -N -P "$TMP_DIR" "https://github.com/mozilla/geckodriver/releases/download/$LATEST/geckodriver-$LATEST-$PLATFORM_EXT"
+tar -x geckodriver -zf "$TMP_DIR/geckodriver-$LATEST-$PLATFORM_EXT" -O > "$BIN_LOCATION"/geckodriver
+chmod +x "$BIN_LOCATION"/geckodriver
+```
+
+If `$BIN_LOCATION` is on the system path, it should be possible to run the integration tests.
+
+So, assuming the use of pipenv:
+
+```
+pipenv run python functional_tests.py
+```
+
+There are currently not many tests - those that are there are in place to test
+the setup above and assume that there will be useful tests in due course.

Added: bloodhound/branches/bh_core_experimental/bh_core/__init__.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/bh_core/__init__.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/bh_core/__init__.py (added)
+++ bloodhound/branches/bh_core_experimental/bh_core/__init__.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,17 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+

Added: bloodhound/branches/bh_core_experimental/bh_core/settings.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/bh_core/settings.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/bh_core/settings.py (added)
+++ bloodhound/branches/bh_core_experimental/bh_core/settings.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,142 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+"""
+Django settings for bh_core project.
+
+Generated by 'django-admin startproject' using Django 2.0.3.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.0/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/2.0/ref/settings/
+
+SECURITY WARNING: do not use the SECRET_KEY below. This file has only had
+minimal changes following on from the point of generation. Do not expect
+this project to be production ready at this point!
+"""
+
+import os
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'zcsm4+ng(*1ct-5ufjreki3d6emagywyn(&$hj8i$2lun*pm&r'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+    'trackers.apps.TrackersConfig',
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'bh_core.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'bh_core.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/2.0/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/2.0/howto/static-files/
+
+STATIC_URL = '/static/'

Added: bloodhound/branches/bh_core_experimental/bh_core/urls.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/bh_core/urls.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/bh_core/urls.py (added)
+++ bloodhound/branches/bh_core_experimental/bh_core/urls.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,40 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+"""bh_core URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/2.0/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+
+from django.contrib import admin
+from django.urls import include, path
+
+urlpatterns = [
+    path('', include('trackers.urls')),
+    # path('admin/', admin.site.urls),
+]

Added: bloodhound/branches/bh_core_experimental/bh_core/wsgi.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/bh_core/wsgi.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/bh_core/wsgi.py (added)
+++ bloodhound/branches/bh_core_experimental/bh_core/wsgi.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,33 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+"""
+WSGI config for bh_core project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bh_core.settings")
+
+application = get_wsgi_application()

Added: bloodhound/branches/bh_core_experimental/functional_tests.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/functional_tests.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/functional_tests.py (added)
+++ bloodhound/branches/bh_core_experimental/functional_tests.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+from selenium import webdriver
+import unittest
+
+
+class TicketViewTest(unittest.TestCase):
+    def setUp(self):
+        self.browser = webdriver.Firefox()
+        self.browser.implicitly_wait(3)
+
+    def tearDown(self):
+        self.browser.quit()
+
+    def test_user_can_add_view_and_delete_ticket(self):
+        self.browser.get('http://localhost:8000')
+
+        self.assertIn('Bloodhound', self.browser.title)
+
+
+if __name__ == '__main__':
+    unittest.main(warnings='ignore')

Added: bloodhound/branches/bh_core_experimental/manage.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/manage.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/manage.py (added)
+++ bloodhound/branches/bh_core_experimental/manage.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+import os
+import sys
+
+if __name__ == "__main__":
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bh_core.settings")
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)

Propchange: bloodhound/branches/bh_core_experimental/manage.py
------------------------------------------------------------------------------
    svn:executable = *

Added: bloodhound/branches/bh_core_experimental/pytest.ini
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/pytest.ini?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/pytest.ini (added)
+++ bloodhound/branches/bh_core_experimental/pytest.ini Mon Jun  4 13:50:34 2018
@@ -0,0 +1,2 @@
+[pytest]
+DJANGO_SETTINGS_MODULE = bh_core.settings

Added: bloodhound/branches/bh_core_experimental/trackers/__init__.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/__init__.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/trackers/__init__.py (added)
+++ bloodhound/branches/bh_core_experimental/trackers/__init__.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,16 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.

Added: bloodhound/branches/bh_core_experimental/trackers/admin.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/admin.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/trackers/admin.py (added)
+++ bloodhound/branches/bh_core_experimental/trackers/admin.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,21 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+
+from django.contrib import admin
+
+# Register your models here.

Added: bloodhound/branches/bh_core_experimental/trackers/apps.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/apps.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/trackers/apps.py (added)
+++ bloodhound/branches/bh_core_experimental/trackers/apps.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+from django.apps import AppConfig
+
+
+class TrackersConfig(AppConfig):
+    name = 'trackers'

Added: bloodhound/branches/bh_core_experimental/trackers/fixtures/empty.yml
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/fixtures/empty.yml?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/trackers/fixtures/empty.yml (added)
+++ bloodhound/branches/bh_core_experimental/trackers/fixtures/empty.yml Mon Jun  4 13:50:34 2018
@@ -0,0 +1 @@
+[]

Added: bloodhound/branches/bh_core_experimental/trackers/models.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/models.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/trackers/models.py (added)
+++ bloodhound/branches/bh_core_experimental/trackers/models.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,80 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+import difflib
+import functools
+import logging
+import uuid
+
+from django.db import models
+
+logger = logging.getLogger(__name__)
+
+
+class Ticket(models.Model):
+    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
+    created = models.DateTimeField(auto_now_add=True, editable=False)
+
+    def last_update(self):
+        last_event = self.changeevent_set.order_by('event_time').last()
+        return self.created if last_event is None else last_event.event_time
+
+    def add_field_event(self, field, newvalue):
+        current_lines = self.get_field_value(field).splitlines(keepends=True)
+        replace_lines = newvalue.splitlines(keepends=True)
+        result = '\n'.join(difflib.ndiff(current_lines, replace_lines))
+
+        tfield, created = TicketField.objects.get_or_create(name=field)
+        c = ChangeEvent(ticket=self, field=tfield, diff=result)
+        c.save()
+
+    def get_field_value(self, field):
+        try:
+            tfield = TicketField.objects.get(name=field)
+        except TicketField.DoesNotExist as e:
+            return ''
+
+        event = self.changeevent_set.filter(field=tfield).order_by('event_time').last()
+        return '' if event is None else event.value()
+
+
+class TicketField(models.Model):
+    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
+    name = models.CharField(max_length=32)
+
+class Label(TicketField):
+    pass
+
+class SharedField(TicketField):
+    pass
+
+class ChangeEvent(models.Model):
+    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
+    ticket = models.ForeignKey(Ticket, models.CASCADE, editable=False, null=False)
+    field = models.ForeignKey(TicketField, models.CASCADE, editable=False, null=False)
+    event_time = models.DateTimeField(auto_now_add=True, editable=False)
+    diff = models.TextField(editable=False)
+
+    def value(self, which=2):
+        return ''.join(difflib.restore(self.diff.splitlines(keepends=True), which)).strip()
+
+    old_value = functools.partialmethod(value, which=1)
+
+    def __str__(self):
+        return "Change to: {}; Field: {}; Diff: {}".format(
+            self.ticket, self.field, self.diff)
+

Added: bloodhound/branches/bh_core_experimental/trackers/tests.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/tests.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/trackers/tests.py (added)
+++ bloodhound/branches/bh_core_experimental/trackers/tests.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,93 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+from django.http import HttpRequest
+from django.test import TestCase
+from django.urls import resolve
+from trackers.views import home
+
+
+class HomePageTest(TestCase):
+    def test_root_url_resolves_to_home_page_view(self):
+        found = resolve('/')
+        self.assertEqual(found.func, home)
+
+    def test_home_page_returns_expected_html(self):
+        request = HttpRequest()
+        response = home(request)
+
+        self.assertTrue(response.content.startswith(b'<html>'))
+        self.assertIn(b'<title>Bloodhound Trackers</title>', response.content)
+        self.assertTrue(response.content.endswith(b'</html>'))
+
+
+from trackers.models import Ticket
+
+class TicketModelTest(TestCase):
+    def test_last_update_on_create_returns_created_date(self):
+        t = Ticket()
+        t.save()
+        self.assertEqual(t.created, t.last_update())
+
+    def test_last_update_returns_last_change_date(self):
+        # test may be safer with a fixture with an existing ticket to check
+        t = Ticket()
+        t.save()
+        t.add_field_event('summary', "this is the summary")
+        self.assertNotEqual(t.created, t.last_update())
+
+    def test_ticket_creation(self):
+        # Currently simple but may need updates for required fields
+        pre_count = Ticket.objects.count()
+        t = Ticket()
+        t.save()
+        self.assertEqual(pre_count + 1, Ticket.objects.count())
+
+    def test_ticket_add_field_event(self):
+        field = 'summary'
+        field_value = "this is the summary"
+
+        t = Ticket()
+        t.save()
+        t.add_field_event(field, field_value)
+
+        self.assertEqual(t.get_field_value(field), field_value)
+
+    def test_ticket_add_two_single_line_field_events_same_field(self):
+        field = 'summary'
+        first_field_value = "this is the summary"
+        second_field_value = "this is the replacement summary"
+
+        t = Ticket()
+        t.save()
+        t.add_field_event(field, first_field_value)
+        t.add_field_event(field, second_field_value)
+
+        self.assertEqual(t.get_field_value(field), second_field_value)
+
+    def test_ticket_add_two_multiline_field_events_same_field(self):
+        field = 'summary'
+        first_field_value = "this is the summary\nwith multiple lines"
+        second_field_value = "this is the replacement summary\nwith multiple lines"
+
+        t = Ticket()
+        t.save()
+        t.add_field_event(field, first_field_value)
+        t.add_field_event(field, second_field_value)
+
+        self.assertEqual(t.get_field_value(field), second_field_value)
+

Added: bloodhound/branches/bh_core_experimental/trackers/urls.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/urls.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/trackers/urls.py (added)
+++ bloodhound/branches/bh_core_experimental/trackers/urls.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,23 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+from django.urls import path
+from . import views
+
+urlpatterns = [
+    path('', views.home, name='home'),
+]

Added: bloodhound/branches/bh_core_experimental/trackers/views.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/views.py?rev=1832850&view=auto
==============================================================================
--- bloodhound/branches/bh_core_experimental/trackers/views.py (added)
+++ bloodhound/branches/bh_core_experimental/trackers/views.py Mon Jun  4 13:50:34 2018
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+from django.http import HttpResponse
+from django.shortcuts import render
+
+def home(request):
+    return HttpResponse('<html><title>Bloodhound Trackers</title></html>')



Working on bh_core (Was: svn commit: r1832850 - in /bloodhound/branches/bh_core_experimental: ./ bh_core/ trackers/ trackers/fixtures/)

Posted by Gary Martin <ga...@physics.org>.
Hi everyone,

I know it has been a while since I talked about this last but I managed to find some time to start playing with some of the ideas around a new bloodhound with django as the backend.

The intention is to start with a core that is relatively flexible. I can't say that I am all that happy with the model yet. In the search for flexibility, I have started by ignoring the label concept at the start.

There is no real ui at this point but I have created simple models for tickets, change events and field types. The change events are based on the difflib module which is part of the python standard library. I have chosen not to bother with optimising on space for a change event at this point. Instead I have gone for a form of diff that allows for the previous and new state to be completely determined from the single event rather than through extracting all the change events for a field on a ticket to rebuild everything. This may not be hugely different from just capturing the current state on each change. Anyway, we can look at changing that at some point if required.

So, obviously the things still to go in are:

 1. some kind of basic UI to give some kind of MVP
 2. labels
 3. multi tracker support

Note that with my related proposal, labels are intended to be a generic way to aid characterisation so that a label may represent a version, a milestone, a component or any other kind of tagging.

One thing we may be able to do to make versions and milestones more concrete would be to make it so that certain labels could be made to be unique to a ticket in a tracker. At this point a ticket *being* a version should give us all the flexibility we need to describe properties of the version. The final missing step for this kind of implementation would be to add a means to link non-version tickets to a version ticket.

We may be able to do this with simple conventions for labels so as to stick to a relatively small core.

Cheers,
    Gary

On Mon, 4 Jun 2018, at 2:50 PM, gjm@apache.org wrote:
> Author: gjm
> Date: Mon Jun  4 13:50:34 2018
> New Revision: 1832850
> 
> URL: http://svn.apache.org/viewvc?rev=1832850&view=rev
> Log:
> add branch for new bh_core experiment
> 
> Added:
>     bloodhound/branches/bh_core_experimental/
>     bloodhound/branches/bh_core_experimental/Pipfile
>     bloodhound/branches/bh_core_experimental/Pipfile.lock
>     bloodhound/branches/bh_core_experimental/README.md
>     bloodhound/branches/bh_core_experimental/bh_core/
>     bloodhound/branches/bh_core_experimental/bh_core/__init__.py
>     bloodhound/branches/bh_core_experimental/bh_core/settings.py
>     bloodhound/branches/bh_core_experimental/bh_core/urls.py
>     bloodhound/branches/bh_core_experimental/bh_core/wsgi.py
>     bloodhound/branches/bh_core_experimental/functional_tests.py
>     bloodhound/branches/bh_core_experimental/manage.py   (with props)
>     bloodhound/branches/bh_core_experimental/pytest.ini
>     bloodhound/branches/bh_core_experimental/trackers/
>     bloodhound/branches/bh_core_experimental/trackers/__init__.py
>     bloodhound/branches/bh_core_experimental/trackers/admin.py
>     bloodhound/branches/bh_core_experimental/trackers/apps.py
>     bloodhound/branches/bh_core_experimental/trackers/fixtures/
>     bloodhound/branches/bh_core_experimental/trackers/fixtures/empty.yml
>     bloodhound/branches/bh_core_experimental/trackers/models.py
>     bloodhound/branches/bh_core_experimental/trackers/tests.py
>     bloodhound/branches/bh_core_experimental/trackers/urls.py
>     bloodhound/branches/bh_core_experimental/trackers/views.py
> 
> Added: bloodhound/branches/bh_core_experimental/Pipfile
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/Pipfile?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/Pipfile (added)
> +++ bloodhound/branches/bh_core_experimental/Pipfile Mon Jun  4 13:50:34 
> 2018
> @@ -0,0 +1,16 @@
> +[[source]]
> +url = "https://pypi.python.org/simple"
> +verify_ssl = true
> +name = "pypi"
> +
> +[dev-packages]
> +selenium = "*"
> +pytest-django = "*"
> +PyYAML = "*"
> +
> +[packages]
> +django = ">=2.0.0"
> +pyyaml = "*"
> +
> +[requires]
> +python_version = "3.6"
> 
> Added: bloodhound/branches/bh_core_experimental/Pipfile.lock
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/Pipfile.lock?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/Pipfile.lock (added)
> +++ bloodhound/branches/bh_core_experimental/Pipfile.lock Mon Jun  4 
> 13:50:34 2018
> @@ -0,0 +1,144 @@
> +{
> +    "_meta": {
> +        "hash": {
> +            "sha256": 
> "4e791c7fd7b1d7f8749e94bcf56e4f78fb6514b5cf0d3174fe26d91f92cb672d"
> +        },
> +        "pipfile-spec": 6,
> +        "requires": {
> +            "python_version": "3.6"
> +        },
> +        "sources": [
> +            {
> +                "name": "pypi",
> +                "url": "https://pypi.python.org/simple",
> +                "verify_ssl": true
> +            }
> +        ]
> +    },
> +    "default": {
> +        "django": {
> +            "hashes": [
> +                
> "sha256:3eb25c99df1523446ec2dc1b00e25eb2ecbdf42c9d8b0b8b32a204a8db9011f8",
> +                
> "sha256:69ff89fa3c3a8337015478a1a0744f52a9fef5d12c1efa01a01f99bcce9bf10c"
> +            ],
> +            "index": "pypi",
> +            "version": "==2.0.6"
> +        },
> +        "pytz": {
> +            "hashes": [
> +                
> "sha256:65ae0c8101309c45772196b21b74c46b2e5d11b6275c45d251b150d5da334555",
> +                
> "sha256:c06425302f2cf668f1bba7a0a03f3c1d34d4ebeef2c72003da308b3947c7f749"
> +            ],
> +            "version": "==2018.4"
> +        },
> +        "pyyaml": {
> +            "hashes": [
> +                
> "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8",
> +                
> "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736",
> +                
> "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f",
> +                
> "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608",
> +                
> "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8",
> +                
> "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab",
> +                
> "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7",
> +                
> "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3",
> +                
> "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1",
> +                
> "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6",
> +                
> "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8",
> +                
> "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4",
> +                
> "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca",
> +                
> "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269"
> +            ],
> +            "index": "pypi",
> +            "version": "==3.12"
> +        }
> +    },
> +    "develop": {
> +        "atomicwrites": {
> +            "hashes": [
> +                
> "sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585",
> +                
> "sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6"
> +            ],
> +            "version": "==1.1.5"
> +        },
> +        "attrs": {
> +            "hashes": [
> +                
> "sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265",
> +                
> "sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b"
> +            ],
> +            "version": "==18.1.0"
> +        },
> +        "more-itertools": {
> +            "hashes": [
> +                
> "sha256:2b6b9893337bfd9166bee6a62c2b0c9fe7735dcf85948b387ec8cba30e85d8e8",
> +                
> "sha256:6703844a52d3588f951883005efcf555e49566a48afd4db4e965d69b883980d3",
> +                
> "sha256:a18d870ef2ffca2b8463c0070ad17b5978056f403fb64e3f15fe62a52db21cc0"
> +            ],
> +            "version": "==4.2.0"
> +        },
> +        "pluggy": {
> +            "hashes": [
> +                
> "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff",
> +                
> "sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c",
> +                
> "sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5"
> +            ],
> +            "version": "==0.6.0"
> +        },
> +        "py": {
> +            "hashes": [
> +                
> "sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881",
> +                
> "sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a"
> +            ],
> +            "version": "==1.5.3"
> +        },
> +        "pytest": {
> +            "hashes": [
> +                
> "sha256:39555d023af3200d004d09e51b4dd9fdd828baa863cded3fd6ba2f29f757ae2d",
> +                
> "sha256:c76e93f3145a44812955e8d46cdd302d8a45fbfc7bf22be24fe231f9d8d8853a"
> +            ],
> +            "version": "==3.6.0"
> +        },
> +        "pytest-django": {
> +            "hashes": [
> +                
> "sha256:534505e0261cc566279032d9d887f844235342806fd63a6925689670fa1b29d7",
> +                
> "sha256:7501942093db2250a32a4e36826edfc542347bb9b26c78ed0649cdcfd49e5789"
> +            ],
> +            "index": "pypi",
> +            "version": "==3.2.1"
> +        },
> +        "pyyaml": {
> +            "hashes": [
> +                
> "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8",
> +                
> "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736",
> +                
> "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f",
> +                
> "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608",
> +                
> "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8",
> +                
> "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab",
> +                
> "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7",
> +                
> "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3",
> +                
> "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1",
> +                
> "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6",
> +                
> "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8",
> +                
> "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4",
> +                
> "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca",
> +                
> "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269"
> +            ],
> +            "index": "pypi",
> +            "version": "==3.12"
> +        },
> +        "selenium": {
> +            "hashes": [
> +                
> "sha256:1372101ad23798462038481f92ba1c7fab8385c788b05da6b44318f10ea52422",
> +                
> "sha256:b8a2630fd858636c894960726ca3c94d8277e516ea3a9d81614fb819a5844764"
> +            ],
> +            "index": "pypi",
> +            "version": "==3.12.0"
> +        },
> +        "six": {
> +            "hashes": [
> +                
> "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
> +                
> "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
> +            ],
> +            "version": "==1.11.0"
> +        }
> +    }
> +}
> 
> Added: bloodhound/branches/bh_core_experimental/README.md
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/README.md?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/README.md (added)
> +++ bloodhound/branches/bh_core_experimental/README.md Mon Jun  4 
> 13:50:34 2018
> @@ -0,0 +1,91 @@
> +# New Bloodhound
> +
> +## Requirements
> +
> +Bloodhound uses pipenv for development process.
> +
> +If you have pip installed already, installation can be a simple as
> +
> +```
> +pip install --user pipenv
> +```
> +
> +For more information on installing and usage of pipenv, see
> +https://docs.pipenv.org/.
> +
> +Once pipenv is installed, the remaining job of installing should be as 
> simple
> +as
> +
> +```
> +pipenv install
> +```
> +
> +If this doesn't work, it should be done from the same directory as the
> +`Pipenv` file.
> +
> +Though possibly annoying, the commands in this file will assume the use 
> of
> +`pipenv` but not that the pipenv shell has been activated.
> +
> +## Setup
> +
> +The basic setup steps to get running are:
> +
> +```
> +pipenv run python manage.py makemigrations trackers
> +pipenv run python manage.py migrate
> +```
> +
> +The above will do the basic database setup.
> +
> +Note that currently models are in flux and, for the moment, no support 
> should
> +be expected for migrations as models change. This will change when 
> basic
> +models gain stability.
> +
> +## Running the development server:
> +
> +```
> +pipenv run python manage.py runserver
> +```
> +
> +## Unit Tests
> +
> +Unit tests are currently being written with the standard unittest 
> framework.
> +This may be replaced with pytest.
> +
> +The tests may be run with the following command:
> +
> +```
> +pipenv run python manage.py test
> +```
> +
> +Fixtures for tests when required can be generated with:
> +
> +```
> +pipenv python manage.py dumpdata bh-core --format=yaml --indent=2 > bh-
> core/fixtures/[fixture-name].yaml
> +```
> +
> +## Integration Tests
> +
> +Selenium tests currently require that Firefox is installed and 
> `geckodriver` is
> +also on the path. One way to do this is (example for 64bit linux 
> distributions):
> +
> +```
> +BIN_LOCATION="$HOME/.local/bin"
> +PLATFORM_EXT="linux64.tar.gz"
> +TMP_DIR=/tmp
> +LATEST=$(wget -O - 
> https://github.com/mozilla/geckodriver/releases/latest 2>&1 | awk 
> 'match($0, /geckodriver-(v.*)-'"$PLATFORM_EXT"'/, a) {print a[1]; 
> exit}')
> +wget -N -P "$TMP_DIR" 
> "https://github.com/mozilla/geckodriver/releases/download/$LATEST/geckodriver-$LATEST-$PLATFORM_EXT"
> +tar -x geckodriver -zf "$TMP_DIR/geckodriver-$LATEST-$PLATFORM_EXT" -O 
> > "$BIN_LOCATION"/geckodriver
> +chmod +x "$BIN_LOCATION"/geckodriver
> +```
> +
> +If `$BIN_LOCATION` is on the system path, it should be possible to run 
> the integration tests.
> +
> +So, assuming the use of pipenv:
> +
> +```
> +pipenv run python functional_tests.py
> +```
> +
> +There are currently not many tests - those that are there are in place 
> to test
> +the setup above and assume that there will be useful tests in due 
> course.
> 
> Added: bloodhound/branches/bh_core_experimental/bh_core/__init__.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/bh_core/__init__.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/bh_core/__init__.py (added)
> +++ bloodhound/branches/bh_core_experimental/bh_core/__init__.py Mon Jun  
> 4 13:50:34 2018
> @@ -0,0 +1,17 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> 
> Added: bloodhound/branches/bh_core_experimental/bh_core/settings.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/bh_core/settings.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/bh_core/settings.py (added)
> +++ bloodhound/branches/bh_core_experimental/bh_core/settings.py Mon Jun  
> 4 13:50:34 2018
> @@ -0,0 +1,142 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +"""
> +Django settings for bh_core project.
> +
> +Generated by 'django-admin startproject' using Django 2.0.3.
> +
> +For more information on this file, see
> +https://docs.djangoproject.com/en/2.0/topics/settings/
> +
> +For the full list of settings and their values, see
> +https://docs.djangoproject.com/en/2.0/ref/settings/
> +
> +SECURITY WARNING: do not use the SECRET_KEY below. This file has only 
> had
> +minimal changes following on from the point of generation. Do not 
> expect
> +this project to be production ready at this point!
> +"""
> +
> +import os
> +
> +# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
> +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
> +
> +
> +# Quick-start development settings - unsuitable for production
> +# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
> +
> +# SECURITY WARNING: keep the secret key used in production secret!
> +SECRET_KEY = 'zcsm4+ng(*1ct-5ufjreki3d6emagywyn(&$hj8i$2lun*pm&r'
> +
> +# SECURITY WARNING: don't run with debug turned on in production!
> +DEBUG = True
> +
> +ALLOWED_HOSTS = []
> +
> +
> +# Application definition
> +
> +INSTALLED_APPS = [
> +    'trackers.apps.TrackersConfig',
> +    'django.contrib.admin',
> +    'django.contrib.auth',
> +    'django.contrib.contenttypes',
> +    'django.contrib.sessions',
> +    'django.contrib.messages',
> +    'django.contrib.staticfiles',
> +]
> +
> +MIDDLEWARE = [
> +    'django.middleware.security.SecurityMiddleware',
> +    'django.contrib.sessions.middleware.SessionMiddleware',
> +    'django.middleware.common.CommonMiddleware',
> +    'django.middleware.csrf.CsrfViewMiddleware',
> +    'django.contrib.auth.middleware.AuthenticationMiddleware',
> +    'django.contrib.messages.middleware.MessageMiddleware',
> +    'django.middleware.clickjacking.XFrameOptionsMiddleware',
> +]
> +
> +ROOT_URLCONF = 'bh_core.urls'
> +
> +TEMPLATES = [
> +    {
> +        'BACKEND': 'django.template.backends.django.DjangoTemplates',
> +        'DIRS': [],
> +        'APP_DIRS': True,
> +        'OPTIONS': {
> +            'context_processors': [
> +                'django.template.context_processors.debug',
> +                'django.template.context_processors.request',
> +                'django.contrib.auth.context_processors.auth',
> +                'django.contrib.messages.context_processors.messages',
> +            ],
> +        },
> +    },
> +]
> +
> +WSGI_APPLICATION = 'bh_core.wsgi.application'
> +
> +
> +# Database
> +# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
> +
> +DATABASES = {
> +    'default': {
> +        'ENGINE': 'django.db.backends.sqlite3',
> +        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
> +    }
> +}
> +
> +
> +# Password validation
> +# 
> https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
> +
> +AUTH_PASSWORD_VALIDATORS = [
> +    {
> +        'NAME': 
> 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
> +    },
> +    {
> +        'NAME': 
> 'django.contrib.auth.password_validation.MinimumLengthValidator',
> +    },
> +    {
> +        'NAME': 
> 'django.contrib.auth.password_validation.CommonPasswordValidator',
> +    },
> +    {
> +        'NAME': 
> 'django.contrib.auth.password_validation.NumericPasswordValidator',
> +    },
> +]
> +
> +
> +# Internationalization
> +# https://docs.djangoproject.com/en/2.0/topics/i18n/
> +
> +LANGUAGE_CODE = 'en-us'
> +
> +TIME_ZONE = 'UTC'
> +
> +USE_I18N = True
> +
> +USE_L10N = True
> +
> +USE_TZ = True
> +
> +
> +# Static files (CSS, JavaScript, Images)
> +# https://docs.djangoproject.com/en/2.0/howto/static-files/
> +
> +STATIC_URL = '/static/'
> 
> Added: bloodhound/branches/bh_core_experimental/bh_core/urls.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/bh_core/urls.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/bh_core/urls.py (added)
> +++ bloodhound/branches/bh_core_experimental/bh_core/urls.py Mon Jun  4 
> 13:50:34 2018
> @@ -0,0 +1,40 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +"""bh_core URL Configuration
> +
> +The `urlpatterns` list routes URLs to views. For more information 
> please see:
> +    https://docs.djangoproject.com/en/2.0/topics/http/urls/
> +Examples:
> +Function views
> +    1. Add an import:  from my_app import views
> +    2. Add a URL to urlpatterns:  path('', views.home, name='home')
> +Class-based views
> +    1. Add an import:  from other_app.views import Home
> +    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
> +Including another URLconf
> +    1. Import the include() function: from django.urls import include, 
> path
> +    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
> +"""
> +
> +from django.contrib import admin
> +from django.urls import include, path
> +
> +urlpatterns = [
> +    path('', include('trackers.urls')),
> +    # path('admin/', admin.site.urls),
> +]
> 
> Added: bloodhound/branches/bh_core_experimental/bh_core/wsgi.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/bh_core/wsgi.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/bh_core/wsgi.py (added)
> +++ bloodhound/branches/bh_core_experimental/bh_core/wsgi.py Mon Jun  4 
> 13:50:34 2018
> @@ -0,0 +1,33 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +"""
> +WSGI config for bh_core project.
> +
> +It exposes the WSGI callable as a module-level variable named 
> ``application``.
> +
> +For more information on this file, see
> +https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
> +"""
> +
> +import os
> +
> +from django.core.wsgi import get_wsgi_application
> +
> +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bh_core.settings")
> +
> +application = get_wsgi_application()
> 
> Added: bloodhound/branches/bh_core_experimental/functional_tests.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/functional_tests.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/functional_tests.py (added)
> +++ bloodhound/branches/bh_core_experimental/functional_tests.py Mon Jun  
> 4 13:50:34 2018
> @@ -0,0 +1,39 @@
> +#!/usr/bin/env python
> +
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +from selenium import webdriver
> +import unittest
> +
> +
> +class TicketViewTest(unittest.TestCase):
> +    def setUp(self):
> +        self.browser = webdriver.Firefox()
> +        self.browser.implicitly_wait(3)
> +
> +    def tearDown(self):
> +        self.browser.quit()
> +
> +    def test_user_can_add_view_and_delete_ticket(self):
> +        self.browser.get('http://localhost:8000')
> +
> +        self.assertIn('Bloodhound', self.browser.title)
> +
> +
> +if __name__ == '__main__':
> +    unittest.main(warnings='ignore')
> 
> Added: bloodhound/branches/bh_core_experimental/manage.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/manage.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/manage.py (added)
> +++ bloodhound/branches/bh_core_experimental/manage.py Mon Jun  4 
> 13:50:34 2018
> @@ -0,0 +1,33 @@
> +#!/usr/bin/env python
> +
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +import os
> +import sys
> +
> +if __name__ == "__main__":
> +    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bh_core.settings")
> +    try:
> +        from django.core.management import execute_from_command_line
> +    except ImportError as exc:
> +        raise ImportError(
> +            "Couldn't import Django. Are you sure it's installed and "
> +            "available on your PYTHONPATH environment variable? Did you 
> "
> +            "forget to activate a virtual environment?"
> +        ) from exc
> +    execute_from_command_line(sys.argv)
> 
> Propchange: bloodhound/branches/bh_core_experimental/manage.py
> ------------------------------------------------------------------------------
>     svn:executable = *
> 
> Added: bloodhound/branches/bh_core_experimental/pytest.ini
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/pytest.ini?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/pytest.ini (added)
> +++ bloodhound/branches/bh_core_experimental/pytest.ini Mon Jun  4 
> 13:50:34 2018
> @@ -0,0 +1,2 @@
> +[pytest]
> +DJANGO_SETTINGS_MODULE = bh_core.settings
> 
> Added: bloodhound/branches/bh_core_experimental/trackers/__init__.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/__init__.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/trackers/__init__.py 
> (added)
> +++ bloodhound/branches/bh_core_experimental/trackers/__init__.py Mon 
> Jun  4 13:50:34 2018
> @@ -0,0 +1,16 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> 
> Added: bloodhound/branches/bh_core_experimental/trackers/admin.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/admin.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/trackers/admin.py (added)
> +++ bloodhound/branches/bh_core_experimental/trackers/admin.py Mon Jun  
> 4 13:50:34 2018
> @@ -0,0 +1,21 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +
> +from django.contrib import admin
> +
> +# Register your models here.
> 
> Added: bloodhound/branches/bh_core_experimental/trackers/apps.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/apps.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/trackers/apps.py (added)
> +++ bloodhound/branches/bh_core_experimental/trackers/apps.py Mon Jun  4 
> 13:50:34 2018
> @@ -0,0 +1,22 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +from django.apps import AppConfig
> +
> +
> +class TrackersConfig(AppConfig):
> +    name = 'trackers'
> 
> Added: bloodhound/branches/bh_core_experimental/trackers/fixtures/
> empty.yml
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/fixtures/empty.yml?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/trackers/fixtures/empty.yml 
> (added)
> +++ bloodhound/branches/bh_core_experimental/trackers/fixtures/empty.yml 
> Mon Jun  4 13:50:34 2018
> @@ -0,0 +1 @@
> +[]
> 
> Added: bloodhound/branches/bh_core_experimental/trackers/models.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/models.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/trackers/models.py (added)
> +++ bloodhound/branches/bh_core_experimental/trackers/models.py Mon Jun  
> 4 13:50:34 2018
> @@ -0,0 +1,80 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +import difflib
> +import functools
> +import logging
> +import uuid
> +
> +from django.db import models
> +
> +logger = logging.getLogger(__name__)
> +
> +
> +class Ticket(models.Model):
> +    id = models.UUIDField(primary_key=True, default=uuid.uuid4, 
> editable=False)
> +    created = models.DateTimeField(auto_now_add=True, editable=False)
> +
> +    def last_update(self):
> +        last_event = self.changeevent_set.order_by('event_time').last()
> +        return self.created if last_event is None else 
> last_event.event_time
> +
> +    def add_field_event(self, field, newvalue):
> +        current_lines = 
> self.get_field_value(field).splitlines(keepends=True)
> +        replace_lines = newvalue.splitlines(keepends=True)
> +        result = '\n'.join(difflib.ndiff(current_lines, replace_lines))
> +
> +        tfield, created = TicketField.objects.get_or_create(name=field)
> +        c = ChangeEvent(ticket=self, field=tfield, diff=result)
> +        c.save()
> +
> +    def get_field_value(self, field):
> +        try:
> +            tfield = TicketField.objects.get(name=field)
> +        except TicketField.DoesNotExist as e:
> +            return ''
> +
> +        event = 
> self.changeevent_set.filter(field=tfield).order_by('event_time').last()
> +        return '' if event is None else event.value()
> +
> +
> +class TicketField(models.Model):
> +    id = models.UUIDField(primary_key=True, default=uuid.uuid4, 
> editable=False)
> +    name = models.CharField(max_length=32)
> +
> +class Label(TicketField):
> +    pass
> +
> +class SharedField(TicketField):
> +    pass
> +
> +class ChangeEvent(models.Model):
> +    id = models.UUIDField(primary_key=True, default=uuid.uuid4, 
> editable=False)
> +    ticket = models.ForeignKey(Ticket, models.CASCADE, editable=False, 
> null=False)
> +    field = models.ForeignKey(TicketField, models.CASCADE, 
> editable=False, null=False)
> +    event_time = models.DateTimeField(auto_now_add=True, 
> editable=False)
> +    diff = models.TextField(editable=False)
> +
> +    def value(self, which=2):
> +        return 
> ''.join(difflib.restore(self.diff.splitlines(keepends=True), 
> which)).strip()
> +
> +    old_value = functools.partialmethod(value, which=1)
> +
> +    def __str__(self):
> +        return "Change to: {}; Field: {}; Diff: {}".format(
> +            self.ticket, self.field, self.diff)
> +
> 
> Added: bloodhound/branches/bh_core_experimental/trackers/tests.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/tests.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/trackers/tests.py (added)
> +++ bloodhound/branches/bh_core_experimental/trackers/tests.py Mon Jun  
> 4 13:50:34 2018
> @@ -0,0 +1,93 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +from django.http import HttpRequest
> +from django.test import TestCase
> +from django.urls import resolve
> +from trackers.views import home
> +
> +
> +class HomePageTest(TestCase):
> +    def test_root_url_resolves_to_home_page_view(self):
> +        found = resolve('/')
> +        self.assertEqual(found.func, home)
> +
> +    def test_home_page_returns_expected_html(self):
> +        request = HttpRequest()
> +        response = home(request)
> +
> +        self.assertTrue(response.content.startswith(b'<html>'))
> +        self.assertIn(b'<title>Bloodhound Trackers</title>', 
> response.content)
> +        self.assertTrue(response.content.endswith(b'</html>'))
> +
> +
> +from trackers.models import Ticket
> +
> +class TicketModelTest(TestCase):
> +    def test_last_update_on_create_returns_created_date(self):
> +        t = Ticket()
> +        t.save()
> +        self.assertEqual(t.created, t.last_update())
> +
> +    def test_last_update_returns_last_change_date(self):
> +        # test may be safer with a fixture with an existing ticket to 
> check
> +        t = Ticket()
> +        t.save()
> +        t.add_field_event('summary', "this is the summary")
> +        self.assertNotEqual(t.created, t.last_update())
> +
> +    def test_ticket_creation(self):
> +        # Currently simple but may need updates for required fields
> +        pre_count = Ticket.objects.count()
> +        t = Ticket()
> +        t.save()
> +        self.assertEqual(pre_count + 1, Ticket.objects.count())
> +
> +    def test_ticket_add_field_event(self):
> +        field = 'summary'
> +        field_value = "this is the summary"
> +
> +        t = Ticket()
> +        t.save()
> +        t.add_field_event(field, field_value)
> +
> +        self.assertEqual(t.get_field_value(field), field_value)
> +
> +    def test_ticket_add_two_single_line_field_events_same_field(self):
> +        field = 'summary'
> +        first_field_value = "this is the summary"
> +        second_field_value = "this is the replacement summary"
> +
> +        t = Ticket()
> +        t.save()
> +        t.add_field_event(field, first_field_value)
> +        t.add_field_event(field, second_field_value)
> +
> +        self.assertEqual(t.get_field_value(field), second_field_value)
> +
> +    def test_ticket_add_two_multiline_field_events_same_field(self):
> +        field = 'summary'
> +        first_field_value = "this is the summary\nwith multiple lines"
> +        second_field_value = "this is the replacement summary\nwith 
> multiple lines"
> +
> +        t = Ticket()
> +        t.save()
> +        t.add_field_event(field, first_field_value)
> +        t.add_field_event(field, second_field_value)
> +
> +        self.assertEqual(t.get_field_value(field), second_field_value)
> +
> 
> Added: bloodhound/branches/bh_core_experimental/trackers/urls.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/urls.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/trackers/urls.py (added)
> +++ bloodhound/branches/bh_core_experimental/trackers/urls.py Mon Jun  4 
> 13:50:34 2018
> @@ -0,0 +1,23 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +from django.urls import path
> +from . import views
> +
> +urlpatterns = [
> +    path('', views.home, name='home'),
> +]
> 
> Added: bloodhound/branches/bh_core_experimental/trackers/views.py
> URL: 
> http://svn.apache.org/viewvc/bloodhound/branches/bh_core_experimental/trackers/views.py?rev=1832850&view=auto
> ==============================================================================
> --- bloodhound/branches/bh_core_experimental/trackers/views.py (added)
> +++ bloodhound/branches/bh_core_experimental/trackers/views.py Mon Jun  
> 4 13:50:34 2018
> @@ -0,0 +1,22 @@
> +#  Licensed to the Apache Software Foundation (ASF) under one
> +#  or more contributor license agreements.  See the NOTICE file
> +#  distributed with this work for additional information
> +#  regarding copyright ownership.  The ASF licenses this file
> +#  to you under the Apache License, Version 2.0 (the
> +#  "License"); you may not use this file except in compliance
> +#  with the License.  You may obtain a copy of the License at
> +#
> +#   http://www.apache.org/licenses/LICENSE-2.0
> +#
> +#  Unless required by applicable law or agreed to in writing,
> +#  software distributed under the License is distributed on an
> +#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +#  KIND, either express or implied.  See the License for the
> +#  specific language governing permissions and limitations
> +#  under the License.
> +
> +from django.http import HttpResponse
> +from django.shortcuts import render
> +
> +def home(request):
> +    return HttpResponse('<html><title>Bloodhound Trackers</title></
> html>')
> 
> 


-- 
Cheers,
    Gary