Skip to content

Commit

Permalink
[#90] Use grouping instead of window function to also support django1…
Browse files Browse the repository at this point in the history
….11 and stop using postgres as test db and use sqlite3 back again
  • Loading branch information
javrasya committed Sep 4, 2019
1 parent ed538e8 commit 30ce190
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 65 deletions.
2 changes: 0 additions & 2 deletions .travis.yml
@@ -1,6 +1,4 @@
language: python
services:
- docker
sudo: false
cache: pip
matrix:
Expand Down
11 changes: 5 additions & 6 deletions requirements.txt
@@ -1,6 +1,5 @@
django-mptt
factory-boy
mock
pyhamcrest
django-cte
psycopg2
django-mptt==0.9.1
factory-boy==2.11.1
mock==2.0.0
pyhamcrest==1.9.0
django-cte==1.1.4
44 changes: 26 additions & 18 deletions river/core/classworkflowobject.py
@@ -1,7 +1,7 @@
from django.contrib import auth
from django.contrib.contenttypes.models import ContentType
from django.db.models import Window, F, Q, IntegerField
from django.db.models.functions import Rank, Cast
from django.db.models import F, Q, IntegerField, Min
from django.db.models.functions import Cast
from django_cte import With

from river.models import State, TransitionApprovalMeta, TransitionApproval, PENDING, Workflow
Expand All @@ -27,29 +27,37 @@ def get_on_approval_objects(self, as_user):
return self.wokflow_object_class.objects.filter(pk__in=object_ids)

def get_available_approvals(self, as_user):
approvals_with_priority_rank = With(
TransitionApproval.objects.filter(workflow=self.workflow, status=PENDING, skipped=False, enabled=True).annotate(
priority_rank=Window(
expression=Rank(),
order_by=F('priority').asc(),
partition_by=[F('workflow'), F('object_id'), F('source_state'), F('destination_state')])
)
those_with_max_priority = With(
TransitionApproval.objects.filter(
workflow=self.workflow, status=PENDING, skipped=False, enabled=True
).values(
'workflow', 'object_id', 'source_state', 'destination_state'
).annotate(min_priority=Min('priority'))
)

workflow_objects = With(
self.wokflow_object_class.objects.all(),
name="workflow_object"
)

approvals_after_ranking = approvals_with_priority_rank \
.join(self._authorized_approvals(as_user), id=approvals_with_priority_rank.col.id) \
.with_cte(approvals_with_priority_rank) \
.annotate(object_id_as_int=Cast('object_id', IntegerField()), priority_rank=approvals_with_priority_rank.col.priority_rank) \
.filter(priority_rank=1)

return workflow_objects.join(approvals_after_ranking, object_id_as_int=workflow_objects.col.pk) \
.with_cte(workflow_objects) \
.filter(source_state=getattr(workflow_objects.col, self.field_name + "_id"))
approvals_with_max_priority = those_with_max_priority.join(
self._authorized_approvals(as_user),
workflow_id=those_with_max_priority.col.workflow_id,
object_id=those_with_max_priority.col.object_id,
source_state_id=those_with_max_priority.col.source_state_id,
destination_state_id=those_with_max_priority.col.destination_state_id,
).with_cte(
those_with_max_priority
).annotate(
object_id_as_int=Cast('object_id', IntegerField()),
min_priority=those_with_max_priority.col.min_priority
).filter(min_priority=F("priority"))

return workflow_objects.join(
approvals_with_max_priority, object_id_as_int=workflow_objects.col.pk
).with_cte(
workflow_objects
).filter(source_state=getattr(workflow_objects.col, self.field_name + "_id"))

@property
def initial_state(self):
Expand Down
2 changes: 1 addition & 1 deletion river/core/instanceworkflowobject.py
Expand Up @@ -31,7 +31,7 @@ def __init__(self, workflow_object, name, field_name):
def initialize_approvals(self):
if not self.initialized:
workflow = Workflow.objects.filter(content_type=self.content_type, field_name=self.field_name).first()
if workflow and workflow.transition_approvals.count() == 0:
if workflow and workflow.transition_approvals.filter(workflow_object=self.workflow_object).count() == 0:
transition_approval_metas = workflow.transition_approval_metas.all()
meta_dict = six.moves.reduce(
lambda agg, meta: dict(agg, **{self._to_key(meta.source_state): agg.get(self._to_key(meta.source_state), []) + [meta]}),
Expand Down
4 changes: 1 addition & 3 deletions river/migrations/0001_initial.py
Expand Up @@ -11,9 +11,7 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0002_remove_content_type_name'),
('auth', '0009_alter_user_last_name_max_length'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL)
]

operations = [
Expand Down
38 changes: 19 additions & 19 deletions test_settings.py
Expand Up @@ -9,29 +9,29 @@

DEBUG = True

# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# },
# }


TEST_DB_PORT = os.environ['POSTGRES_5432_TCP_PORT']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'river',
'USER': 'river',
'PASSWORD': 'river',
'HOST': 'localhost',
'PORT': TEST_DB_PORT,
'TEST': {
'NAME': 'test-db' + str(uuid4()),
},
}
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
}


# TEST_DB_PORT = os.environ['POSTGRES_5432_TCP_PORT']
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.postgresql_psycopg2',
# 'NAME': 'river',
# 'USER': 'river',
# 'PASSWORD': 'river',
# 'HOST': 'localhost',
# 'PORT': TEST_DB_PORT,
# 'TEST': {
# 'NAME': 'test-db' + str(uuid4()),
# },
# }
# }

# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
Expand Down
22 changes: 6 additions & 16 deletions tox.ini
@@ -1,36 +1,26 @@
[tox]
envlist = {py27,py34}-{dj1.7,dj1.8,dj1.9,dj1.10,dj1.11},
{py35}-{dj1.8,dj1.9,dj1.10,dj1.11,dj2.0,dj2.1},
{py36}-{dj1.8,dj1.9,dj1.10,dj1.11,dj2.0,dj2.1},
envlist = {py27,py34}-{dj1.11},
{py35}-{dj1.11,dj2.0,dj2.1,dj2.2},
{py36}-{dj1.11,dj2.0,dj2.1,dj2.2},
cov

[testenv]
docker =
postgres:11.5-alpine
dockerenv =
POSTGRES_DB=river
POSTGRES_USER=river
POSTGRES_PASSWORD=river
deps =
dj1.7: pytest-django==3.1.2
{dj1.8,dj1.9,dj1.10,dj1.11,dj2.0,dj2.1}: pytest-django>3.1.2
{dj1.11,dj2.0,dj2.1,dj2.2}: pytest-django>3.1.2
pytest-cov
-rrequirements.txt
dj1.7: Django>=1.7,<1.8.0
dj1.8: Django>=1.8,<1.9.0
dj1.9: Django>=1.9,<1.10.0
dj1.10: Django>=1.10,<1.11.0
dj1.11: Django>=1.11,<1.12.0
dj2.0: Django>=2.0,<2.1.0
dj2.1: Django>=2.1,<2.2.0
dj2.2: Django>=2.2,<2.3.0
commands = py.test --ds='test_settings' --junitxml=../junit-{envname}.xml

[testenv:cov]
basepython = python3.6
deps =
pytest-django
pytest-cov
django>=2.1,<2.2.0
django>=2.2,<2.3.0
-rrequirements.txt
commands =
py.test --ds='test_settings' --cov ./ --cov-report term-missing
Expand Down

0 comments on commit 30ce190

Please sign in to comment.