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 2020/09/23 20:27:43 UTC

[bloodhound-core] branch main created (now 104011c)

This is an automated email from the ASF dual-hosted git repository.

gjm pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git.


      at 104011c  Correct path in README.md; add superuser creation

This branch includes the following new commits:

     new 0394b68  add branch for new bh_core experiment
     new 3854c5d  Improving README.md accuracy; adjusting Pipfile
     new 367f219  Extract common model enforcing id & created fields
     new d3dc9d9  Add djangorestframework and register admin pages
     new 73249fa  Complete adding django rest framework example code
     new 31f1b1d  Adds a basic ticket view and update api
     new e810572  Add swagger for api viewing and more api endpoints
     new 5ff4a05  Attempt to add links to api output for ticket list
     new a9d5a3c  Remove strict Python requirement; update README
     new c862c4a  Update to README for some more useful information
     new ae3966a  Remove python version dependency
     new ac3d187  Refactor api
     new d5f0a61  Updates to setup advice
     new 104011c  Correct path in README.md; add superuser creation

The 14 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[bloodhound-core] 14/14: Correct path in README.md; add superuser creation

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit 104011c39fc8165a59faf49709fa51b1a7cded9c
Author: Gary Martin <gj...@apache.org>
AuthorDate: Tue Sep 22 15:40:30 2020 +0100

    Correct path in README.md; add superuser creation
---
 README.md | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index f0b6e39..ee10622 100644
--- a/README.md
+++ b/README.md
@@ -83,6 +83,20 @@ 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.
 
+## Setting up a superuser
+
+For certain operations it will be useful to have accounts and superusers to
+work with. There are a few ways to add a superuser. For interactive use, the
+`createsuperuser` action is usually straightforward enough:
+
+```
+pipenv run python manage.py createsuperuser --email admin@example.com --username admin
+```
+
+Entering the password twice on prompting is currently required. If the options
+for `--username` and `--email` are skipped, the command will request these
+details first.
+
 ## Running the development server:
 
 ```
@@ -100,8 +114,8 @@ Currently there is not much to see at the specified location. More work has
 been done on the core API. The following views may be of interest as you
 explore:
 
- * http://127.0.0.1:8000/ticket/
  * http://127.0.0.1:8000/schema_view/
+ * http://127.0.0.1:8000/api/
 
 These paths are subject to change.
 


[bloodhound-core] 07/14: Add swagger for api viewing and more api endpoints

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit e810572d83da6650d2cbbe0367a79134d005f385
Author: Gary Martin <gj...@apache.org>
AuthorDate: Sun Oct 14 00:23:29 2018 +0000

    Add swagger for api viewing and more api endpoints
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1843804 13f79535-47bb-0310-9956-ffa450edef68
---
 Pipfile                 |   1 +
 Pipfile.lock            | 118 +++++++++++++++++++++++++++++++++++++++++++++++-
 bh_core/settings.py     |   1 +
 trackers/models.py      |  10 ++--
 trackers/serializers.py |   9 +++-
 trackers/urls.py        |   3 ++
 trackers/views.py       |  29 +++++++++---
 7 files changed, 159 insertions(+), 12 deletions(-)

diff --git a/Pipfile b/Pipfile
index 056c071..fe4fd91 100644
--- a/Pipfile
+++ b/Pipfile
@@ -11,6 +11,7 @@ PyYAML = "*"
 [packages]
 django = ">=2.0.0"
 djangorestframework = "*"
+django-rest-swagger = "*"
 
 [requires]
 python_version = "3.6"
diff --git a/Pipfile.lock b/Pipfile.lock
index 7d244b5..c681e5e 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "f70363525f7fd7a95075f66854bc135a3c1d45142d1e02ebe126442621614be9"
+            "sha256": "9d8b5c33b68a3c95ceadb2fa4e343ea79a7739d9711887ae9562f774b830d49d"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -16,6 +16,34 @@
         ]
     },
     "default": {
+        "certifi": {
+            "hashes": [
+                "sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638",
+                "sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a"
+            ],
+            "version": "==2018.8.24"
+        },
+        "chardet": {
+            "hashes": [
+                "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+                "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+            ],
+            "version": "==3.0.4"
+        },
+        "coreapi": {
+            "hashes": [
+                "sha256:46145fcc1f7017c076a2ef684969b641d18a2991051fddec9458ad3f78ffc1cb",
+                "sha256:bf39d118d6d3e171f10df9ede5666f63ad80bba9a29a8ec17726a66cf52ee6f3"
+            ],
+            "version": "==2.3.3"
+        },
+        "coreschema": {
+            "hashes": [
+                "sha256:5e6ef7bf38c1525d5e55a895934ab4273548629f16aed5c0a6caa74ebf45551f",
+                "sha256:9503506007d482ab0867ba14724b93c18a33b22b6d19fb419ef2d239dd4a1607"
+            ],
+            "version": "==0.0.4"
+        },
         "django": {
             "hashes": [
                 "sha256:acdcc1f61fdb0a0c82a1d3bf1879a414e7732ea894a7632af7f6d66ec7ab5bb3",
@@ -24,6 +52,14 @@
             "index": "pypi",
             "version": "==2.1.2"
         },
+        "django-rest-swagger": {
+            "hashes": [
+                "sha256:48f6aded9937e90ae7cbe9e6c932b9744b8af80cc4e010088b3278c700e0685b",
+                "sha256:b039b0288bab4665cd45dc5d16f94b13911bc4ad0ed55f74ad3b90aa31c87c17"
+            ],
+            "index": "pypi",
+            "version": "==2.2.0"
+        },
         "djangorestframework": {
             "hashes": [
                 "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9",
@@ -32,12 +68,92 @@
             "index": "pypi",
             "version": "==3.8.2"
         },
+        "idna": {
+            "hashes": [
+                "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
+                "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
+            ],
+            "version": "==2.7"
+        },
+        "itypes": {
+            "hashes": [
+                "sha256:c6e77bb9fd68a4bfeb9d958fea421802282451a25bac4913ec94db82a899c073"
+            ],
+            "version": "==1.1.0"
+        },
+        "jinja2": {
+            "hashes": [
+                "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
+                "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
+            ],
+            "version": "==2.10"
+        },
+        "markupsafe": {
+            "hashes": [
+                "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
+            ],
+            "version": "==1.0"
+        },
+        "openapi-codec": {
+            "hashes": [
+                "sha256:1bce63289edf53c601ea3683120641407ff6b708803b8954c8a876fe778d2145"
+            ],
+            "version": "==1.3.2"
+        },
         "pytz": {
             "hashes": [
                 "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053",
                 "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277"
             ],
             "version": "==2018.5"
+        },
+        "requests": {
+            "hashes": [
+                "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1",
+                "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a"
+            ],
+            "version": "==2.19.1"
+        },
+        "simplejson": {
+            "hashes": [
+                "sha256:067a7177ddfa32e1483ba5169ebea1bc2ea27f224853211ca669325648ca5642",
+                "sha256:2b8cb601d9ba0381499db719ccc9dfbb2fbd16013f5ff096b1a68a4775576a04",
+                "sha256:2c139daf167b96f21542248f8e0a06596c9b9a7a41c162cc5c9ee9f3833c93cd",
+                "sha256:2fc546e6af49fb45b93bbe878dea4c48edc34083729c0abd09981fe55bdf7f91",
+                "sha256:354fa32b02885e6dae925f1b5bbf842c333c1e11ea5453ddd67309dc31fdb40a",
+                "sha256:37e685986cf6f8144607f90340cff72d36acf654f3653a6c47b84c5c38d00df7",
+                "sha256:3af610ee72efbe644e19d5eaad575c73fb83026192114e5f6719f4901097fce2",
+                "sha256:3b919fc9cf508f13b929a9b274c40786036b31ad28657819b3b9ba44ba651f50",
+                "sha256:3dd289368bbd064974d9a5961101f080e939cbe051e6689a193c99fb6e9ac89b",
+                "sha256:491de7acc423e871a814500eb2dcea8aa66c4a4b1b4825d18f756cdf58e370cb",
+                "sha256:495511fe5f10ccf4e3ed4fc0c48318f533654db6c47ecbc970b4ed215c791968",
+                "sha256:65b41a5cda006cfa7c66eabbcf96aa704a6be2a5856095b9e2fd8c293bad2b46",
+                "sha256:6c3258ffff58712818a233b9737fe4be943d306c40cf63d14ddc82ba563f483a",
+                "sha256:75e3f0b12c28945c08f54350d91e624f8dd580ab74fd4f1bbea54bc6b0165610",
+                "sha256:79b129fe65fdf3765440f7a73edaffc89ae9e7885d4e2adafe6aa37913a00fbb",
+                "sha256:b1f329139ba647a9548aa05fb95d046b4a677643070dc2afc05fa2e975d09ca5",
+                "sha256:c206f47cbf9f32b573c9885f0ec813d2622976cf5effcf7e472344bc2e020ac1",
+                "sha256:d8e238f20bcf70063ee8691d4a72162bcec1f4c38f83c93e6851e72ad545dabb",
+                "sha256:ee9625fc8ee164902dfbb0ff932b26df112da9f871c32f0f9c1bcf20c350fe2a",
+                "sha256:fb2530b53c28f0d4d84990e945c2ebb470edb469d63e389bf02ff409012fe7c5",
+                "sha256:feadb95170e45f439455354904768608e356c5b174ca30b3d11b0e3f24b5c0df"
+            ],
+            "version": "==3.16.0"
+        },
+        "uritemplate": {
+            "hashes": [
+                "sha256:01c69f4fe8ed503b2951bef85d996a9d22434d2431584b5b107b2981ff416fbd",
+                "sha256:1b9c467a940ce9fb9f50df819e8ddd14696f89b9a8cc87ac77952ba416e0a8fd",
+                "sha256:c02643cebe23fc8adb5e6becffe201185bf06c40bda5c0b4028a93f1527d011d"
+            ],
+            "version": "==3.0.0"
+        },
+        "urllib3": {
+            "hashes": [
+                "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
+                "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
+            ],
+            "version": "==1.23"
         }
     },
     "develop": {
diff --git a/bh_core/settings.py b/bh_core/settings.py
index 7482fd8..1cf7328 100644
--- a/bh_core/settings.py
+++ b/bh_core/settings.py
@@ -60,6 +60,7 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'rest_framework',
+    'rest_framework_swagger',
 ]
 
 MIDDLEWARE = [
diff --git a/trackers/models.py b/trackers/models.py
index 6637f17..d819fd4 100644
--- a/trackers/models.py
+++ b/trackers/models.py
@@ -24,6 +24,7 @@ from django.db import models
 
 logger = logging.getLogger(__name__)
 
+
 class ModelCommon(models.Model):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
     created = models.DateTimeField(auto_now_add=True, editable=False)
@@ -31,6 +32,7 @@ class ModelCommon(models.Model):
     class Meta:
         abstract = True
 
+
 class Ticket(ModelCommon):
     title = models.CharField(max_length=200, null=True)
     description = models.TextField(null=True)
@@ -61,10 +63,11 @@ class Ticket(ModelCommon):
 class TicketField(ModelCommon):
     name = models.CharField(max_length=32)
 
+
 class ChangeEvent(ModelCommon):
-    ticket = models.ForeignKey(Ticket, models.CASCADE, editable=False, null=False)
-    field = models.ForeignKey(TicketField, models.CASCADE, editable=False, null=False)
-    diff = models.TextField(editable=False)
+    ticket = models.ForeignKey(Ticket, models.CASCADE, null=False)
+    field = models.ForeignKey(TicketField, models.CASCADE)
+    diff = models.TextField()
 
     def value(self, which=2):
         return ''.join(difflib.restore(self.diff.splitlines(keepends=True), which)).strip()
@@ -74,4 +77,3 @@ class ChangeEvent(ModelCommon):
     def __str__(self):
         return "Change to: {}; Field: {}; Diff: {}".format(
             self.ticket, self.field, self.diff)
-
diff --git a/trackers/serializers.py b/trackers/serializers.py
index 8d0a8d6..5416191 100644
--- a/trackers/serializers.py
+++ b/trackers/serializers.py
@@ -1,5 +1,5 @@
 from rest_framework import serializers
-from trackers.models import Ticket
+from trackers.models import Ticket, TicketField, ChangeEvent
 
 
 class TicketSerializer(serializers.ModelSerializer):
@@ -7,6 +7,13 @@ class TicketSerializer(serializers.ModelSerializer):
         model = Ticket
         fields = '__all__'
 
+
+class TicketFieldSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = TicketField
+        fields = '__all__'
+
+
 class ChangeEventSerializer(serializers.ModelSerializer):
     class Meta:
         model = ChangeEvent
diff --git a/trackers/urls.py b/trackers/urls.py
index 3987dd5..20b115b 100644
--- a/trackers/urls.py
+++ b/trackers/urls.py
@@ -20,6 +20,9 @@ from . import views
 
 urlpatterns = [
     path('', views.home, name='home'),
+    path('schema_view/', views.schema_view),
+    path('field/', views.TicketFieldListCreate.as_view()),
     path('ticket/', views.TicketListCreate.as_view()),
     path('ticket/<uuid:id>', views.TicketViewUpdate.as_view()),
+    path('ticket/<uuid:id>/event/', views.ChangeEventListCreate.as_view()),
 ]
diff --git a/trackers/views.py b/trackers/views.py
index 8aa474e..13498ed 100644
--- a/trackers/views.py
+++ b/trackers/views.py
@@ -18,19 +18,36 @@
 from django.http import HttpResponse
 from django.shortcuts import render
 from rest_framework import generics
-from trackers.serializers import TicketSerializer
-from trackers.models import Ticket
+from . import serializers
+from . import models
+
+from rest_framework_swagger.views import get_swagger_view
+
+schema_view = get_swagger_view(title='Bloodhound Core API')
+
 
 def home(request):
     return HttpResponse('<html><title>Bloodhound Trackers</title></html>')
 
 
 class TicketListCreate(generics.ListCreateAPIView):
-    queryset = Ticket.objects.all()
-    serializer_class = TicketSerializer
+    queryset = models.Ticket.objects.all()
+    serializer_class = serializers.TicketSerializer
 
 
 class TicketViewUpdate(generics.RetrieveUpdateAPIView):
-    queryset = Ticket.objects.all()
-    serializer_class = TicketSerializer
+    queryset = models.Ticket.objects.all()
+    serializer_class = serializers.TicketSerializer
     lookup_field = 'id'
+
+
+class TicketFieldListCreate(generics.ListCreateAPIView):
+    queryset = models.TicketField.objects.all()
+    serializer_class = serializers.TicketFieldSerializer
+    lookup_field = 'ticket'
+
+
+class ChangeEventListCreate(generics.ListCreateAPIView):
+    queryset = models.ChangeEvent.objects.all()
+    serializer_class = serializers.ChangeEventSerializer
+    lookup_field = 'ticket'


[bloodhound-core] 12/14: Refactor api

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit ac3d18794ac0566a8f28f2fb19bac0843d3c8c29
Author: Gary Martin <gj...@apache.org>
AuthorDate: Tue Feb 19 13:29:30 2019 +0000

    Refactor api
    
     * move api under its own path
     * add urls to model views to help with api navigation
     * add user and group to api
     * experiment with change events as a subpath of tickets
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1853879 13f79535-47bb-0310-9956-ffa450edef68
---
 bh_core/settings.py     |  6 ++++++
 trackers/models.py      | 16 +++++++++++++---
 trackers/serializers.py | 31 +++++++++++++++++++++++++++++++
 trackers/urls.py        | 17 +++++++++++++----
 trackers/views.py       | 28 +++++++++++++++-------------
 5 files changed, 78 insertions(+), 20 deletions(-)

diff --git a/bh_core/settings.py b/bh_core/settings.py
index 1cf7328..67148b7 100644
--- a/bh_core/settings.py
+++ b/bh_core/settings.py
@@ -142,3 +142,9 @@ USE_TZ = True
 # https://docs.djangoproject.com/en/2.0/howto/static-files/
 
 STATIC_URL = '/static/'
+
+REST_FRAMEWORK = {
+    'DEFAULT_PERMISSION_CLASSES': [
+        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
+    ]
+}
diff --git a/trackers/models.py b/trackers/models.py
index 6ddd882..7baab71 100644
--- a/trackers/models.py
+++ b/trackers/models.py
@@ -35,11 +35,12 @@ class ModelCommon(models.Model):
 
 
 class Ticket(ModelCommon):
-    title = models.CharField(max_length=200, null=True)
-    description = models.TextField(null=True)
 
     def api_url(self):
-        return reverse('ticket_view', args=(self.id,))
+        return reverse('ticket-detail', args=(self.id,))
+
+    def api_events_url(self):
+        return reverse('changeevent-list', args=(self.id,))
 
     def last_update(self):
         last_event = self.changeevent_set.order_by('created').last()
@@ -67,6 +68,9 @@ class Ticket(ModelCommon):
 class TicketField(ModelCommon):
     name = models.CharField(max_length=32)
 
+    def api_url(self):
+        return reverse('ticketfield-detail', args=(self.id,))
+
 
 class ChangeEvent(ModelCommon):
     ticket = models.ForeignKey(Ticket, models.CASCADE, null=False)
@@ -81,3 +85,9 @@ class ChangeEvent(ModelCommon):
     def __str__(self):
         return "Change to: {}; Field: {}; Diff: {}".format(
             self.ticket, self.field, self.diff)
+
+    def api_url(self):
+        return reverse('changeevent-detail', args=(self.ticket.id, self.id,))
+
+    def api_ticket_url(self):
+        return reverse('ticket-detail', args=(self.ticket.id,))
diff --git a/trackers/serializers.py b/trackers/serializers.py
index b38dcdb..2616ff1 100644
--- a/trackers/serializers.py
+++ b/trackers/serializers.py
@@ -1,9 +1,23 @@
+from django.contrib.auth.models import User, Group
 from rest_framework import serializers
 from trackers import models
 
 
+class UserSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = User
+        fields = ('url', 'username', 'email', 'is_staff')
+
+
+class GroupSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = Group
+        fields = ('url', 'name')
+
+
 class TicketSerializer(serializers.ModelSerializer):
     api_url = serializers.SerializerMethodField()
+    api_events_url = serializers.SerializerMethodField()
 
     class Meta:
         model = models.Ticket
@@ -12,14 +26,31 @@ class TicketSerializer(serializers.ModelSerializer):
     def get_api_url(self, obj):
         return self.context['request'].build_absolute_uri(obj.api_url())
 
+    def get_api_events_url(self, obj):
+        return self.context['request'].build_absolute_uri(obj.api_events_url())
+
 
 class TicketFieldSerializer(serializers.ModelSerializer):
+    api_url = serializers.SerializerMethodField()
+
     class Meta:
         model = models.TicketField
         fields = '__all__'
 
+    def get_api_url(self, obj):
+        return self.context['request'].build_absolute_uri(obj.api_url())
+
 
 class ChangeEventSerializer(serializers.ModelSerializer):
+    api_url = serializers.SerializerMethodField()
+    api_ticket_url = serializers.SerializerMethodField()
+
     class Meta:
         model = models.ChangeEvent
         fields = '__all__'
+
+    def get_api_url(self, obj):
+        return self.context['request'].build_absolute_uri(obj.api_url())
+
+    def get_api_ticket_url(self, obj):
+        return self.context['request'].build_absolute_uri(obj.api_ticket_url())
diff --git a/trackers/urls.py b/trackers/urls.py
index d211bb4..bebc31c 100644
--- a/trackers/urls.py
+++ b/trackers/urls.py
@@ -16,13 +16,22 @@
 #  under the License.
 
 from django.urls import path
+from django.conf.urls import include
+from rest_framework import routers
 from . import views
 
+router = routers.DefaultRouter()
+router.register('users', views.UserViewSet)
+router.register('groups', views.GroupViewSet)
+router.register('tickets', views.TicketViewSet)
+router.register('ticketfields', views.TicketFieldViewSet)
+
+ticket_router = routers.DefaultRouter()
+ticket_router.register('ticketevents', views.ChangeEventViewSet)
+
 urlpatterns = [
     path('', views.home, name='home'),
+    path('api/', include(router.urls)),
+    path('api/tickets/<uuid:id>/', include(ticket_router.urls)),
     path('schema_view/', views.schema_view),
-    path('field/', views.TicketFieldListCreate.as_view()),
-    path('ticket/', views.TicketListCreate.as_view()),
-    path('ticket/<uuid:id>', views.TicketViewUpdate.as_view(), name='ticket_view'),
-    path('ticket/<uuid:id>/event/', views.ChangeEventListCreate.as_view()),
 ]
diff --git a/trackers/views.py b/trackers/views.py
index 13498ed..03559b8 100644
--- a/trackers/views.py
+++ b/trackers/views.py
@@ -15,9 +15,9 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
+from django.contrib.auth.models import User, Group
 from django.http import HttpResponse
-from django.shortcuts import render
-from rest_framework import generics
+from rest_framework import viewsets
 from . import serializers
 from . import models
 
@@ -30,24 +30,26 @@ def home(request):
     return HttpResponse('<html><title>Bloodhound Trackers</title></html>')
 
 
-class TicketListCreate(generics.ListCreateAPIView):
-    queryset = models.Ticket.objects.all()
-    serializer_class = serializers.TicketSerializer
+class UserViewSet(viewsets.ModelViewSet):
+    queryset = User.objects.all()
+    serializer_class = serializers.UserSerializer
 
 
-class TicketViewUpdate(generics.RetrieveUpdateAPIView):
-    queryset = models.Ticket.objects.all()
-    serializer_class = serializers.TicketSerializer
-    lookup_field = 'id'
+class GroupViewSet(viewsets.ModelViewSet):
+    queryset = Group.objects.all()
+    serializer_class = serializers.GroupSerializer
 
 
-class TicketFieldListCreate(generics.ListCreateAPIView):
+class TicketFieldViewSet(viewsets.ModelViewSet):
     queryset = models.TicketField.objects.all()
     serializer_class = serializers.TicketFieldSerializer
-    lookup_field = 'ticket'
 
 
-class ChangeEventListCreate(generics.ListCreateAPIView):
+class TicketViewSet(viewsets.ModelViewSet):
+    queryset = models.Ticket.objects.all()
+    serializer_class = serializers.TicketSerializer
+
+
+class ChangeEventViewSet(viewsets.ModelViewSet):
     queryset = models.ChangeEvent.objects.all()
     serializer_class = serializers.ChangeEventSerializer
-    lookup_field = 'ticket'


[bloodhound-core] 10/14: Update to README for some more useful information

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit c862c4ae7eb3984044c9f87b9417a23a66132ab2
Author: Gary Martin <gj...@apache.org>
AuthorDate: Tue Dec 18 01:22:19 2018 +0000

    Update to README for some more useful information
    
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1849138 13f79535-47bb-0310-9956-ffa450edef68
---
 README.md | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/README.md b/README.md
index b159963..d3f6549 100644
--- a/README.md
+++ b/README.md
@@ -83,6 +83,22 @@ models gain stability.
 pipenv run python manage.py runserver
 ```
 
+Amongst the initial output of that command will be something like:
+
+```
+Starting development server at http://127.0.0.1:8000/
+Quit the server with CONTROL-C.
+```
+
+Currently there is not much to see at the specified location. More work has
+been done on the core API. The following views may be of interest as you
+explore:
+
+ * http://127.0.0.1:8000/ticket/
+ * http://127.0.0.1:8000/schema_view/
+
+These paths are subject to change.
+
 ## Unit Tests
 
 Unit tests are currently being written with the standard unittest framework.


[bloodhound-core] 11/14: Remove python version dependency

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit ae3966a937e5b90cbdb5b60998d6590d53d71c43
Author: Gary Martin <gj...@apache.org>
AuthorDate: Tue Feb 19 13:28:43 2019 +0000

    Remove python version dependency
    
     * at the moment setting a python version is being slightly
       counterproductive as we are really developing for a range
     * additionally updated the Pipfile.lock
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1853878 13f79535-47bb-0310-9956-ffa450edef68
---
 Pipfile      |  2 ++
 Pipfile.lock | 50 +++++++++++++++++++++++++-------------------------
 2 files changed, 27 insertions(+), 25 deletions(-)

diff --git a/Pipfile b/Pipfile
index 105e3db..114750c 100644
--- a/Pipfile
+++ b/Pipfile
@@ -12,3 +12,5 @@ PyYAML = "*"
 django = ">=2.0.0"
 djangorestframework = "*"
 django-rest-swagger = "*"
+
+[requires]
diff --git a/Pipfile.lock b/Pipfile.lock
index c4f4c8d..4929a85 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -44,11 +44,11 @@
         },
         "django": {
             "hashes": [
-                "sha256:068d51054083d06ceb32ce02b7203f1854256047a0d58682677dd4f81bceabd7",
-                "sha256:55409a056b27e6d1246f19ede41c6c610e4cab549c005b62cbeefabc6433356b"
+                "sha256:275bec66fd2588dd517ada59b8bfb23d4a9abc5a362349139ddda3c7ff6f5ade",
+                "sha256:939652e9d34d7d53d74d5d8ef82a19e5f8bb2de75618f7e5360691b6e9667963"
             ],
             "index": "pypi",
-            "version": "==2.1.4"
+            "version": "==2.1.7"
         },
         "django-rest-swagger": {
             "hashes": [
@@ -60,11 +60,11 @@
         },
         "djangorestframework": {
             "hashes": [
-                "sha256:607865b0bb1598b153793892101d881466bd5a991de12bd6229abb18b1c86136",
-                "sha256:63f76cbe1e7d12b94c357d7e54401103b2e52aef0f7c1650d6c820ad708776e5"
+                "sha256:79c6efbb2514bc50cf25906d7c0a5cfead714c7af667ff4bd110312cd380ae66",
+                "sha256:a4138613b67e3a223be6c97f53b13d759c5b90d2b433bad670b8ebf95402075f"
             ],
             "index": "pypi",
-            "version": "==3.9.0"
+            "version": "==3.9.1"
         },
         "idna": {
             "hashes": [
@@ -127,10 +127,10 @@
         },
         "pytz": {
             "hashes": [
-                "sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca",
-                "sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6"
+                "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9",
+                "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"
             ],
-            "version": "==2018.7"
+            "version": "==2018.9"
         },
         "requests": {
             "hashes": [
@@ -175,10 +175,10 @@
     "develop": {
         "atomicwrites": {
             "hashes": [
-                "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0",
-                "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"
+                "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
+                "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
             ],
-            "version": "==1.2.1"
+            "version": "==1.3.0"
         },
         "attrs": {
             "hashes": [
@@ -189,18 +189,18 @@
         },
         "more-itertools": {
             "hashes": [
-                "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092",
-                "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e",
-                "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"
+                "sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40",
+                "sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1"
             ],
-            "version": "==4.3.0"
+            "markers": "python_version > '2.7'",
+            "version": "==6.0.0"
         },
         "pluggy": {
             "hashes": [
-                "sha256:447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095",
-                "sha256:bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f"
+                "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616",
+                "sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a"
             ],
-            "version": "==0.8.0"
+            "version": "==0.8.1"
         },
         "py": {
             "hashes": [
@@ -211,18 +211,18 @@
         },
         "pytest": {
             "hashes": [
-                "sha256:f689bf2fc18c4585403348dd56f47d87780bf217c53ed9ae7a3e2d7faa45f8e9",
-                "sha256:f812ea39a0153566be53d88f8de94839db1e8a05352ed8a49525d7d7f37861e9"
+                "sha256:80cfd9c8b9e93f419abcc0400e9f595974a98e44b6863a77d3e1039961bfc9c4",
+                "sha256:c2396a15726218a2dfef480861c4ba37bd3952ebaaa5b0fede3fc23fddcd7f8c"
             ],
-            "version": "==4.0.2"
+            "version": "==4.2.1"
         },
         "pytest-django": {
             "hashes": [
-                "sha256:deffd9d65827c582bd0a85638a0fe52f0eb65a764872ddcee9ce51cdf6ae9f55",
-                "sha256:fe1f71a0171f6b7edac37654da0904c9bd5ffba5221ab5a76779ab870611f41f"
+                "sha256:3d489db7c9bd18d7c154347b1bdfb82cc6b1ec8539543508b199c77e5eb2caec",
+                "sha256:87c31e53ad09ca4f061b82a9d71ad1e3e399c7a5ec9d28f7c3c38a9a9afbd027"
             ],
             "index": "pypi",
-            "version": "==3.4.4"
+            "version": "==3.4.7"
         },
         "pyyaml": {
             "hashes": [


[bloodhound-core] 03/14: Extract common model enforcing id & created fields

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit 367f2190b9ca1e79cb8bdb707b0a473446ce7250
Author: Gary Martin <gj...@apache.org>
AuthorDate: Wed Jun 6 15:19:29 2018 +0000

    Extract common model enforcing id & created fields
    
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1833042 13f79535-47bb-0310-9956-ffa450edef68
---
 trackers/models.py | 27 +++++++++++----------------
 1 file changed, 11 insertions(+), 16 deletions(-)

diff --git a/trackers/models.py b/trackers/models.py
index 1887e4e..2a2513c 100644
--- a/trackers/models.py
+++ b/trackers/models.py
@@ -24,14 +24,18 @@ from django.db import models
 
 logger = logging.getLogger(__name__)
 
-
-class Ticket(models.Model):
+class ModelCommon(models.Model):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
     created = models.DateTimeField(auto_now_add=True, editable=False)
 
+    class Meta:
+        abstract = True
+
+class Ticket(ModelCommon):
+
     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
+        last_event = self.changeevent_set.order_by('created').last()
+        return self.created if last_event is None else last_event.created
 
     def add_field_event(self, field, newvalue):
         current_lines = self.get_field_value(field).splitlines(keepends=True)
@@ -48,25 +52,16 @@ class Ticket(models.Model):
         except TicketField.DoesNotExist as e:
             return ''
 
-        event = self.changeevent_set.filter(field=tfield).order_by('event_time').last()
+        event = self.changeevent_set.filter(field=tfield).order_by('created').last()
         return '' if event is None else event.value()
 
 
-class TicketField(models.Model):
-    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
+class TicketField(ModelCommon):
     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)
+class ChangeEvent(ModelCommon):
     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):


[bloodhound-core] 04/14: Add djangorestframework and register admin pages

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit d3dc9d90cd70508e7c6898ae69b572a04aab47ef
Author: Gary Martin <gj...@apache.org>
AuthorDate: Sun Oct 14 00:21:39 2018 +0000

    Add djangorestframework and register admin pages
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1843801 13f79535-47bb-0310-9956-ffa450edef68
---
 Pipfile                 |   1 +
 Pipfile.lock            | 107 ++++++++++++++++++++++++++----------------------
 bh_core/urls.py         |   2 +-
 trackers/admin.py       |   4 ++
 trackers/models.py      |   2 +
 trackers/serializers.py |   8 ++++
 trackers/views.py       |  10 +++++
 7 files changed, 85 insertions(+), 49 deletions(-)

diff --git a/Pipfile b/Pipfile
index 33c6799..056c071 100644
--- a/Pipfile
+++ b/Pipfile
@@ -10,6 +10,7 @@ PyYAML = "*"
 
 [packages]
 django = ">=2.0.0"
+djangorestframework = "*"
 
 [requires]
 python_version = "3.6"
diff --git a/Pipfile.lock b/Pipfile.lock
index 1f1f2aa..7d244b5 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "5ba6eeb95fa58896eba1e3528bdea3ae2430c5424239bef1345adc500dc53fcd"
+            "sha256": "f70363525f7fd7a95075f66854bc135a3c1d45142d1e02ebe126442621614be9"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -18,100 +18,104 @@
     "default": {
         "django": {
             "hashes": [
-                "sha256:3eb25c99df1523446ec2dc1b00e25eb2ecbdf42c9d8b0b8b32a204a8db9011f8",
-                "sha256:69ff89fa3c3a8337015478a1a0744f52a9fef5d12c1efa01a01f99bcce9bf10c"
+                "sha256:acdcc1f61fdb0a0c82a1d3bf1879a414e7732ea894a7632af7f6d66ec7ab5bb3",
+                "sha256:efbcad7ebb47daafbcead109b38a5bd519a3c3cd92c6ed0f691ff97fcdd16b45"
             ],
             "index": "pypi",
-            "version": "==2.0.6"
+            "version": "==2.1.2"
+        },
+        "djangorestframework": {
+            "hashes": [
+                "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9",
+                "sha256:c375e4f95a3a64fccac412e36fb42ba36881e52313ec021ef410b40f67cddca4"
+            ],
+            "index": "pypi",
+            "version": "==3.8.2"
         },
         "pytz": {
             "hashes": [
-                "sha256:65ae0c8101309c45772196b21b74c46b2e5d11b6275c45d251b150d5da334555",
-                "sha256:c06425302f2cf668f1bba7a0a03f3c1d34d4ebeef2c72003da308b3947c7f749"
+                "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053",
+                "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277"
             ],
-            "version": "==2018.4"
+            "version": "==2018.5"
         }
     },
     "develop": {
         "atomicwrites": {
             "hashes": [
-                "sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585",
-                "sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6"
+                "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0",
+                "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"
             ],
-            "version": "==1.1.5"
+            "version": "==1.2.1"
         },
         "attrs": {
             "hashes": [
-                "sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265",
-                "sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b"
+                "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
+                "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
             ],
-            "version": "==18.1.0"
+            "version": "==18.2.0"
         },
         "more-itertools": {
             "hashes": [
-                "sha256:2b6b9893337bfd9166bee6a62c2b0c9fe7735dcf85948b387ec8cba30e85d8e8",
-                "sha256:6703844a52d3588f951883005efcf555e49566a48afd4db4e965d69b883980d3",
-                "sha256:a18d870ef2ffca2b8463c0070ad17b5978056f403fb64e3f15fe62a52db21cc0"
+                "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092",
+                "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e",
+                "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"
             ],
-            "version": "==4.2.0"
+            "version": "==4.3.0"
         },
         "pluggy": {
             "hashes": [
-                "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff",
-                "sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c",
-                "sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5"
+                "sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1",
+                "sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1"
             ],
-            "version": "==0.6.0"
+            "version": "==0.7.1"
         },
         "py": {
             "hashes": [
-                "sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881",
-                "sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a"
+                "sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694",
+                "sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"
             ],
-            "version": "==1.5.3"
+            "version": "==1.7.0"
         },
         "pytest": {
             "hashes": [
-                "sha256:26838b2bc58620e01675485491504c3aa7ee0faf335c37fcd5f8731ca4319591",
-                "sha256:32c49a69566aa7c333188149ad48b58ac11a426d5352ea3d8f6ce843f88199cb"
+                "sha256:7e258ee50338f4e46957f9e09a0f10fb1c2d05493fa901d113a8dafd0790de4e",
+                "sha256:9332147e9af2dcf46cd7ceb14d5acadb6564744ddff1fe8c17f0ce60ece7d9a2"
             ],
-            "version": "==3.6.1"
+            "version": "==3.8.2"
         },
         "pytest-django": {
             "hashes": [
-                "sha256:534505e0261cc566279032d9d887f844235342806fd63a6925689670fa1b29d7",
-                "sha256:7501942093db2250a32a4e36826edfc542347bb9b26c78ed0649cdcfd49e5789"
+                "sha256:49e9ffc856bc6a1bec1c26c5c7b7213dff7cc8bc6b64d624c4d143d04aff0bcf",
+                "sha256:b379282feaf89069cb790775ab6bbbd2bd2038a68c7ef9b84a41898e0b551081"
             ],
             "index": "pypi",
-            "version": "==3.2.1"
+            "version": "==3.4.3"
         },
         "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"
+                "sha256:3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b",
+                "sha256:3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf",
+                "sha256:40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a",
+                "sha256:558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3",
+                "sha256:a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1",
+                "sha256:aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1",
+                "sha256:bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613",
+                "sha256:d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04",
+                "sha256:d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f",
+                "sha256:e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537",
+                "sha256:e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531"
             ],
             "index": "pypi",
-            "version": "==3.12"
+            "version": "==3.13"
         },
         "selenium": {
             "hashes": [
-                "sha256:1372101ad23798462038481f92ba1c7fab8385c788b05da6b44318f10ea52422",
-                "sha256:b8a2630fd858636c894960726ca3c94d8277e516ea3a9d81614fb819a5844764"
+                "sha256:ab192cd046164c40fabcf44b47c66c8b12495142f4a69dcc55ea6eeef096e614",
+                "sha256:fdb6b1143d8899e8a32e358ad05bf5d89a480dbac359dbbd341592aa8696dcd1"
             ],
             "index": "pypi",
-            "version": "==3.12.0"
+            "version": "==3.14.1"
         },
         "six": {
             "hashes": [
@@ -119,6 +123,13 @@
                 "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
             ],
             "version": "==1.11.0"
+        },
+        "urllib3": {
+            "hashes": [
+                "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
+                "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
+            ],
+            "version": "==1.23"
         }
     }
 }
diff --git a/bh_core/urls.py b/bh_core/urls.py
index 9bf7a1c..408612b 100644
--- a/bh_core/urls.py
+++ b/bh_core/urls.py
@@ -36,5 +36,5 @@ from django.urls import include, path
 
 urlpatterns = [
     path('', include('trackers.urls')),
-    # path('admin/', admin.site.urls),
+    path('admin/', admin.site.urls),
 ]
diff --git a/trackers/admin.py b/trackers/admin.py
index e1f3d8f..2a4a378 100644
--- a/trackers/admin.py
+++ b/trackers/admin.py
@@ -19,3 +19,7 @@
 from django.contrib import admin
 
 # Register your models here.
+from trackers.models import Ticket, ChangeEvent
+
+admin.site.register(Ticket)
+admin.site.register(ChangeEvent)
diff --git a/trackers/models.py b/trackers/models.py
index 2a2513c..6637f17 100644
--- a/trackers/models.py
+++ b/trackers/models.py
@@ -32,6 +32,8 @@ class ModelCommon(models.Model):
         abstract = True
 
 class Ticket(ModelCommon):
+    title = models.CharField(max_length=200, null=True)
+    description = models.TextField(null=True)
 
     def last_update(self):
         last_event = self.changeevent_set.order_by('created').last()
diff --git a/trackers/serializers.py b/trackers/serializers.py
new file mode 100644
index 0000000..37c14f6
--- /dev/null
+++ b/trackers/serializers.py
@@ -0,0 +1,8 @@
+from rest_framework import serializers
+from trackers.models import Ticket
+
+
+class TicketSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Ticket
+        fields = '__all__'
diff --git a/trackers/views.py b/trackers/views.py
index 6e21c4f..3483c9f 100644
--- a/trackers/views.py
+++ b/trackers/views.py
@@ -17,6 +17,16 @@
 
 from django.http import HttpResponse
 from django.shortcuts import render
+from rest_framework import generics
 
 def home(request):
     return HttpResponse('<html><title>Bloodhound Trackers</title></html>')
+
+
+from trackers.serializers import TicketSerializer
+from trackers.models import Ticket
+
+
+class PlanetList(generics.ListCreateAPIView):
+    queryset = Ticket.objects.all()
+    serializer_class = TicketSerializer


[bloodhound-core] 08/14: Attempt to add links to api output for ticket list

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit 5ff4a05a6b64fb7c730eab2df44a3ceb35b39a4a
Author: Gary Martin <gj...@apache.org>
AuthorDate: Sun Oct 14 00:23:31 2018 +0000

    Attempt to add links to api output for ticket list
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1843805 13f79535-47bb-0310-9956-ffa450edef68
---
 trackers/models.py      |  4 ++++
 trackers/serializers.py | 13 +++++++++----
 trackers/urls.py        |  2 +-
 3 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/trackers/models.py b/trackers/models.py
index d819fd4..6ddd882 100644
--- a/trackers/models.py
+++ b/trackers/models.py
@@ -21,6 +21,7 @@ import logging
 import uuid
 
 from django.db import models
+from django.urls import reverse
 
 logger = logging.getLogger(__name__)
 
@@ -37,6 +38,9 @@ class Ticket(ModelCommon):
     title = models.CharField(max_length=200, null=True)
     description = models.TextField(null=True)
 
+    def api_url(self):
+        return reverse('ticket_view', args=(self.id,))
+
     def last_update(self):
         last_event = self.changeevent_set.order_by('created').last()
         return self.created if last_event is None else last_event.created
diff --git a/trackers/serializers.py b/trackers/serializers.py
index 5416191..b38dcdb 100644
--- a/trackers/serializers.py
+++ b/trackers/serializers.py
@@ -1,20 +1,25 @@
 from rest_framework import serializers
-from trackers.models import Ticket, TicketField, ChangeEvent
+from trackers import models
 
 
 class TicketSerializer(serializers.ModelSerializer):
+    api_url = serializers.SerializerMethodField()
+
     class Meta:
-        model = Ticket
+        model = models.Ticket
         fields = '__all__'
 
+    def get_api_url(self, obj):
+        return self.context['request'].build_absolute_uri(obj.api_url())
+
 
 class TicketFieldSerializer(serializers.ModelSerializer):
     class Meta:
-        model = TicketField
+        model = models.TicketField
         fields = '__all__'
 
 
 class ChangeEventSerializer(serializers.ModelSerializer):
     class Meta:
-        model = ChangeEvent
+        model = models.ChangeEvent
         fields = '__all__'
diff --git a/trackers/urls.py b/trackers/urls.py
index 20b115b..d211bb4 100644
--- a/trackers/urls.py
+++ b/trackers/urls.py
@@ -23,6 +23,6 @@ urlpatterns = [
     path('schema_view/', views.schema_view),
     path('field/', views.TicketFieldListCreate.as_view()),
     path('ticket/', views.TicketListCreate.as_view()),
-    path('ticket/<uuid:id>', views.TicketViewUpdate.as_view()),
+    path('ticket/<uuid:id>', views.TicketViewUpdate.as_view(), name='ticket_view'),
     path('ticket/<uuid:id>/event/', views.ChangeEventListCreate.as_view()),
 ]


[bloodhound-core] 01/14: add branch for new bh_core experiment

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit 0394b68f7b4bd03e97d4911d358339043fbd67b2
Author: Gary Martin <gj...@apache.org>
AuthorDate: Mon Jun 4 13:50:34 2018 +0000

    add branch for new bh_core experiment
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1832850 13f79535-47bb-0310-9956-ffa450edef68
---
 Pipfile                     |  16 +++++
 Pipfile.lock                | 144 ++++++++++++++++++++++++++++++++++++++++++++
 README.md                   |  91 ++++++++++++++++++++++++++++
 bh_core/__init__.py         |  17 ++++++
 bh_core/settings.py         | 142 +++++++++++++++++++++++++++++++++++++++++++
 bh_core/urls.py             |  40 ++++++++++++
 bh_core/wsgi.py             |  33 ++++++++++
 functional_tests.py         |  39 ++++++++++++
 manage.py                   |  33 ++++++++++
 pytest.ini                  |   2 +
 trackers/__init__.py        |  16 +++++
 trackers/admin.py           |  21 +++++++
 trackers/apps.py            |  22 +++++++
 trackers/fixtures/empty.yml |   1 +
 trackers/models.py          |  80 ++++++++++++++++++++++++
 trackers/tests.py           |  93 ++++++++++++++++++++++++++++
 trackers/urls.py            |  23 +++++++
 trackers/views.py           |  22 +++++++
 18 files changed, 835 insertions(+)

diff --git a/Pipfile b/Pipfile
new file mode 100644
index 0000000..e912a7b
--- /dev/null
+++ b/Pipfile
@@ -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"
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 0000000..4836a5b
--- /dev/null
+++ b/Pipfile.lock
@@ -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"
+        }
+    }
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fd17d1d
--- /dev/null
+++ b/README.md
@@ -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.
diff --git a/bh_core/__init__.py b/bh_core/__init__.py
new file mode 100644
index 0000000..534df97
--- /dev/null
+++ b/bh_core/__init__.py
@@ -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.
+
diff --git a/bh_core/settings.py b/bh_core/settings.py
new file mode 100644
index 0000000..579b660
--- /dev/null
+++ b/bh_core/settings.py
@@ -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/'
diff --git a/bh_core/urls.py b/bh_core/urls.py
new file mode 100644
index 0000000..9bf7a1c
--- /dev/null
+++ b/bh_core/urls.py
@@ -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),
+]
diff --git a/bh_core/wsgi.py b/bh_core/wsgi.py
new file mode 100644
index 0000000..15905c2
--- /dev/null
+++ b/bh_core/wsgi.py
@@ -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()
diff --git a/functional_tests.py b/functional_tests.py
new file mode 100644
index 0000000..df49437
--- /dev/null
+++ b/functional_tests.py
@@ -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')
diff --git a/manage.py b/manage.py
new file mode 100755
index 0000000..63afb12
--- /dev/null
+++ b/manage.py
@@ -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)
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 0000000..7cf71fa
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,2 @@
+[pytest]
+DJANGO_SETTINGS_MODULE = bh_core.settings
diff --git a/trackers/__init__.py b/trackers/__init__.py
new file mode 100644
index 0000000..084b296
--- /dev/null
+++ b/trackers/__init__.py
@@ -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.
diff --git a/trackers/admin.py b/trackers/admin.py
new file mode 100644
index 0000000..e1f3d8f
--- /dev/null
+++ b/trackers/admin.py
@@ -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.
diff --git a/trackers/apps.py b/trackers/apps.py
new file mode 100644
index 0000000..7b9d013
--- /dev/null
+++ b/trackers/apps.py
@@ -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'
diff --git a/trackers/fixtures/empty.yml b/trackers/fixtures/empty.yml
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/trackers/fixtures/empty.yml
@@ -0,0 +1 @@
+[]
diff --git a/trackers/models.py b/trackers/models.py
new file mode 100644
index 0000000..1887e4e
--- /dev/null
+++ b/trackers/models.py
@@ -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)
+
diff --git a/trackers/tests.py b/trackers/tests.py
new file mode 100644
index 0000000..cbc666b
--- /dev/null
+++ b/trackers/tests.py
@@ -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)
+
diff --git a/trackers/urls.py b/trackers/urls.py
new file mode 100644
index 0000000..a7a1c75
--- /dev/null
+++ b/trackers/urls.py
@@ -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'),
+]
diff --git a/trackers/views.py b/trackers/views.py
new file mode 100644
index 0000000..6e21c4f
--- /dev/null
+++ b/trackers/views.py
@@ -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>')


[bloodhound-core] 13/14: Updates to setup advice

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit d5f0a61e2836ce8b1c8aa6b7bce134264ae8c003
Author: Gary Martin <gj...@apache.org>
AuthorDate: Tue Jan 14 02:06:28 2020 +0000

    Updates to setup advice
---
 README.md | 112 ++++++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 65 insertions(+), 47 deletions(-)

diff --git a/README.md b/README.md
index d3f6549..f0b6e39 100644
--- a/README.md
+++ b/README.md
@@ -1,61 +1,67 @@
 # New Bloodhound
 
-## Requirements
+## Getting the Bloodhound Code:
 
-### Python
+There is a reasonable chance that you are reading these instructions from a
+copy of the source code that you have already placed on the computer that you
+wish to install on. If this is the case you can skip on to the next section.
 
-Bloodhound core is currently written in Python3. It should be possible to
-install and run the core successfully with Python 3.6 or newer. You may
-find that versions from Python 3.4 work but this is not currently tested
-and it is possible that Python features from newer versions may sneak in.
+While in early development, the alternatives for getting the code include
+checking out from [Subversion] with the following command:
 
-If you do not already have an appropriate version of Python installed, you
-may wish to follow instructions for your platform here:
+```
+svn checkout https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental/ bloodhound
+```
 
-  https://docs.python-guide.org/starting/installation/
+or cloning with [Git] from the [Apache Bloodhound Github mirror] - the command
+below should also check out the appropriate branch:
 
-### Pipenv
+```
+git clone --branch bh_core_experimental https://github.com/apache/bloodhound.git
+```
 
-Pipenv is used for looking after Python package dependencies and virtual
-environment management.
+With the commands as specified, both will place the code in the `bloodhound`
+directory.
 
-If you already have the `pip` program installed already, installation of
-pipenv can be as simple as
+[Subversion]: https://subversion.apache.org/
+[Git]: https://git-scm.com/
+[Apache Bloodhound mirror]: https://github.com/apache/bloodhound
+[Github]: https://github.com/
 
-```
-pip install --user pipenv
-```
+## Installing Python and Pipenv
 
-For more information on installing and usage of pipenv, see
-https://docs.pipenv.org/.
+Bloodhound core is currently written in [Python 3] and uses [Pipenv] for
+looking after the python based dependencies.
 
-Once pipenv is installed, the final bit of setup ahead of installing the
-rest of the project dependencies is to ensure that you have picked out the
-appropriate version of Python for your environment. For the same directory
-as the `Pipfile` for the project, you should run:
+It should be possible to install and run the core successfully with Python 3.6
+or newer. You may find that versions from Python 3.4 work but this is not
+currently tested and it is possible that Python features from newer versions
+may sneak in.
 
-```
-pipenv --python 3
-```
+The guide at <https://docs.python-guide.org/> gives good instructions for
+installing Python on [Linux][Python on Linux], [MacOS][Python on MacOS] and
+[Windows][Python on Windows].
 
-If you have multiple versions of Python 3 installed, you may need to be
-more specific about the version.
+Further information about pipenv is available at <https://docs.pipenv.org/>.
 
-### Pipfile Specified Requirements
+[Python 3]: https://docs.python.org/3/
+[Pipenv]: https://pipenv.readthedocs.io/en/latest/
+[Python on Linux]: https://docs.python-guide.org/starting/install3/linux/#install3-linux
+[Python on MacOS]: https://docs.python-guide.org/starting/install3/osx/#install3-osx
+[Python on Windows]: https://docs.python-guide.org/starting/install3/win/#install3-windows
 
-With pipenv installed and the Python version selected, the rest of the
-Python based requrements can be installed with the following command from
-the same director as the `Pipfile` for the project:
+## Preparing the Python environment
 
-```
-pipenv install
-```
+It should now be possible to use pipenv to install the rest of the project
+dependencies and bloodhound itself. Note that the exactly required command may
+depend on details like whether you have multiple versions of python available
+but for most cases, the following should work. If in doubt, just be more
+specific about the python version that you intend to use.
 
-Additionally, to run tests described later, you should install the
-development dependencies:
+For the same directory as the `Pipfile` for the project run:
 
 ```
-pipenv install --dev
+pipenv --python 3 install
 ```
 
 ## Setup
@@ -104,27 +110,28 @@ These paths are subject to change.
 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:
+Running the tests require a little extra setup:
 
 ```
-pipenv run python manage.py test
+pipenv install --dev
 ```
 
-Fixtures for tests when required can be generated with:
+after which the tests may be run with the following command:
 
 ```
-pipenv run python manage.py dumpdata trackers --format=yaml --indent=2 > trackers/fixtures/[fixture-name].yaml
+pipenv run python manage.py test
 ```
 
 ## 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):
-
+The [Selenium] tests currently require that Firefox is installed and
+[geckodriver] is also on the path. If you 
 ```
-BIN_LOCATION="$HOME/.local/bin"
 PLATFORM_EXT="linux64.tar.gz"
-TMP_DIR=/tmp
+BIN_LOCATION="$HOME/.local/bin"
+TMP_DIR=/tmp/geckodriver_download
+mkdir -p "$BIN_LOCATION" "$TMP_DIR"
+
 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
@@ -140,3 +147,14 @@ 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.
+
+[Selenium]: https://selenium.dev/
+[geckodriver]: https://firefox-source-docs.mozilla.org/testing/geckodriver/
+
+## Development notes:
+
+Fixtures for tests when required can be generated with:
+
+```
+pipenv run python manage.py dumpdata trackers --format=yaml --indent=2 > trackers/fixtures/[fixture-name].yaml
+```


[bloodhound-core] 09/14: Remove strict Python requirement; update README

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit a9d5a3c6db80fae178a50d105fa5a4e7d1aeff7f
Author: Gary Martin <gj...@apache.org>
AuthorDate: Tue Dec 18 01:12:17 2018 +0000

    Remove strict Python requirement; update README
    
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1849137 13f79535-47bb-0310-9956-ffa450edef68
---
 Pipfile      |   3 --
 Pipfile.lock | 126 +++++++++++++++++++++++++++++++++--------------------------
 README.md    |  51 ++++++++++++++++++------
 3 files changed, 111 insertions(+), 69 deletions(-)

diff --git a/Pipfile b/Pipfile
index fe4fd91..105e3db 100644
--- a/Pipfile
+++ b/Pipfile
@@ -12,6 +12,3 @@ PyYAML = "*"
 django = ">=2.0.0"
 djangorestframework = "*"
 django-rest-swagger = "*"
-
-[requires]
-python_version = "3.6"
diff --git a/Pipfile.lock b/Pipfile.lock
index c681e5e..c4f4c8d 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,12 +1,10 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "9d8b5c33b68a3c95ceadb2fa4e343ea79a7739d9711887ae9562f774b830d49d"
+            "sha256": "32e23ea6222c95881c6e0ea71ead44b51d710f950c012a4c125dec779388bf69"
         },
         "pipfile-spec": 6,
-        "requires": {
-            "python_version": "3.6"
-        },
+        "requires": {},
         "sources": [
             {
                 "name": "pypi",
@@ -18,10 +16,10 @@
     "default": {
         "certifi": {
             "hashes": [
-                "sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638",
-                "sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a"
+                "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
+                "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
             ],
-            "version": "==2018.8.24"
+            "version": "==2018.11.29"
         },
         "chardet": {
             "hashes": [
@@ -46,11 +44,11 @@
         },
         "django": {
             "hashes": [
-                "sha256:acdcc1f61fdb0a0c82a1d3bf1879a414e7732ea894a7632af7f6d66ec7ab5bb3",
-                "sha256:efbcad7ebb47daafbcead109b38a5bd519a3c3cd92c6ed0f691ff97fcdd16b45"
+                "sha256:068d51054083d06ceb32ce02b7203f1854256047a0d58682677dd4f81bceabd7",
+                "sha256:55409a056b27e6d1246f19ede41c6c610e4cab549c005b62cbeefabc6433356b"
             ],
             "index": "pypi",
-            "version": "==2.1.2"
+            "version": "==2.1.4"
         },
         "django-rest-swagger": {
             "hashes": [
@@ -62,18 +60,18 @@
         },
         "djangorestframework": {
             "hashes": [
-                "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9",
-                "sha256:c375e4f95a3a64fccac412e36fb42ba36881e52313ec021ef410b40f67cddca4"
+                "sha256:607865b0bb1598b153793892101d881466bd5a991de12bd6229abb18b1c86136",
+                "sha256:63f76cbe1e7d12b94c357d7e54401103b2e52aef0f7c1650d6c820ad708776e5"
             ],
             "index": "pypi",
-            "version": "==3.8.2"
+            "version": "==3.9.0"
         },
         "idna": {
             "hashes": [
-                "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
-                "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
+                "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
+                "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
             ],
-            "version": "==2.7"
+            "version": "==2.8"
         },
         "itypes": {
             "hashes": [
@@ -90,9 +88,36 @@
         },
         "markupsafe": {
             "hashes": [
-                "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
+                "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
+                "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
+                "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
+                "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
+                "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
+                "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
+                "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
+                "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
+                "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
+                "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
+                "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
+                "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
+                "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
+                "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
+                "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
+                "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
+                "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
+                "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
+                "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
+                "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
+                "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
+                "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
+                "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
+                "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
+                "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
+                "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
+                "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
+                "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
             ],
-            "version": "==1.0"
+            "version": "==1.1.0"
         },
         "openapi-codec": {
             "hashes": [
@@ -102,41 +127,32 @@
         },
         "pytz": {
             "hashes": [
-                "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053",
-                "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277"
+                "sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca",
+                "sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6"
             ],
-            "version": "==2018.5"
+            "version": "==2018.7"
         },
         "requests": {
             "hashes": [
-                "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1",
-                "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a"
+                "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
+                "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
             ],
-            "version": "==2.19.1"
+            "version": "==2.21.0"
         },
         "simplejson": {
             "hashes": [
                 "sha256:067a7177ddfa32e1483ba5169ebea1bc2ea27f224853211ca669325648ca5642",
-                "sha256:2b8cb601d9ba0381499db719ccc9dfbb2fbd16013f5ff096b1a68a4775576a04",
-                "sha256:2c139daf167b96f21542248f8e0a06596c9b9a7a41c162cc5c9ee9f3833c93cd",
                 "sha256:2fc546e6af49fb45b93bbe878dea4c48edc34083729c0abd09981fe55bdf7f91",
                 "sha256:354fa32b02885e6dae925f1b5bbf842c333c1e11ea5453ddd67309dc31fdb40a",
                 "sha256:37e685986cf6f8144607f90340cff72d36acf654f3653a6c47b84c5c38d00df7",
                 "sha256:3af610ee72efbe644e19d5eaad575c73fb83026192114e5f6719f4901097fce2",
                 "sha256:3b919fc9cf508f13b929a9b274c40786036b31ad28657819b3b9ba44ba651f50",
                 "sha256:3dd289368bbd064974d9a5961101f080e939cbe051e6689a193c99fb6e9ac89b",
-                "sha256:491de7acc423e871a814500eb2dcea8aa66c4a4b1b4825d18f756cdf58e370cb",
-                "sha256:495511fe5f10ccf4e3ed4fc0c48318f533654db6c47ecbc970b4ed215c791968",
-                "sha256:65b41a5cda006cfa7c66eabbcf96aa704a6be2a5856095b9e2fd8c293bad2b46",
                 "sha256:6c3258ffff58712818a233b9737fe4be943d306c40cf63d14ddc82ba563f483a",
                 "sha256:75e3f0b12c28945c08f54350d91e624f8dd580ab74fd4f1bbea54bc6b0165610",
-                "sha256:79b129fe65fdf3765440f7a73edaffc89ae9e7885d4e2adafe6aa37913a00fbb",
                 "sha256:b1f329139ba647a9548aa05fb95d046b4a677643070dc2afc05fa2e975d09ca5",
-                "sha256:c206f47cbf9f32b573c9885f0ec813d2622976cf5effcf7e472344bc2e020ac1",
-                "sha256:d8e238f20bcf70063ee8691d4a72162bcec1f4c38f83c93e6851e72ad545dabb",
                 "sha256:ee9625fc8ee164902dfbb0ff932b26df112da9f871c32f0f9c1bcf20c350fe2a",
-                "sha256:fb2530b53c28f0d4d84990e945c2ebb470edb469d63e389bf02ff409012fe7c5",
-                "sha256:feadb95170e45f439455354904768608e356c5b174ca30b3d11b0e3f24b5c0df"
+                "sha256:fb2530b53c28f0d4d84990e945c2ebb470edb469d63e389bf02ff409012fe7c5"
             ],
             "version": "==3.16.0"
         },
@@ -150,10 +166,10 @@
         },
         "urllib3": {
             "hashes": [
-                "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
-                "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
+                "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
+                "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
             ],
-            "version": "==1.23"
+            "version": "==1.24.1"
         }
     },
     "develop": {
@@ -181,10 +197,10 @@
         },
         "pluggy": {
             "hashes": [
-                "sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1",
-                "sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1"
+                "sha256:447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095",
+                "sha256:bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f"
             ],
-            "version": "==0.7.1"
+            "version": "==0.8.0"
         },
         "py": {
             "hashes": [
@@ -195,18 +211,18 @@
         },
         "pytest": {
             "hashes": [
-                "sha256:7e258ee50338f4e46957f9e09a0f10fb1c2d05493fa901d113a8dafd0790de4e",
-                "sha256:9332147e9af2dcf46cd7ceb14d5acadb6564744ddff1fe8c17f0ce60ece7d9a2"
+                "sha256:f689bf2fc18c4585403348dd56f47d87780bf217c53ed9ae7a3e2d7faa45f8e9",
+                "sha256:f812ea39a0153566be53d88f8de94839db1e8a05352ed8a49525d7d7f37861e9"
             ],
-            "version": "==3.8.2"
+            "version": "==4.0.2"
         },
         "pytest-django": {
             "hashes": [
-                "sha256:49e9ffc856bc6a1bec1c26c5c7b7213dff7cc8bc6b64d624c4d143d04aff0bcf",
-                "sha256:b379282feaf89069cb790775ab6bbbd2bd2038a68c7ef9b84a41898e0b551081"
+                "sha256:deffd9d65827c582bd0a85638a0fe52f0eb65a764872ddcee9ce51cdf6ae9f55",
+                "sha256:fe1f71a0171f6b7edac37654da0904c9bd5ffba5221ab5a76779ab870611f41f"
             ],
             "index": "pypi",
-            "version": "==3.4.3"
+            "version": "==3.4.4"
         },
         "pyyaml": {
             "hashes": [
@@ -227,25 +243,25 @@
         },
         "selenium": {
             "hashes": [
-                "sha256:ab192cd046164c40fabcf44b47c66c8b12495142f4a69dcc55ea6eeef096e614",
-                "sha256:fdb6b1143d8899e8a32e358ad05bf5d89a480dbac359dbbd341592aa8696dcd1"
+                "sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c",
+                "sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d"
             ],
             "index": "pypi",
-            "version": "==3.14.1"
+            "version": "==3.141.0"
         },
         "six": {
             "hashes": [
-                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
-                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
+                "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
+                "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
             ],
-            "version": "==1.11.0"
+            "version": "==1.12.0"
         },
         "urllib3": {
             "hashes": [
-                "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
-                "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
+                "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
+                "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
             ],
-            "version": "==1.23"
+            "version": "==1.24.1"
         }
     }
 }
diff --git a/README.md b/README.md
index 9c30a36..b159963 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,25 @@
 
 ## Requirements
 
-Bloodhound uses pipenv for development process.
+### Python
 
-If you have pip installed already, installation can be a simple as
+Bloodhound core is currently written in Python3. It should be possible to
+install and run the core successfully with Python 3.6 or newer. You may
+find that versions from Python 3.4 work but this is not currently tested
+and it is possible that Python features from newer versions may sneak in.
+
+If you do not already have an appropriate version of Python installed, you
+may wish to follow instructions for your platform here:
+
+  https://docs.python-guide.org/starting/installation/
+
+### Pipenv
+
+Pipenv is used for looking after Python package dependencies and virtual
+environment management.
+
+If you already have the `pip` program installed already, installation of
+pipenv can be as simple as
 
 ```
 pip install --user pipenv
@@ -13,28 +29,41 @@ 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
+Once pipenv is installed, the final bit of setup ahead of installing the
+rest of the project dependencies is to ensure that you have picked out the
+appropriate version of Python for your environment. For the same directory
+as the `Pipfile` for the project, you should run:
 
 ```
-pipenv install
+pipenv --python 3
 ```
 
-If this doesn't work, it should be done from the same directory as the
-`Pipenv` file.
+If you have multiple versions of Python 3 installed, you may need to be
+more specific about the version.
+
+### Pipfile Specified Requirements
 
-Additionally, to run tests described later, you'll also need to install the
+With pipenv installed and the Python version selected, the rest of the
+Python based requrements can be installed with the following command from
+the same director as the `Pipfile` for the project:
+
+```
+pipenv install
+```
+
+Additionally, to run tests described later, you should install the
 development dependencies:
 
 ```
 pipenv install --dev
 ```
 
-Though possibly annoying, the commands in this file will assume the use of
-`pipenv` but not that the pipenv shell has been activated.
-
 ## Setup
 
+Although it will make the commands more verbose, where a command requires
+the pipenv environment that has been created, we will use the `pipenv run`
+command in preference to requiring that the environment is 'activated'.
+
 The basic setup steps to get running are:
 
 ```


[bloodhound-core] 02/14: Improving README.md accuracy; adjusting Pipfile

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit 3854c5dbf450bcb110fc1a0922b0d2567bf69999
Author: Gary Martin <gj...@apache.org>
AuthorDate: Tue Jun 5 21:26:50 2018 +0000

    Improving README.md accuracy; adjusting Pipfile
    
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1832975 13f79535-47bb-0310-9956-ffa450edef68
---
 Pipfile      |  1 -
 Pipfile.lock | 28 ++++------------------------
 README.md    | 14 ++++++++++----
 3 files changed, 14 insertions(+), 29 deletions(-)

diff --git a/Pipfile b/Pipfile
index e912a7b..33c6799 100644
--- a/Pipfile
+++ b/Pipfile
@@ -10,7 +10,6 @@ PyYAML = "*"
 
 [packages]
 django = ">=2.0.0"
-pyyaml = "*"
 
 [requires]
 python_version = "3.6"
diff --git a/Pipfile.lock b/Pipfile.lock
index 4836a5b..1f1f2aa 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "4e791c7fd7b1d7f8749e94bcf56e4f78fb6514b5cf0d3174fe26d91f92cb672d"
+            "sha256": "5ba6eeb95fa58896eba1e3528bdea3ae2430c5424239bef1345adc500dc53fcd"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -30,26 +30,6 @@
                 "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": {
@@ -92,10 +72,10 @@
         },
         "pytest": {
             "hashes": [
-                "sha256:39555d023af3200d004d09e51b4dd9fdd828baa863cded3fd6ba2f29f757ae2d",
-                "sha256:c76e93f3145a44812955e8d46cdd302d8a45fbfc7bf22be24fe231f9d8d8853a"
+                "sha256:26838b2bc58620e01675485491504c3aa7ee0faf335c37fcd5f8731ca4319591",
+                "sha256:32c49a69566aa7c333188149ad48b58ac11a426d5352ea3d8f6ce843f88199cb"
             ],
-            "version": "==3.6.0"
+            "version": "==3.6.1"
         },
         "pytest-django": {
             "hashes": [
diff --git a/README.md b/README.md
index fd17d1d..9c30a36 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,13 @@ pipenv install
 If this doesn't work, it should be done from the same directory as the
 `Pipenv` file.
 
+Additionally, to run tests described later, you'll also need to install the
+development dependencies:
+
+```
+pipenv install --dev
+```
+
 Though possibly annoying, the commands in this file will assume the use of
 `pipenv` but not that the pipenv shell has been activated.
 
@@ -61,7 +68,7 @@ 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
+pipenv run python manage.py dumpdata trackers --format=yaml --indent=2 > trackers/fixtures/[fixture-name].yaml
 ```
 
 ## Integration Tests
@@ -79,9 +86,8 @@ tar -x geckodriver -zf "$TMP_DIR/geckodriver-$LATEST-$PLATFORM_EXT" -O > "$BIN_L
 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:
+If `$BIN_LOCATION` is on the system path, and the development server is
+running, it should be possible to run the integration tests.
 
 ```
 pipenv run python functional_tests.py


[bloodhound-core] 06/14: Adds a basic ticket view and update api

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit 31f1b1d58d196cf91d04a794a04f31bb83bb58f2
Author: Gary Martin <gj...@apache.org>
AuthorDate: Sun Oct 14 00:23:26 2018 +0000

    Adds a basic ticket view and update api
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1843803 13f79535-47bb-0310-9956-ffa450edef68
---
 trackers/serializers.py |  5 +++++
 trackers/urls.py        |  3 ++-
 trackers/views.py       | 10 +++++++---
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/trackers/serializers.py b/trackers/serializers.py
index 37c14f6..8d0a8d6 100644
--- a/trackers/serializers.py
+++ b/trackers/serializers.py
@@ -6,3 +6,8 @@ class TicketSerializer(serializers.ModelSerializer):
     class Meta:
         model = Ticket
         fields = '__all__'
+
+class ChangeEventSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = ChangeEvent
+        fields = '__all__'
diff --git a/trackers/urls.py b/trackers/urls.py
index 7537b32..3987dd5 100644
--- a/trackers/urls.py
+++ b/trackers/urls.py
@@ -20,5 +20,6 @@ from . import views
 
 urlpatterns = [
     path('', views.home, name='home'),
-    path('ticket', views.TicketList.as_view()),
+    path('ticket/', views.TicketListCreate.as_view()),
+    path('ticket/<uuid:id>', views.TicketViewUpdate.as_view()),
 ]
diff --git a/trackers/views.py b/trackers/views.py
index 64d3c34..8aa474e 100644
--- a/trackers/views.py
+++ b/trackers/views.py
@@ -18,15 +18,19 @@
 from django.http import HttpResponse
 from django.shortcuts import render
 from rest_framework import generics
+from trackers.serializers import TicketSerializer
+from trackers.models import Ticket
 
 def home(request):
     return HttpResponse('<html><title>Bloodhound Trackers</title></html>')
 
 
-from trackers.serializers import TicketSerializer
-from trackers.models import Ticket
+class TicketListCreate(generics.ListCreateAPIView):
+    queryset = Ticket.objects.all()
+    serializer_class = TicketSerializer
 
 
-class TicketList(generics.ListCreateAPIView):
+class TicketViewUpdate(generics.RetrieveUpdateAPIView):
     queryset = Ticket.objects.all()
     serializer_class = TicketSerializer
+    lookup_field = 'id'


[bloodhound-core] 05/14: Complete adding django rest framework example code

Posted by gj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git

commit 73249fa33262a0a3cf53dd8481e51fe58f43bd2a
Author: Gary Martin <gj...@apache.org>
AuthorDate: Sun Oct 14 00:23:25 2018 +0000

    Complete adding django rest framework example code
    
    git-svn-id: https://svn.apache.org/repos/asf/bloodhound/branches/bh_core_experimental@1843802 13f79535-47bb-0310-9956-ffa450edef68
---
 bh_core/settings.py | 1 +
 trackers/urls.py    | 1 +
 trackers/views.py   | 2 +-
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/bh_core/settings.py b/bh_core/settings.py
index 579b660..7482fd8 100644
--- a/bh_core/settings.py
+++ b/bh_core/settings.py
@@ -59,6 +59,7 @@ INSTALLED_APPS = [
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+    'rest_framework',
 ]
 
 MIDDLEWARE = [
diff --git a/trackers/urls.py b/trackers/urls.py
index a7a1c75..7537b32 100644
--- a/trackers/urls.py
+++ b/trackers/urls.py
@@ -20,4 +20,5 @@ from . import views
 
 urlpatterns = [
     path('', views.home, name='home'),
+    path('ticket', views.TicketList.as_view()),
 ]
diff --git a/trackers/views.py b/trackers/views.py
index 3483c9f..64d3c34 100644
--- a/trackers/views.py
+++ b/trackers/views.py
@@ -27,6 +27,6 @@ from trackers.serializers import TicketSerializer
 from trackers.models import Ticket
 
 
-class PlanetList(generics.ListCreateAPIView):
+class TicketList(generics.ListCreateAPIView):
     queryset = Ticket.objects.all()
     serializer_class = TicketSerializer