Skip to content

Commit

Permalink
Merge a520c0d into fa72f33
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmiller committed Dec 18, 2018
2 parents fa72f33 + a520c0d commit e841bf6
Show file tree
Hide file tree
Showing 46 changed files with 996 additions and 177 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
@@ -1,9 +1,8 @@
language: python
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"

services:
- postgresql
install:
Expand Down
16 changes: 15 additions & 1 deletion changelog.md
@@ -1,5 +1,11 @@
### 0.13.0 (Major Release)

#### Removes support for Python 2.x

Due to the upgrade to Django 2.x, Opal no longer supports Python 2.x.

Opal is now tested against Python 3.5, 3.6

#### Episode.active

The field `Episode.active` was previously implicitly set when calling `.set_tag_names()` to
Expand Down Expand Up @@ -103,9 +109,14 @@ including the `reopen_episode_modal.html` template and the url/view at `template

* Adds in a footer updated/created by to the form base template

* Changes the default value of `_ft` fields on `ForeignKeyOrFreeTextField` from b'' to ''. This requires a migration

* `__unicode__` model methods have been renamed `__str__`

* Adds an index argument to `PatientList.as_menuitem()` and `Pathway.as_menuitem()`

* Adds a `get_absolute_url()` method to `Patient` and `Episode`
* Adds a `get_absolute_url()` method to `Patient` and `Episode


* Renames the (undocumented, internal) Angular service `FieldTranslater` to `FieldTranslator`

Expand All @@ -114,6 +125,9 @@ including the `reopen_episode_modal.html` template and the url/view at `template

#### Updates to the Dependency Graph

* Django: 1.10.8 -> 2.0.9
* Django Rest Framework: 3.4.7 -> 3.7.4
* Django Reversion: 1.10.2 -> 3.0.1
* Letter: 0.4.1 -> 0.5
* Requests: 2.18.4 -> 2.20.1
* Psycopg2: 2.7 -> 2.7.6.1
Expand Down
14 changes: 13 additions & 1 deletion doc/docs/reference/upgrading.md
Expand Up @@ -3,7 +3,16 @@
This document provides instructions for specific steps required to upgrading your Opal
application to a later version where there are extra steps required.

### 0.13.0 -> 0.12.0 - 0.11.2
### 0.12.0 - 0.11.2 -> 0.13.0

#### Python versions

Opal 0.13.0 drops support for Python 2.x
If you have not already done so, you will need to upgrade your application to Python 3
in order to upgrade.

You may also like to run the tests for your application with the 'show warnings'
flag e.g. `python -Wd manage.py test`

#### Upgrading Opal

Expand All @@ -16,6 +25,9 @@ you have specified them in for instance, a requirements.txt.

# requirements.txt
opal==0.10.0
django==2.0.9
django-reversion==3.0.1
djangorestframework==3.7.4
letter==0.5
psycopg==2.7.6.1
python-dateutil==2.7.5
Expand Down
4 changes: 2 additions & 2 deletions opal/core/application.py
Expand Up @@ -5,7 +5,7 @@
import itertools
import os

from django.core.urlresolvers import reverse
from django.urls import reverse
from opal.core import plugins, menus


Expand Down Expand Up @@ -134,7 +134,7 @@ def get_menu_items(klass, user=None):
items = []
items += klass.menuitems
if user:
if not user.is_authenticated():
if not user.is_authenticated:
return []
else:
items.append(logout)
Expand Down
3 changes: 1 addition & 2 deletions opal/core/fields.py
@@ -1,6 +1,5 @@
from django.db import models
from django.contrib.contenttypes.models import ContentType
from six import b
from django.db.models.signals import pre_delete
from opal.utils import _itersubclasses

Expand Down Expand Up @@ -83,7 +82,7 @@ def contribute_to_class(self, cls, name):
)
fk_field.contribute_to_class(cls, self.fk_field_name)
ft_field = models.CharField(
max_length=255, blank=True, null=True, default=b('')
max_length=255, blank=True, null=True, default=''
)
ft_field.contribute_to_class(cls, self.ft_field_name)

Expand Down
2 changes: 1 addition & 1 deletion opal/core/log.py
Expand Up @@ -23,7 +23,7 @@ def emit(self, record):
# In case the error occurrs before the authentication middleware
# has run we need to check that the request has a user
if hasattr(record.request, "user"):
if record.request.user.is_authenticated():
if record.request.user.is_authenticated:
user = record.request.user.username

m = "Request to host {0} on application {1} from user {2} with {3}"
Expand Down
2 changes: 1 addition & 1 deletion opal/core/pathway/pathways.py
Expand Up @@ -5,7 +5,7 @@
import json
from collections import defaultdict

from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db import models, transaction
from django.utils.text import slugify
from six import string_types
Expand Down
2 changes: 1 addition & 1 deletion opal/core/pathway/tests/test_api.py
@@ -1,6 +1,6 @@
import json
from opal.core.test import OpalTestCase
from django.core.urlresolvers import reverse
from django.urls import reverse
from mock import patch, MagicMock
from opal.core.pathway.tests.pathway_test import pathways as test_pathways

Expand Down
2 changes: 1 addition & 1 deletion opal/core/pathway/tests/test_pathways.py
Expand Up @@ -5,7 +5,7 @@
from opal.core.exceptions import InitializationError
from django.utils import timezone
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.urls import reverse
from opal.core import exceptions
from opal.core.test import OpalTestCase
from opal.core.views import OpalSerializer
Expand Down
2 changes: 1 addition & 1 deletion opal/core/pathway/tests/test_steps.py
@@ -1,7 +1,7 @@
"""
unittests for opal.core.pathway.steps
"""
from django.core.urlresolvers import reverse
from django.urls import reverse
from mock import MagicMock

from opal.core import exceptions
Expand Down
11 changes: 5 additions & 6 deletions opal/core/pathway/tests/test_template_tags.py
Expand Up @@ -28,13 +28,12 @@ def test_global_template_context_not_changed(self, get_form_template):
self.assertFalse(rendered.strip().endswith("editing.colour"))

def test_nested_template_context(self, get_form_template):
template = Template('{% load pathways %}{% multisave models.Colour %}')
models = dict(models=dict(Colour=Colour), some_test_var="onions")
template.render(Context(models))
self.assertEqual(
get_form_template.render.call_args[0][0]["some_test_var"],
'onions'
template = Template(
'{% load pathways %}{% multisave models.Colour %}OMG: {{ some_test_var }}'
)
models = dict(models=dict(Colour=Colour), some_test_var="onions")
resp = template.render(Context(models))
self.assertIn('OMG: onions', resp)

def test_add_common_context(self, get_form_template):
ctx = template_tags.add_common_context({}, Colour)
Expand Down
2 changes: 1 addition & 1 deletion opal/core/pathway/tests/test_url.py
@@ -1,5 +1,5 @@
from opal.core.test import OpalTestCase
from django.core.urlresolvers import reverse
from django.urls import reverse


class PathwayReverseUrlTests(OpalTestCase):
Expand Down
2 changes: 1 addition & 1 deletion opal/core/pathway/tests/test_views.py
@@ -1,5 +1,5 @@
from opal.core.test import OpalTestCase
from django.core.urlresolvers import reverse
from django.urls import reverse
from opal.core.pathway.tests.pathway_test import pathways as test_pathways


Expand Down
2 changes: 1 addition & 1 deletion opal/core/patient_lists.py
Expand Up @@ -352,7 +352,7 @@ def to_dict(klass, user=None, **kw):
tag_slugs = {}
tag_list = [i for i in TaggedPatientList.for_user(user)]

if user.is_authenticated():
if user.is_authenticated:
for taglist in tag_list:
slug = taglist().get_slug()
tag = taglist.tag
Expand Down
4 changes: 2 additions & 2 deletions opal/core/search/extract.py
Expand Up @@ -10,7 +10,7 @@
import tempfile
import zipfile

from django.template import Context, loader
from django.template import loader
from django.utils.encoding import force_bytes
from six import text_type

Expand Down Expand Up @@ -222,7 +222,7 @@ def get_data_dictionary():
def write_data_dictionary(file_name):
dictionary = get_data_dictionary()
t = loader.get_template("extract_data_schema.html")
ctx = Context(dict(schema=dictionary))
ctx = dict(schema=dictionary)
rendered = t.render(ctx)
with open(file_name, "w") as f:
f.write(rendered)
Expand Down
4 changes: 2 additions & 2 deletions opal/core/search/tests/test_api.py
Expand Up @@ -21,10 +21,10 @@ def setUp(self):
self.patient, self.episode = self.new_patient_and_episode_please()
self.request = self.rf.get("/")

def test_403(self):
def test_401(self):
url = reverse('extract-schema-list', request=self.request)
response = self.client.get(url)
self.assertEqual(
response.status_code,
status.HTTP_403_FORBIDDEN
401
)
2 changes: 1 addition & 1 deletion opal/core/search/tests/test_extract.py
Expand Up @@ -5,7 +5,7 @@
import json
import os

from django.core.urlresolvers import reverse
from django.urls import reverse
from django.test import override_settings
from mock import mock_open, patch, Mock, MagicMock

Expand Down
10 changes: 7 additions & 3 deletions opal/core/search/views.py
Expand Up @@ -45,7 +45,7 @@ class ExtractTemplateView(LoginRequiredMixin, TemplateView):
def ajax_login_required(view):
@wraps(view)
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated():
if not request.user.is_authenticated:
raise PermissionDenied
return view(request, *args, **kwargs)
return wrapper
Expand All @@ -54,7 +54,7 @@ def wrapper(request, *args, **kwargs):
def ajax_login_required_view(view):
@wraps(view)
def wrapper(self, *args, **kwargs):
if not self.request.user.is_authenticated():
if not self.request.user.is_authenticated:
raise PermissionDenied
return view(self, *args, **kwargs)
return wrapper
Expand Down Expand Up @@ -159,7 +159,11 @@ def post(self, *args, **kwargs):
)
episodes = query.get_episodes()
fname = zip_archive(episodes, query.description(), self.request.user)
resp = HttpResponse(open(fname, 'rb').read())

with open(fname, 'rb') as download:
content = download.read()

resp = HttpResponse(content)
disp = 'attachment; filename="{0}extract{1}.zip"'.format(
settings.OPAL_BRAND_NAME, datetime.datetime.now().isoformat())
resp['Content-Disposition'] = disp
Expand Down
7 changes: 4 additions & 3 deletions opal/middleware.py
@@ -1,20 +1,21 @@
"""
Opal Middlewares
"""
from django.utils.deprecation import MiddlewareMixin
# Hat tip:
# http://kevinzhang.org/posts/django-angularjs-and-csrf-xsrf-protection.html

ANGULAR_HEADER_NAME = 'HTTP_X_XSRF_TOKEN'


class AngularCSRFRename(object):
class AngularCSRFRename(MiddlewareMixin):
def process_request(self, request):
if ANGULAR_HEADER_NAME in request.META:
token = request.META[ANGULAR_HEADER_NAME]
request.META['HTTP_X_CSRFTOKEN'] = token
del request.META[ANGULAR_HEADER_NAME]


class DjangoReversionWorkaround(object):
class DjangoReversionWorkaround(MiddlewareMixin):
def process_request(self, request):
access = request.user.is_authenticated() # noqa:
access = request.user.is_authenticated # noqa:
16 changes: 8 additions & 8 deletions opal/migrations/0001_initial.py
Expand Up @@ -208,7 +208,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=200)),
('criteria', models.TextField()),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
options={
},
Expand Down Expand Up @@ -592,7 +592,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=255)),
('object_id', models.PositiveIntegerField()),
('content_type', models.ForeignKey(to='contenttypes.ContentType')),
('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)),
],
options={
},
Expand All @@ -602,7 +602,7 @@ class Migration(migrations.Migration):
name='Tagging',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('episode', models.ForeignKey(to='opal.Episode')),
('episode', models.ForeignKey(to='opal.Episode', on_delete=models.CASCADE)),
],
options={
},
Expand All @@ -619,7 +619,7 @@ class Migration(migrations.Migration):
('restricted', models.BooleanField(default=False, help_text=b'Whether this team is restricted to only a subset of users')),
('direct_add', models.BooleanField(default=True)),
('show_all', models.BooleanField(default=False)),
('parent', models.ForeignKey(blank=True, to='opal.Team', null=True)),
('parent', models.ForeignKey(blank=True, to='opal.Team', null=True, on_delete=models.CASCADE)),
('useful_numbers', models.ManyToManyField(to='opal.ContactNumber', blank=True)),
],
options={
Expand Down Expand Up @@ -647,7 +647,7 @@ class Migration(migrations.Migration):
('readonly', models.BooleanField(default=False, help_text=b'This user will only be able to read data - they have no write/edit permissions')),
('restricted_only', models.BooleanField(default=False, help_text=b'This user will only see teams that they have been specifically added to')),
('roles', models.ManyToManyField(to='opal.Role')),
('user', models.OneToOneField(related_name=b'profile', to=settings.AUTH_USER_MODEL)),
('user', models.OneToOneField(related_name='profile', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
options={
},
Expand All @@ -668,13 +668,13 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='tagging',
name='team',
field=models.ForeignKey(blank=True, to='opal.Team', null=True),
field=models.ForeignKey(blank=True, to='opal.Team', null=True, on_delete=models.CASCADE),
preserve_default=True,
),
migrations.AddField(
model_name='tagging',
name='user',
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True),
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
preserve_default=True,
),
migrations.AlterUniqueTogether(
Expand All @@ -684,7 +684,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='episode',
name='patient',
field=models.ForeignKey(to='opal.Patient'),
field=models.ForeignKey(to='opal.Patient', on_delete=models.CASCADE),
preserve_default=True,
),
]
8 changes: 4 additions & 4 deletions opal/migrations/0003_auto_20150922_1825.py
Expand Up @@ -21,7 +21,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='episode',
name='created_by',
field=models.ForeignKey(related_name='created_opal_episode_subrecords', blank=True, to=settings.AUTH_USER_MODEL, null=True),
field=models.ForeignKey(related_name='created_opal_episode_subrecords', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
),
migrations.AddField(
model_name='episode',
Expand All @@ -31,7 +31,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='episode',
name='updated_by',
field=models.ForeignKey(related_name='updated_opal_episode_subrecords', blank=True, to=settings.AUTH_USER_MODEL, null=True),
field=models.ForeignKey(related_name='updated_opal_episode_subrecords', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
),
migrations.AddField(
model_name='tagging',
Expand All @@ -41,7 +41,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='tagging',
name='created_by',
field=models.ForeignKey(related_name='created_opal_tagging_subrecords', blank=True, to=settings.AUTH_USER_MODEL, null=True),
field=models.ForeignKey(related_name='created_opal_tagging_subrecords', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
),
migrations.AddField(
model_name='tagging',
Expand All @@ -51,6 +51,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='tagging',
name='updated_by',
field=models.ForeignKey(related_name='updated_opal_tagging_subrecords', blank=True, to=settings.AUTH_USER_MODEL, null=True),
field=models.ForeignKey(related_name='updated_opal_tagging_subrecords', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
),
]

0 comments on commit e841bf6

Please sign in to comment.