Skip to content

Commit

Permalink
Merge f1c0d76 into b13c275
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmiller committed Nov 2, 2018
2 parents b13c275 + f1c0d76 commit e4c1a2a
Show file tree
Hide file tree
Showing 40 changed files with 788 additions and 128 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
9 changes: 9 additions & 0 deletions 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 @@ -105,6 +111,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


Expand Down
12 changes: 12 additions & 0 deletions doc/docs/reference/upgrading.md
Expand Up @@ -5,6 +5,15 @@ application to a later version where there are extra steps required.

### 0.13.0 -> 0.12.0 - 0.11.2

#### 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

How you do this depends on how you have configured your application. You will need to
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

#### Free text or foreign key fields are now, by default case insensitive
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
2 changes: 1 addition & 1 deletion opal/core/fields.py
Expand Up @@ -158,7 +158,7 @@ def __get__(self, inst, cls):
return self
try:
foreign_obj = getattr(inst, self.fk_field_name)
except:
except Exception:
return 'Unknown Lookuplist Entry'
# foreign_obj = getattr(inst, self.fk_field_name)
if foreign_obj is None:
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 @@ -351,7 +351,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),
),
]
2 changes: 1 addition & 1 deletion opal/migrations/0009_glossolaliasubscription.py
Expand Up @@ -17,7 +17,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('subscription_type', models.CharField(default=b'all_information', max_length=2, choices=[(b'all_information', b'All Information'), (b'core_demographics', b'Core Demographics')])),
('gloss_id', models.IntegerField()),
('patient', models.ForeignKey(to='opal.Patient')),
('patient', models.ForeignKey(to='opal.Patient', on_delete=models.CASCADE)),
],
),
]
4 changes: 2 additions & 2 deletions opal/migrations/0011_patientrecordaccess.py
Expand Up @@ -18,8 +18,8 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('created', models.DateTimeField(auto_now_add=True)),
('patient', models.ForeignKey(to='opal.Patient')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('patient', models.ForeignKey(to='opal.Patient', on_delete=models.CASCADE)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
),
]

0 comments on commit e4c1a2a

Please sign in to comment.