Skip to content

Commit

Permalink
Merge branch 'v0.10.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
fredkingham committed Apr 18, 2018
2 parents 3422377 + be70a51 commit 31b993f
Show file tree
Hide file tree
Showing 30 changed files with 1,703 additions and 116 deletions.
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
### 0.10.1 (Minor Release)

#### Plugin API end points can now override application end points

A change to the order that APIs are registered with Django Rest Framework allows
plugins to now override the core Opal application APIs.

#### Fonts are now locally sourced

Fonts are now served from Opal's static assets rather than from the Google CDN.

#### print/screen stylesheets have been collapsed into opal.css

Print/screen differences are now in opal.css with media tags.

#### Google Analytics is now deferred

The loading in of Google Analytics is now deferred to the bottom of the body
tag to allow the page to load without waiting on analytics scripts to load.

#### Scaffold version control failures

The `startplugin` and `startproject` commands initialize a git repository by
default. If we (The `subprocess` module) cannot find the `git` command, we now
continue with a message printed to screen rather than raising an exception.

#### Episode.objects.serialised now uses select_related

`ForeignKeyOrFreeText` fields now have their ForeignKey items preselected when
we use `Episode.objects.serialised`. This provides a speed boost for applications
with moderately heavy `ForeignKeyOrFreeText` usage.

(Approx 30-40% in our tests.)


### 0.10.0 (Major Release)

This is a major release with breaking changes from upstream dependencies.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Opal
====

[![Build Status](https://travis-ci.org/openhealthcare/opal.svg?branch=v0.10.0)](https://travis-ci.org/openhealthcare/opal)
[![Coverage Status](https://coveralls.io/repos/github/openhealthcare/opal/badge.svg?branch=v0.10.0)](https://coveralls.io/github/openhealthcare/opal?branch=v0.10.0)
[![Build Status](https://travis-ci.org/openhealthcare/opal.svg?branch=v0.10.1)](https://travis-ci.org/openhealthcare/opal)
[![Coverage Status](https://coveralls.io/repos/github/openhealthcare/opal/badge.svg?branch=v0.10.1)](https://coveralls.io/github/openhealthcare/opal?branch=v0.10.1)
[![PyPI version](https://badge.fury.io/py/opal.svg)](https://badge.fury.io/py/opal)


Expand Down
31 changes: 15 additions & 16 deletions doc/docs/reference/upgrading.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,21 @@
This document provides instructions for specific steps required to upgrading your Opal
application to a later version where there are extra steps required.

### 0.9.1 -> 0.10.0

### 0.10.0 -> 0.10.1

#### Upgrading Opal

How you do this depends on how you have configured your application, but updating your
requirements.txt to update the version should work.

# requirements.txt
opal==0.10.1

There are no migrations or additional commands for this upgrae, and we are not aware of
any backwards incompatible changes.

### 0.9.0 -> 0.10.0

#### Upgrading Opal

Expand Down Expand Up @@ -103,21 +117,6 @@ logs into second will throw a CSRF failure because Django invalidates CSRF
tokens on login.


### 0.9.0 -> 0.9.1

#### Upgrading Opal

How you do this depends on how you have configured your application, but updating your
requirements.txt to update the version should work.

# requirements.txt
opal==0.9.1

After re-installing (via for instance `pip install -r requirements.txt`) you will need to
run the migrations for Opal 0.9.1

$ python manage.py migrate opal

### 0.8.3 -> 0.9.0

`episode.date_of_episode`, `episode.date_of_admission` and `episode.discharge_date` are all deprecated.
Expand Down
2 changes: 1 addition & 1 deletion doc/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ dev_addr: 0.0.0.0:8965
include_next_prev: false

extra:
version: v0.10.0
version: v0.10.1

markdown_extensions:
- fenced_code
2 changes: 1 addition & 1 deletion opal/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""
Declare our current version string
"""
__version__ = '0.10.0'
__version__ = '0.10.1.rc1'
49 changes: 28 additions & 21 deletions opal/core/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,32 +390,39 @@ def retrieve(self, request, pk=None):
return json_response(patientlist.to_dict(request.user))


router.register('patient', PatientViewSet)
router.register('episode', EpisodeViewSet)
router.register('record', RecordViewSet)
router.register('userprofile', UserProfileViewSet)
router.register('user', UserViewSet)
router.register('tagging', TaggingViewSet)
router.register('patientlist', PatientListViewSet)
router.register('patientrecordaccess', PatientRecordAccessViewSet)
def register_plugin_apis():
for plugin in plugins.OpalPlugin.list():
for api in plugin.get_apis():
router.register(*api)

router.register('referencedata', ReferenceDataViewSet)
router.register('metadata', MetadataViewSet)

for subrecord in subrecords():
sub_name = subrecord.get_api_name()
def register_subrecords():
for subrecord in subrecords():
sub_name = subrecord.get_api_name()

class SubViewSet(SubrecordViewSet):
base_name = sub_name
model = subrecord
class SubViewSet(SubrecordViewSet):
base_name = sub_name
model = subrecord

router.register(sub_name, SubViewSet)
router.register(sub_name, SubViewSet)


def register_plugin_apis():
for plugin in plugins.OpalPlugin.list():
for api in plugin.get_apis():
router.register(*api)
def initialize_router():
# plugin apis get initialised first, so that plugins
# can
register_plugin_apis()
router.register('patient', PatientViewSet)
router.register('episode', EpisodeViewSet)
router.register('record', RecordViewSet)
router.register('userprofile', UserProfileViewSet)
router.register('user', UserViewSet)
router.register('tagging', TaggingViewSet)
router.register('patientlist', PatientListViewSet)
router.register('patientrecordaccess', PatientRecordAccessViewSet)

router.register('referencedata', ReferenceDataViewSet)
router.register('metadata', MetadataViewSet)
register_subrecords()


register_plugin_apis()
initialize_router()
37 changes: 35 additions & 2 deletions opal/core/scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
Opal scaffolding and code generation
"""
import inspect
import errno
import os
import subprocess
import sys

from django.core import management
from django.utils.crypto import get_random_string
from django.apps import apps
Expand Down Expand Up @@ -57,6 +59,9 @@ def create_lookuplists(root_dir):


def call(cmd, **kwargs):
"""
Call an external program in a subprocess
"""
write("Calling: {}".format(' '.join(cmd)))
try:
subprocess.check_call(cmd, **kwargs)
Expand All @@ -65,6 +70,26 @@ def call(cmd, **kwargs):
sys.exit(1)


def call_if_exists(cmd, failure_message, **kwargs):
"""
Call an external program in a subprocess if it exists.
Returns True.
If it does not exist, write a failure message and return False
without raising an exception
"""
try:
call(cmd, **kwargs)
return True
except OSError as e:
if e.errno == errno.ENOENT:
write(failure_message)
return False
else:
raise


def start_plugin(name, USERLAND):
name = name

Expand Down Expand Up @@ -103,7 +128,11 @@ def start_plugin(name, USERLAND):
services = jsdir/'services'
services.mkdir()
# 5. Initialize git repo
call(('git', 'init'), cwd=root, stdout=subprocess.PIPE)
call_if_exists(
('git', 'init'),
'Unable to locate git; Skipping git repository initialization.',
cwd=root, stdout=subprocess.PIPE
)

write('Plugin complete at {0}'.format(reponame))
return
Expand Down Expand Up @@ -252,7 +281,11 @@ def manage(command):
manage('createopalsuperuser')

# 10. Initialise git repo
call(('git', 'init'), cwd=project_dir, stdout=subprocess.PIPE)
call_if_exists(
('git', 'init'),
'Unable to locate git; Skipping git repository initialization.',
cwd=project_dir, stdout=subprocess.PIPE
)

# 11. Load referencedata shipped with Opal
manage('load_lookup_lists')
Expand Down
26 changes: 24 additions & 2 deletions opal/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from opal.core.subrecords import (
episode_subrecords, patient_subrecords
)
from opal.core.fields import ForeignKeyOrFreeText
from functools import reduce


Expand All @@ -32,6 +33,23 @@ def search(self, some_query):
return qs


def prefetch(qs):
"""
Given a Queryset QS, examine the model for `ForeignKeyOrFreetext`
fields or `ManyToMany` fields and add `select_related` or
`prefetch_related` calls to the queryset as appropriate to reduce
the total number of database queries required to serialise the
contents of the queryset.
"""
for name, value in list(vars(qs.model).items()):
if isinstance(value, ForeignKeyOrFreeText):
qs = qs.select_related(value.fk_field_name)

for related in qs.model._meta.many_to_many:
qs = qs.prefetch_related(related.attname)
return qs


class EpisodeQueryset(models.QuerySet):

def search(self, some_query):
Expand All @@ -51,7 +69,9 @@ def serialised_episode_subrecords(self, episodes, user):

for model in episode_subrecords():
name = model.get_api_name()
subrecords = model.objects.filter(episode__in=episodes)
subrecords = prefetch(
model.objects.filter(episode__in=episodes)
)

for related in model._meta.many_to_many:
subrecords = subrecords.prefetch_related(related.attname)
Expand All @@ -74,7 +94,9 @@ def serialised(self, user, episodes,
episode_subs = self.serialised_episode_subrecords(episodes, user)
for model in patient_subrecords():
name = model.get_api_name()
subrecords = model.objects.filter(patient__in=patient_ids)
subrecords = prefetch(
model.objects.filter(patient__in=patient_ids)
)

for sub in subrecords:
patient_subs[sub.patient_id][name].append(sub.to_dict(user))
Expand Down
79 changes: 79 additions & 0 deletions opal/static/css/opal.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,82 @@
/* lato-300 - latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 300;
src: url('../fonts/lato-v14-latin-300.eot'); /* IE9 Compat Modes */
src: local('Lato Light'), local('Lato-Light'),
url('../fonts/lato-v14-latin-300.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/lato-v14-latin-300.woff2') format('woff2'), /* Super Modern Browsers */
url('../fonts/lato-v14-latin-300.woff') format('woff'), /* Modern Browsers */
url('../fonts/lato-v14-latin-300.ttf') format('truetype'), /* Safari, Android, iOS */
url('../fonts/lato-v14-latin-300.svg#Lato') format('svg'); /* Legacy iOS */
}
/* lato-regular - latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
src: url('../fonts/lato-v14-latin-regular.eot'); /* IE9 Compat Modes */
src: local('Lato Regular'), local('Lato-Regular'),
url('../fonts/lato-v14-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/lato-v14-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('../fonts/lato-v14-latin-regular.woff') format('woff'), /* Modern Browsers */
url('../fonts/lato-v14-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
url('../fonts/lato-v14-latin-regular.svg#Lato') format('svg'); /* Legacy iOS */
}
/* lato-700 - latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 700;
src: url('../fonts/lato-v14-latin-700.eot'); /* IE9 Compat Modes */
src: local('Lato Bold'), local('Lato-Bold'),
url('../fonts/lato-v14-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/lato-v14-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
url('../fonts/lato-v14-latin-700.woff') format('woff'), /* Modern Browsers */
url('../fonts/lato-v14-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
url('../fonts/lato-v14-latin-700.svg#Lato') format('svg'); /* Legacy iOS */
}


@media print{
@page {
size: landscape;
}
.patient-list-container {
overflow: visible;
overflow-y: visible;
}

li {
padding-top: 0px;
padding-bottom: 0px;
line-height: 12px;
}

.screen-only {
display: none;
}

/*
* it needs important because bootstrap
*/
.always-on-print-block{
display: block !important;
}

table {
font-size: 12px
}
}

@media screen{
.print-only {
display: none;
}
}


/* Bootstrap resets */
html, .outer-container{
height: 100%;
Expand Down
Loading

0 comments on commit 31b993f

Please sign in to comment.