diff --git a/changelog.md b/changelog.md index ddc5a9659..f4f7b3c31 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,8 @@ Removes the methods PatientDetailCtrl.dischargeEpisode() and PatientListCtrl.dis Removes PatientList.removeFromMine() (The special casing of the 'Mine' list is strongly unadvised and set for complete removal.) +Removes the front end Flow service + #### Miscelanious changes Bumps Flake8 version to 3.7.8 - new code will now be required to pass flake8 v3.7.8 diff --git a/doc/docs/guides/flow.md b/doc/docs/guides/flow.md deleted file mode 100644 index 65b6ca88e..000000000 --- a/doc/docs/guides/flow.md +++ /dev/null @@ -1,69 +0,0 @@ -# Flow hooks in Opal - -Opal provides various hooks that developers can use to customise behaviour at certain key points -in a patient's journey through a clinical service - for example when a patient is discharged. - -These hooks are associated with verbs `enter`, `exit`. - -### The Flow service - -Your application scaffold will have created a file at -`./yourapp/assets/js/yourapp/services/flow.js`. This file will declare an angular service that -your application will use to determine how to move to the correct next step for a patient. - -To enable this, we must set the following setting: - -```python -# settings.py -OPAL_FLOW_SERVICE = 'MyAppFlow' -``` - -Flow services must define an `enter` and an `exit` method, which both return the appropriate -angular controller and template to use. Although Opal provides sensible default controllers and -templates for these common actions, applications with custom flows may customise these methods -as required. - -### The enter verb - -The enter verb is called when a patient is added to a service - for instance by adding a patient -to a team list. - -Implementations of enter are expected to return a dictionary of the controller and template they -wish to be called. - - enter: function(){ - return { - 'controller': 'HospitalNumberCtrl', - 'template' : '/templates/hospital_number_modal.html/' - } - } - -A common strategy is to examine angular `$route` or `$routeParams` to determine where the user is -in the application. -For instance you may wish to have custom controllers for a particular patient list, or the search -results page. - -### The exit verb - -The exit verb is called when a patient is moving through a service - for instance when we -discharge a patient, or end -one phase of a clinical pathway. - -Implementations of exit are expected to return a dictionary of the controller and template -they wish to be called. - -The enter verb will receive the episode that we are acting on - for instance a controller -that acts differently for deceased patients might look as follows - - exit: function(episode){ - if(episode.demographics[0].deceased){ - return { - 'controller': 'DeceasedDischargeEpisodeCtrl', - 'template' : '/templates/modals/deceased_discharge.html/' - } - } - return { - 'controller': 'DischargeEpisodeCtrl', - 'template' : '/templates/discharge_episode_modal.html' - } - } diff --git a/doc/docs/guides/topic-guides.md b/doc/docs/guides/topic-guides.md index 21fc82ca9..5a6f89e9a 100644 --- a/doc/docs/guides/topic-guides.md +++ b/doc/docs/guides/topic-guides.md @@ -22,7 +22,6 @@ A list of all available topic guides. |[Core Clinical Model](archetypes.md)| The core clinical data model available to Opal applications| |[Reference data](referencedata.md) | Canonical coded terms and reference data| |[App metadata](metadata.md) | Working with Metadata on the front end | -|[Flow](flow.md) | Hooks to customise key moments in a patient's flow through a clinical service| |[Angular models](working_with_data_in_angular.md)|Working with Clinical data in Angular| |[Roles & Permissions](roles_and_permissions.md)| How we handle authorization in Opal| |[Tagging](tagging.md) | Tagging episodes in Opal | diff --git a/doc/mkdocs.yml b/doc/mkdocs.yml index a6365c8b2..a6eb7e8d1 100644 --- a/doc/mkdocs.yml +++ b/doc/mkdocs.yml @@ -27,7 +27,6 @@ pages: - Referencedata: guides/referencedata.md - Metadata: guides/metadata.md - Angular Models: guides/working_with_data_in_angular.md - - Flow: guides/flow.md - Roles & Permissions: guides/roles_and_permissions.md - Tagging: guides/tagging.md - JSON API: guides/json_api.md diff --git a/opal/core/episodes.py b/opal/core/episodes.py index cce7cbaa7..144dfcaa1 100644 --- a/opal/core/episodes.py +++ b/opal/core/episodes.py @@ -16,7 +16,6 @@ Display Permissions -Flow By registering episode category, plugins and applications can achieve a huge degree of flexibility over the behaviour of their episodes. diff --git a/opal/core/search/static/js/test/search.controller.test.js b/opal/core/search/static/js/test/search.controller.test.js index cdf842c08..b0c302259 100644 --- a/opal/core/search/static/js/test/search.controller.test.js +++ b/opal/core/search/static/js/test/search.controller.test.js @@ -4,7 +4,6 @@ describe('SearchCtrl', function (){ var $scope, $httpBackend, $window, $rootScope, $controller; var ngProgressLite var location; - var Flow; var profile, schema, options, locationDetails, controller; var PatientSummary, $analytics; @@ -23,7 +22,6 @@ describe('SearchCtrl', function (){ $rootScope = $injector.get('$rootScope'); $scope = $rootScope.$new(); $controller = $injector.get('$controller'); - Flow = $injector.get('Flow'); PatientSummary = $injector.get('PatientSummary'); $httpBackend = $injector.get('$httpBackend'); location = $injector.get('$location'); @@ -40,7 +38,6 @@ describe('SearchCtrl', function (){ controller = $controller('SearchCtrl', { $scope : $scope, $location : location, - Flow : Flow, options : options, schema : schema, profile : profile, diff --git a/opal/static/js/opal/controllers/patient_detail.js b/opal/static/js/opal/controllers/patient_detail.js index d0f240c5b..f636f235b 100644 --- a/opal/static/js/opal/controllers/patient_detail.js +++ b/opal/static/js/opal/controllers/patient_detail.js @@ -2,7 +2,7 @@ angular.module('opal.controllers').controller( 'PatientDetailCtrl', function( $rootScope, $scope, $modal, $location, $routeParams, - Flow, Item, patientLoader, patient, profile, metadata + Item, patientLoader, patient, profile, metadata ){ $scope.profile = profile; $scope.patient = patient; diff --git a/opal/static/js/opal/controllers/patient_list.js b/opal/static/js/opal/controllers/patient_list.js index 92ba6d7e7..f1d850f71 100644 --- a/opal/static/js/opal/controllers/patient_list.js +++ b/opal/static/js/opal/controllers/patient_list.js @@ -1,366 +1,283 @@ angular.module('opal.controllers').controller( - 'PatientListCtrl', function($scope, $q, $http, $cookies, - $location, $routeParams, - $modal, $rootScope, $window, $injector, - growl, Flow, Item, Episode, - episodedata, metadata, profile, episodeLoader, - episodeVisibility){ - - $scope.ready = false; - var version = window.version; - if(episodedata.status == 'error'){ - if($cookies.get('opal.previousPatientList')){ - $cookies.remove('opal.previousPatientList'); - $location.path('/list/') - return - } - $window.location.href = '/404'; - return - }else{ - $scope.episodes = episodedata.data; - $rootScope.state = 'normal'; - $scope.url = $location.url(); - $scope.metadata = metadata; - $scope.listView = true; - - $scope.num_episodes = _.keys($scope.episodes).length; - - $scope.rix = 0; // row index - $scope._ = _; - - $scope.query = { - hospital_number: '', first_name: '', surname: '', ward: '', bed: '' - }; - $scope.$location = $location; - $scope.path_base = '/list/'; - $scope.profile = profile; - - $cookies.put('opal.previousPatientList', $routeParams.slug); - var tags = $routeParams.slug.split('-') - $scope.currentTag = tags[0]; - $scope.currentSubTag = tags.length == 2 ? tags[1] : ""; - $scope.tag_display = metadata.tag_display; - var pertinantTag = $scope.currentSubTag || $scope.currentTag; - - // - // These are used for setting custom list sort orders - // - $scope.comparators = null; - - if($scope.metadata.patient_list_comparators && - _.has($scope.metadata.patient_list_comparators, $routeParams.slug)){ - $scope.comparators = $injector.get( - $scope.metadata.patient_list_comparators[$routeParams.slug] - ); - } - } + 'PatientListCtrl', function($scope, $q, $http, $cookies, + $location, $routeParams, + $modal, $rootScope, $window, $injector, + growl, Item, Episode, + episodedata, metadata, profile, episodeLoader, + episodeVisibility){ + + $scope.ready = false; + var version = window.version; + if(episodedata.status == 'error'){ + if($cookies.get('opal.previousPatientList')){ + $cookies.remove('opal.previousPatientList'); + $location.path('/list/') + return + } + $window.location.href = '/404'; + return + }else{ + $scope.episodes = episodedata.data; + $rootScope.state = 'normal'; + $scope.url = $location.url(); + $scope.metadata = metadata; + $scope.listView = true; + + $scope.num_episodes = _.keys($scope.episodes).length; + + $scope.rix = 0; // row index + $scope._ = _; + + $scope.query = { + hospital_number: '', first_name: '', surname: '', ward: '', bed: '' + }; + $scope.$location = $location; + $scope.path_base = '/list/'; + $scope.profile = profile; + + $cookies.put('opal.previousPatientList', $routeParams.slug); + var tags = $routeParams.slug.split('-') + $scope.currentTag = tags[0]; + $scope.currentSubTag = tags.length == 2 ? tags[1] : ""; + $scope.tag_display = metadata.tag_display; + var pertinantTag = $scope.currentSubTag || $scope.currentTag; - $scope.compareEpisodes = function(p1, p2) { - if($scope.comparators){ - return p1.compare(p2, $scope.comparators); - }else{ - return p1.compare(p2); - } - }; + // + // These are used for setting custom list sort orders + // + $scope.comparators = null; - $scope.getVisibleEpisodes = function() { - var visibleEpisodes = []; - var episode_list = []; - var episodePresent; + if($scope.metadata.patient_list_comparators && + _.has($scope.metadata.patient_list_comparators, $routeParams.slug)){ + $scope.comparators = $injector.get( + $scope.metadata.patient_list_comparators[$routeParams.slug] + ); + } + } - visibleEpisodes = _.filter($scope.episodes, function(episode){ - return episodeVisibility(episode, $scope); - }); + $scope.compareEpisodes = function(p1, p2) { + if($scope.comparators){ + return p1.compare(p2, $scope.comparators); + }else{ + return p1.compare(p2); + } + }; - visibleEpisodes.sort($scope.compareEpisodes); + $scope.getVisibleEpisodes = function() { + var visibleEpisodes = []; + var episode_list = []; + var episodePresent; - if($scope.rows && visibleEpisodes.length){ - if($scope.episode){ - episodePresent = _.any(visibleEpisodes, function(x){ - return x.id === $scope.episode.id; - }); - } + visibleEpisodes = _.filter($scope.episodes, function(episode){ + return episodeVisibility(episode, $scope); + }); - if(!episodePresent){ - $scope.select_episode(visibleEpisodes[0], 0); - } + visibleEpisodes.sort($scope.compareEpisodes); + + if($scope.rows && visibleEpisodes.length){ + if($scope.episode){ + episodePresent = _.any(visibleEpisodes, function(x){ + return x.id === $scope.episode.id; + }); } - return visibleEpisodes; - }; - $scope.rows = $scope.getVisibleEpisodes(); + if(!episodePresent){ + $scope.select_episode(visibleEpisodes[0], 0); + } + } + return visibleEpisodes; + }; + + $scope.rows = $scope.getVisibleEpisodes(); + $scope.episode = $scope.rows[0]; + + $scope.ready = true; + + // + // Reload a single episode from the server. + // Useful for Pathway callbacks. + // + $scope.refresh = function(episode_id){ + episodeLoader(episode_id).then( + function(episode){ + $scope.episodes[episode_id] = episode; + if($scope.episode.id == episode_id){ + $scope.episode = episode; + } + $scope.rows = $scope.getVisibleEpisodes() + } + ) + } + + // + // This is used to be callable we can pass to + // the table row iterator in the spreadsheet template. + // + $scope.isSelectedEpisode = function(episode){ + return episode === $scope.episode; + } + + $scope.editTags = function(){ + $rootScope.state = 'modal'; + $scope.open_modal( + 'EditTeamsCtrl', + '/templates/modals/edit_teams.html', + {episode: $scope.episode} + ).then(function(){ + if(!$scope.episode.hasTag(pertinantTag)){ + delete $scope.episodes[$scope.episode.id]; + $scope.rows = _.filter($scope.rows, function(e){ + return e.id !== $scope.episode.id; + }); + } + $rootScope.state = 'normal'; $scope.episode = $scope.rows[0]; - - $scope.ready = true; - - // - // Reload a single episode from the server. - // Useful for Pathway callbacks. - // - $scope.refresh = function(episode_id){ - episodeLoader(episode_id).then( - function(episode){ - $scope.episodes[episode_id] = episode; - if($scope.episode.id == episode_id){ - $scope.episode = episode; + }); + }; + + $scope.jumpToEpisodeDetail = function(episode){ + $location.url(episode.link); + } + + $scope.jumpToTag = function(tag){ + if(_.contains(_.keys(metadata.tag_hierarchy), tag)){ + $location.path($scope.path_base + tag) + }else{ + for(var prop in metadata.tag_hierarchy){ + if(metadata.tag_hierarchy.hasOwnProperty(prop)){ + if(_.contains(_.values(metadata.tag_hierarchy[prop]), tag)){ + $location.path($scope.path_base + prop + '-' + tag) } - $scope.rows = $scope.getVisibleEpisodes() } - ) + } + } + }; + + $scope.$on('keydown', function(event, e) { + if ($rootScope.state == 'normal') { + switch (e.keyCode) { + case 191: // question mark + if(e.shiftKey){ + $scope.keyboard_shortcuts(); + } + break; + case 13: + if(profile.can_see_pid()){ + $scope.jumpToEpisodeDetail($scope.episode); + } + break; + case 38: // up + goUp(); + break; + case 40: // down + goDown(); + break; + } } + }); - // - // This is used to be callable we can pass to - // the table row iterator in the spreadsheet template. - // - $scope.isSelectedEpisode = function(episode){ - return episode === $scope.episode; - } + $scope.is_tag_visible_in_list = function(tag){ + return _.contains(metadata.tag_visible_in_list, tag); + }; - $scope.editTags = function(){ - $rootScope.state = 'modal'; - $scope.open_modal( - 'EditTeamsCtrl', - '/templates/modals/edit_teams.html', - {episode: $scope.episode} - ).then(function(){ - if(!$scope.episode.hasTag(pertinantTag)){ - delete $scope.episodes[$scope.episode.id]; - $scope.rows = _.filter($scope.rows, function(e){ - return e.id !== $scope.episode.id; - }); - } - $rootScope.state = 'normal'; - $scope.episode = $scope.rows[0]; - }); - }; - - $scope.jumpToEpisodeDetail = function(episode){ - $location.url(episode.link); - } + $scope.print = function() { + $window.print(); + }; - $scope.jumpToTag = function(tag){ - if(_.contains(_.keys(metadata.tag_hierarchy), tag)){ - $location.path($scope.path_base + tag) - }else{ - for(var prop in metadata.tag_hierarchy){ - if(metadata.tag_hierarchy.hasOwnProperty(prop)){ - if(_.contains(_.values(metadata.tag_hierarchy[prop]), tag)){ - $location.path($scope.path_base + prop + '-' + tag) - } - } - } - }; - } + $scope.focusOnQuery = function() { + $rootScope.state = 'search'; + }; - $scope.$watch('query.hospital_number', function() { - $scope.rows = $scope.getVisibleEpisodes(); - }); - - $scope.$watch('query.ward', function() { - $scope.rows = $scope.getVisibleEpisodes(); - }); - - $scope.$watch('query.bed', function() { - $scope.rows = $scope.getVisibleEpisodes(); - }); - - $scope.$watch('query.name', function() { - $scope.rows = $scope.getVisibleEpisodes(); - }); - - $scope.$on('keydown', function(event, e) { - if ($rootScope.state == 'normal') { - switch (e.keyCode) { - case 191: // question mark - if(e.shiftKey){ - $scope.keyboard_shortcuts(); - } - break; - case 13: - if(profile.can_see_pid()){ - $scope.jumpToEpisodeDetail($scope.episode); - } - break; - case 38: // up - goUp(); - break; - case 40: // down - goDown(); - break; - case 78: // n - $scope.addEpisode(); - } - } - }); - - $scope.getRowIxFromEpisodeId = function(episodeId) { - for (var rix = 0; rix < $scope.rows.length; rix++) { - if ($scope.rows[rix].id == episodeId) { - return rix; - } - }; - return -1; - }; - - function getEpisode(rix) { - return $scope.rows[rix]; - }; - - $scope.print = function() { - $window.print(); - }; - - $scope.focusOnQuery = function() { - $rootScope.state = 'search'; - }; - - $scope.blurOnQuery = function() { - $rootScope.state = 'normal'; - }; - - $scope.addEpisode = function() { - if(profile.readonly){ return null; }; - - var enter = Flow.enter( - { - current_tags: { - tag: $scope.currentTag, - subtag: $scope.currentSubTag - } - }, - $scope - ); - - $rootScope.state = 'modal'; - enter.then( - function(resolved) { - // We have either retrieved an existing episode or created a new one, - // rendered a new modal for which we are waiting, - // or has cancelled the process at some point. - - var return_to_normal = function(episode){ - // This ensures that the relevant episode is added to the table and - // selected. - var rowIx; - $rootScope.state = 'normal'; - if (episode && episode != 'cancel') { - // - // Occasionally the addPatient modal will add an episode to a list we're - // not currently on. So we check to see if they're tagged to this list. - // - if(episode.tagging[0][$scope.currentTag]){ - if(!$scope.currentSubTag || episode.tagging[0][$scope.currentSubTag]){ - $scope.episodes[episode.id] = episode; - $scope.rows = $scope.getVisibleEpisodes(); - rowIx = $scope.getRowIxFromEpisodeId(episode.id); - $scope.num_episodes += 1; - } - } - var msg = episode.demographics[0].first_name + " " + episode.demographics[0].surname; - var newTags = _.intersection(_.keys(episode.tagging[0]), _.keys(metadata.tags)); - var newTag; - - if(newTags.length > 1){ - var tagObjs = _.filter(metadata.tags, function(t){ return _.contains(newTags, t.name); }); - if($scope.currentSubTag.length){ - newTag = _.findWhere(tagObjs, {parent_tag: $scope.currentTag}).name; - } - else{ - newTag = _.findWhere(tagObjs, {name: $scope.currentTag}).name; - } - } - else{ - newTag = newTags[0]; - } - msg += " added to the " + metadata.tags[newTag].display_name + " list"; - growl.success(msg); - - } - }; - if(resolved && resolved.then){ // OMG - it's a promise! - resolved.then( - function(r){ return_to_normal(r) }, - function(r){ return_to_normal(r) } - ); - }else{ - return_to_normal(resolved); - } - }, - function(reason){ - // The modal has been dismissed. Practically speaking this means - // that the Angular UI called dismiss rather than our cancel() - // method on the OPAL controller. We just need to re-set in order - // to re-enable keybard listeners. - $rootScope.state = 'normal'; - }); - }; - - $scope.removeFromList = function(episode_id){ - delete $scope.episodes[episode_id]; - $scope.rows = $scope.getVisibleEpisodes(); - $scope.num_episodes -= 1; - $scope.episode = $scope.rows[0]; - } + $scope.blurOnQuery = function() { + $rootScope.state = 'normal'; + }; - $scope.newNamedItem = function(episode, name) { - return episode.recordEditor.newItem(name); - }; - - $scope.is_tag_visible_in_list = function(tag){ - return _.contains(metadata.tag_visible_in_list, tag); - }; - - $scope.editNamedItem = function(episode, name, iix) { - var reset_state = function(result){ - if (name == 'tagging') { - // User may have removed current tag - $scope.rows = $scope.getVisibleEpisodes(); - } - var item = _.last(episode[name]); - - if (episode[name].sort){ - episode.sortColumn(item.columnName, item.sort); - } - }; - - if(iix === episode[name].length){ - episode.recordEditor.newItem(name); - } - else{ - var item = episode[name][iix]; + $scope.$watch('query.hospital_number', function() { + $scope.rows = $scope.getVisibleEpisodes(); + }); - episode.recordEditor.editItem(name, item).then(function(result){ - reset_state(result); - }); - } - }; - - function goUp() { - var episode; - if ($scope.rix > 0) { - $scope.rix--; - $scope.episode = getEpisode($scope.rix); - }; - }; - - function goDown() { - var episode = getEpisode($scope.rix); - if ($scope.rix < $scope.rows.length - 1) { - $scope.rix++; - $scope.episode = $scope.rows[$scope.rix]; - }; - }; - - $scope.select_episode = function(episode, rix){ - $scope.episode = episode; - $scope.rix = rix; + $scope.$watch('query.ward', function() { + $scope.rows = $scope.getVisibleEpisodes(); + }); + + $scope.$watch('query.bed', function() { + $scope.rows = $scope.getVisibleEpisodes(); + }); + + $scope.$watch('query.name', function() { + $scope.rows = $scope.getVisibleEpisodes(); + }); + + + + + $scope.removeFromList = function(episode_id){ + delete $scope.episodes[episode_id]; + $scope.rows = $scope.getVisibleEpisodes(); + $scope.num_episodes -= 1; + $scope.episode = $scope.rows[0]; + } + + + $scope.newNamedItem = function(episode, name) { + return episode.recordEditor.newItem(name); + }; + + + $scope.editNamedItem = function(episode, name, iix) { + var reset_state = function(result){ + if (name == 'tagging') { + // User may have removed current tag + $scope.rows = $scope.getVisibleEpisodes(); } + var item = _.last(episode[name]); - $scope.keyboard_shortcuts = function(){ - $modal.open({ - controller: "KeyBoardShortcutsCtrl", - templateUrl: 'list_keyboard_shortcuts.html' - }) + if (episode[name].sort){ + episode.sortColumn(item.columnName, item.sort); } - }); + }; + + if(iix === episode[name].length){ + episode.recordEditor.newItem(name); + } + else{ + var item = episode[name][iix]; + + episode.recordEditor.editItem(name, item).then(function(result){ + reset_state(result); + }); + } + }; + + function getEpisode(rix) { + return $scope.rows[rix]; + }; + + function goUp() { + var episode; + if ($scope.rix > 0) { + $scope.rix--; + $scope.episode = getEpisode($scope.rix); + }; + }; + + function goDown() { + var episode = getEpisode($scope.rix); + if ($scope.rix < $scope.rows.length - 1) { + $scope.rix++; + $scope.episode = $scope.rows[$scope.rix]; + }; + }; + + $scope.select_episode = function(episode, rix){ + $scope.episode = episode; + $scope.rix = rix; + } + + $scope.keyboard_shortcuts = function(){ + $modal.open({ + controller: "KeyBoardShortcutsCtrl", + templateUrl: 'list_keyboard_shortcuts.html' + }) + } + }); diff --git a/opal/static/js/opal/services/flow.js b/opal/static/js/opal/services/flow.js deleted file mode 100644 index cd6273a38..000000000 --- a/opal/static/js/opal/services/flow.js +++ /dev/null @@ -1,75 +0,0 @@ -angular.module( - 'opal.services' -).factory( - 'Flow', - function($q, $http, $modal, $cacheFactory, $injector){ - "use strict"; - var get_flow_service = function(){ - var OPAL_FLOW_SERVICE = $injector.get('OPAL_FLOW_SERVICE'); - if(OPAL_FLOW_SERVICE){ - return $injector.get(OPAL_FLOW_SERVICE); - }else{ - return { - enter: function(){ - return { - 'controller': 'HospitalNumberCtrl', - 'template' : '/templates/hospital_number_modal.html' - }; - }, - exit: function(){ - return { - 'controller': 'DischargeEpisodeCtrl', - 'template' : '/templates/discharge_episode_modal.html' - }; - } - }; - } - } - - - var Flow = { - - enter: function(config, context){ - var deferred = $q.defer(); - var target = get_flow_service().enter(); - var result = $modal.open({ - backdrop: 'static', - templateUrl: target.template, - controller: target.controller, - resolve: { - referencedata: function(Referencedata){ return Referencedata.load() }, - metadata: function(Metadata){ return Metadata.load(); }, - tags: function(){ return config.current_tags}, - hospital_number: function(){ return config.hospital_number; }, - context: function(){ return context; } - } - }).result; - deferred.resolve(result); - return deferred.promise; - }, - - exit: function(episode, config, context){ - var deferred = $q.defer(); - var target = get_flow_service().exit(episode) - var result = $modal.open({ - backdrop: 'static', - templateUrl: target.template, - controller: target.controller, - keyboard: false, - resolve: { - episode : function() { return episode; }, - referencedata: function(Referencedata){ return Referencedata.load() }, - metadata : function(Metadata){ return Metadata.load(); }, - tags : function() { return config.current_tags; }, - context : function(){ return context; } - } - }).result; - - deferred.resolve(result); - return deferred.promise; - } - - }; - return Flow; - } -); diff --git a/opal/static/js/test/field_translate.service.test.js b/opal/static/js/test/field_translate.service.test.js index 7c41fb298..8dd6e1aae 100644 --- a/opal/static/js/test/field_translate.service.test.js +++ b/opal/static/js/test/field_translate.service.test.js @@ -88,7 +88,7 @@ describe('services', function() { expect(result.demographics).toEqual(patientData.demographics[0]); }); - it('should handle single empty strings', function(){ + describe('should handle single empty strings', function(){ it("should remove the spaces from around ints and floats", function(){ jsPatientData.demographics.age = ""; jsPatientData.demographics.weight = ""; diff --git a/opal/static/js/test/flow.service.test.js b/opal/static/js/test/flow.service.test.js deleted file mode 100644 index f52660bf9..000000000 --- a/opal/static/js/test/flow.service.test.js +++ /dev/null @@ -1,150 +0,0 @@ -// -// Unit tests for our Flow Service -// - -describe('Flow ', function(){ - "use strict"; - var $httpBackend, $modal, $rootScope; - var options, Flow, Referencedata; - var metadata = {load: function(){}}; - var referencedata = {load: function(){}}; - var mock_flow_service; - - beforeEach(function(){ - - module('opal.services'); - module('opal.controllers'); - spyOn(metadata, "load").and.returnValue("some metadata"); - spyOn(referencedata, "load").and.returnValue("some reference data"); - mock_flow_service = null; - - module(function($provide){ - $provide.value('OPAL_FLOW_SERVICE', mock_flow_service); - }); - - inject(function($injector){ - Flow = $injector.get('Flow'); - $modal = $injector.get('$modal'); - $rootScope = $injector.get('$rootScope'); - $httpBackend = $injector.get('$httpBackend'); - }); - - spyOn($modal, 'open').and.returnValue({result: null}); - }); - - describe('enter', function(){ - it('should call the modal with the enter flow', function(){ - Flow.enter({hospital_number: '555-456'}, {some: "context"}); - var args = $modal.open.calls.mostRecent().args; - expect(args[0].controller).toEqual('HospitalNumberCtrl'); - expect(args[0].templateUrl).toEqual('/templates/hospital_number_modal.html'); - var resolves = args[0].resolve; - expect(resolves.tags()).toEqual(undefined); - expect(resolves.hospital_number()).toEqual('555-456'); - expect(resolves.referencedata(referencedata)).toEqual("some reference data"); - expect(resolves.metadata(metadata)).toEqual("some metadata"); - expect(resolves.context()).toEqual({some: "context"}); - }); - }); - - describe('exit', function(){ - it('should call exit', function(){ - Flow.exit('episode', {current_tags: {}}, {some: "context"}); - var args = $modal.open.calls.mostRecent().args; - expect(args[0].controller).toEqual('DischargeEpisodeCtrl'); - expect(args[0].templateUrl).toEqual('/templates/discharge_episode_modal.html'); - var resolves = args[0].resolve; - expect(resolves.tags()).toEqual({}); - expect(resolves.episode()).toEqual('episode'); - expect(resolves.referencedata(referencedata)).toEqual("some reference data"); - expect(resolves.metadata(metadata)).toEqual("some metadata"); - - expect(resolves.context()).toEqual({some: "context"}); - }); - }); -}); - - -/// -/// For reasons beyond my comprehension, it seems impossible to -/// re-set these mocks/injectors/providers within the same describe block, so we -/// are starting again with a fresh context. -/// -describe('Flow ', function(){ - "use strict"; - var $httpBackend, $modal, $rootScope; - var options, Flow, Referencedata; - var metadata = {load: function(){}}; - var referencedata = {load: function(){}}; - var mock_flow_service; - - beforeEach(function(){ - - module('opal.services'); - module('opal.controllers'); - spyOn(metadata, "load").and.returnValue("some metadata"); - spyOn(referencedata, "load").and.returnValue("some reference data"); - mock_flow_service = { - enter: jasmine.createSpy().and.returnValue( - { - 'controller': 'HospitalNumberCtrl', - 'template' : '/templates/hospital_number_modal.html' - } - ), - exit: jasmine.createSpy().and.returnValue( - { - 'controller': 'DischargeEpisodeCtrl', - 'template' : '/templates/discharge_episode_modal.html' - } - ) - }; - - module(function($provide){ - $provide.value('TestCustomFlowService', mock_flow_service); - $provide.value('OPAL_FLOW_SERVICE', 'TestCustomFlowService'); - }); - - inject(function($injector){ - Flow = $injector.get('Flow'); - $modal = $injector.get('$modal'); - $rootScope = $injector.get('$rootScope'); - $httpBackend = $injector.get('$httpBackend'); - }); - - }); - - - describe('with custom service', function() { - - describe('enter', function() { - it('should call the custom service', function() { - Flow.enter({hospital_number: '555-456'}, {some: "context"}); - expect(mock_flow_service.enter).toHaveBeenCalled(); - }); - - }); - - describe('exit', function() { - - it('should call the custom service', function() { - Flow.exit({hospital_number: '555-456'}, {some: "context"}); - expect(mock_flow_service.exit).toHaveBeenCalled(); - }); - - it('should resolve the promise with the result of the modal', function(){ - spyOn($modal, 'open').and.returnValue({result: "discharged"}); - var exitPromise = Flow.exit({hospital_number: '555-456'}, {some: "context"}); - var expected; - - exitPromise.then(function(x){ - expected = x; - }); - $rootScope.$apply(); - expect(expected).toBe('discharged'); - }); - - }); - - - }); -}); diff --git a/opal/static/js/test/patient_detail.controller.test.js b/opal/static/js/test/patient_detail.controller.test.js index 0c728de30..2e05e8ddf 100644 --- a/opal/static/js/test/patient_detail.controller.test.js +++ b/opal/static/js/test/patient_detail.controller.test.js @@ -2,7 +2,7 @@ describe('PatientDetailCtrl', function(){ "use strict"; var $scope, $rootScope, $controller, $modal, $routeParams, $httpBackend; - var Flow, patientLoader, Episode, opalTestHelper; + var patientLoader, Episode, opalTestHelper; var patient, controller, metadata, profile; var mkcontroller; @@ -28,15 +28,10 @@ describe('PatientDetailCtrl', function(){ metadata = opalTestHelper.getMetaData(); profile = opalTestHelper.getUserProfile(); - Flow = {exit: jasmine.createSpy().and.returnValue({ - then: function(fn){ fn(); } - }) }; - mkcontroller = function(patient){ controller = $controller('PatientDetailCtrl', { $scope : $scope, $routeParams : $routeParams, - Flow : Flow, patient : patient, profile : profile, metadata : metadata, diff --git a/opal/static/js/test/patient_list.controller.test.js b/opal/static/js/test/patient_list.controller.test.js index 9b3085c5c..7c88c1950 100644 --- a/opal/static/js/test/patient_list.controller.test.js +++ b/opal/static/js/test/patient_list.controller.test.js @@ -6,7 +6,7 @@ describe('PatientListCtrl', function() { var $scope, $cookies, $controller, $q, $dialog, $httpBackend; var $$injector; var $location, $routeParams, $http; - var Flow, opalTestHelper; + var opalTestHelper; var episodedata, controller; var $modal, metadata, $rootScope; @@ -38,7 +38,6 @@ describe('PatientListCtrl', function() { $routeParams = $injector.get('$routeParams'); $httpBackend = $injector.get('$httpBackend'); $location = $injector.get('$location'); - Flow = $injector.get('Flow'); $$injector = $injector.get('$injector'); episodeVisibility = $injector.get('episodeVisibility'); opalTestHelper = $injector.get('opalTestHelper'); @@ -83,7 +82,6 @@ describe('PatientListCtrl', function() { $window : fakeWindow, $injector : $$injector, growl : growl, - Flow : Flow, episodedata : episodedata, profile : profile, metadata : md, @@ -117,7 +115,7 @@ describe('PatientListCtrl', function() { expect($rootScope.state).toBe("normal"); }); - it('should leave the episode if it does have the pertinant tag', function(){ + describe('should leave the episode if it does have the pertinant tag', function(){ it('should filter an episode if the episode does not have the same tags', function(){ // imitate the case where we remove all the tags $scope.episodes[episode.id] = episode; @@ -423,24 +421,6 @@ describe('PatientListCtrl', function() { }); - describe('n', function() { - - it('should addEpisode()', function() { - spyOn($scope, 'addEpisode'); - $scope.$broadcast('keydown', { keyCode: 78 }); - expect($scope.addEpisode).toHaveBeenCalledWith(); - }); - - }); - - }); - - describe('getRowIxFromEpisodeId()', function() { - - it('should return -1 if the id is not an episode we know about', function() { - expect($scope.getRowIxFromEpisodeId(73872387)).toEqual(-1) - }); - }); describe('print()', function() { @@ -471,149 +451,6 @@ describe('PatientListCtrl', function() { }); - describe('adding an episode', function() { - var fake_episode_resolver = function(){ - return {then : function(fn){ fn(new Episode(episodeData2)); }}; - }; - - it('should call flow', function() { - spyOn(Flow, 'enter').and.callFake(fake_episode_resolver); - $scope.addEpisode(); - expect(Flow.enter).toHaveBeenCalledWith( - { - current_tags: { - tag: $scope.currentTag, - subtag: $scope.currentSubTag - } - }, - $scope - ); - }); - - it('should allow the enter flow to resolve with a promise', function() { - $scope.currentTag = 'mine'; - spyOn(Flow, 'enter').and.callFake( - function(){ - return { - then : function(fn){ fn({ - then: function(fn){ fn(new Episode(episodeData) ) } - })} - } - } - ); - $scope.addEpisode(); - expect(Flow.enter).toHaveBeenCalledWith({current_tags: { - tag: $scope.currentTag, - subtag: $scope.currentSubTag - }}, $scope); - - expect(growl.success).toHaveBeenCalledWith('John Smith added to the Mine list'); - }); - - it('should print the correct message even dependent on the current tag', function(){ - $scope.currentTag = 'tropical'; - spyOn(Flow, 'enter').and.callFake( - function(){ - return { - then : function(fn){ fn({ - then: function(fn){ fn(new Episode(episodeData) ) } - })} - } - } - ); - $scope.addEpisode(); - expect(growl.success).toHaveBeenCalledWith('John Smith added to the Tropical list'); - }); - - it('should print the correct message even in the case of multiple tags with hierarchies on the current tag', function(){ - $scope.currentTag = 'opat'; - $scope.currentSubTag = 'opat_referrals'; - var episodeData3 = angular.copy(episodeData); - episodeData3.tagging = [ - {opat: true, opat_referrals: true, mine: true} - ]; - spyOn(Flow, 'enter').and.callFake( - function(){ - return { - then : function(fn){ fn({ - then: function(fn){ fn(new Episode(episodeData3) ) } - })} - } - } - ); - $scope.addEpisode(); - expect(growl.success).toHaveBeenCalledWith( - 'John Smith added to the OPAT Referral list'); - }); - - it('should add the new episode to episodes if it has the current tag', function() { - spyOn(Flow, 'enter').and.callFake(fake_episode_resolver); - expect($scope.rows.length).toBe(1); - $scope.addEpisode(); - expect($scope.rows.length).toBe(2); - }); - - it('should not add the new episode to episodes if it does not have the current tag', - function() { - episodeData2.tagging = [{'mine': true, 'id_inpatients': true}]; - spyOn(Flow, 'enter').and.callFake(fake_episode_resolver); - expect($scope.rows.length).toBe(1); - $scope.addEpisode(); - expect($scope.rows.length).toBe(1); - }); - - describe('for a readonly user', function(){ - beforeEach(function(){ - profile.readonly = true; - }); - - it('should return null', function(){ - expect($scope.addEpisode()).toBe(null); - }); - - afterEach(function(){ - profile.readonly = false; - }); - }); - - describe('When a promise returned by flow is rejected', function() { - it('should reset the state', function() { - spyOn(Flow, 'enter').and.callFake( - function(){ - return { - then : function(cb, eb){ cb( - { - then: function(cb, eb){ - eb(); - } - } - ) } - } - } - ); - $scope.addEpisode() - expect($rootScope.state).toEqual('normal'); - }); - - }); - - describe('When the modal is dismissed', function() { - - it('should reset the state', function() { - spyOn(Flow, 'enter').and.callFake( - function(){ - return { - then : function(cb, eb){ eb() } - } - } - ); - $scope.addEpisode() - expect($rootScope.state).toEqual('normal'); - }); - - }); - }); - describe('removeFromList', function() { it('should remove the episode from the list', function() { $scope.removeFromList(123); diff --git a/opal/static/js/test/record_editor_test.js b/opal/static/js/test/record_editor_test.js index 98a7bfa3e..27347a2f8 100644 --- a/opal/static/js/test/record_editor_test.js +++ b/opal/static/js/test/record_editor_test.js @@ -3,7 +3,7 @@ describe('RecordEditor', function(){ var $scope, $modal, $routeParams; var $rootScope, $q, $controller; - var Flow, Episode, episode; + var Episode, episode; var controller, UserProfile; var opalTestHelper; var profile, $log; diff --git a/opal/templates/patient_lists/layouts/card_list_base.html b/opal/templates/patient_lists/layouts/card_list_base.html index 39da6da9d..8e08febf8 100644 --- a/opal/templates/patient_lists/layouts/card_list_base.html +++ b/opal/templates/patient_lists/layouts/card_list_base.html @@ -43,11 +43,6 @@

There are no patients on this list.

-

- - Would you like to {% block add_patient_if_none %}add one?{% endblock %} - -

diff --git a/opal/templates/patient_lists/layouts/spreadsheet_list_base.html b/opal/templates/patient_lists/layouts/spreadsheet_list_base.html index c06b20121..c128a3788 100644 --- a/opal/templates/patient_lists/layouts/spreadsheet_list_base.html +++ b/opal/templates/patient_lists/layouts/spreadsheet_list_base.html @@ -9,14 +9,6 @@

{% include "patient_lists/partials/list_dropdown_menu.html" %} - {% if not user.profile.explicit_access_only %} - - {% endif %}

@@ -204,11 +196,6 @@

There are no patients on this list.

-

- - Would you like to {% block add_patient_if_none %}add one?{% endblock %} - -