Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Feature patient history #66

Merged
merged 39 commits into from

3 participants

@kbl
Collaborator

Not fully functional pull request mainly for code review sake.

I have some cases which aren't fully clear for me:

  • could user be "checked in" in many treatment areas?
  • if so which one should be presented on assignmet desk (which readio button should be checked?)
  • what should I do with assignments when admin is about to 'reset distribution'?
@jordanbyron
Owner

Hey Marcin,

Here are the answers to your questions:

could user be "checked in" in many treatment areas?

I think we might be using the same term, checked in, for different meanings. When a patient is checked in, that just means a Patient record has been generated and they are given a "chart number" aka Patient#id. Then they go to the Assignment Desk where they are routed to one Treatment Area. So at any given time, patients should only be assigned to one treatment area.

That might seem weird, but we need to keep the relationship a one to many because radiology is an exception. Some patients require x-rays be taken before they go onto their final treatment area. In those cases, they are assigned to two treatment areas: radiology and any other treatment area. I know that's confusing, and if you need me to explain a bit better just let me know.

if so which one should be presented on assignmet desk (which readio button should be checked?

The radio button should show the treatment area assignment which the patient hasn't been check out of and is not radiology. The radiology check box should be checked if an assignment exists for radiology that hasn't been checked out.

what should I do with assignments when admin is about to 'reset distribution'?

Good question. Let me explain the purpose for that function first: Reset Distribution was created because not all patients which check in actually go through the entire clinic and check out. Some have to leave, others just get tired of waiting. So at the end of the first day, we want to reset our reports so those assignments are back to zero.

Now we might be able to fix this by scoping those reports to a given day. So show all PatientAssignment records created on a provided date (which should default to today). Then we can remove the Reset Distribution function entirely. Sound good?


I hope to check out your code sometime today, but I've got a few other things to do first. Thanks for working on this!!! :thumbsup:

Gemfile
@@ -19,10 +19,11 @@ gem 'jquery-rails'
group :development do
gem 'rainbow'
- gem 'progress_bar', :require => false
- gem 'highline', :require => false
- gem 'faker', :require => false
- gem 'rubyzip', :require => false
+ gem 'progress_bar', :require => false
+ gem 'highline', :require => false
+ gem 'faker', :require => false
+ gem 'rubyzip', :require => false
+ gem 'spork-testunit', :require => false
@jordanbyron Owner

Why did you include spork-testunit as a development dependency? In general I try to keep all the dependencies to a minimum.

@kbl Collaborator
kbl added a note

Good point. I thought that it wouldn't be possible to run spork without such dependency in dev scope which seems to be false.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
app/controllers/admin/patient_assignments_controller.rb
((2 lines not shown))
+ class PatientAssignmentsController < ApplicationController
+
+ before_filter :admin_required, :set_current_tab
+
+ def show
+ @patient = Patient.find(params[:patient_id])
+ end
+
+ private
+
+ def set_current_tab
+ @current_tab = 'patients'
+ end
+
+ end
+end
@jordanbyron Owner

Adding a new controller is probably overkill for just one view. I think an action in the existing Admin::PatientsController would do the trick. Something like Admin::PatientsController#history

@kbl Collaborator
kbl added a note

There is no such thing as Admin::PatientsController, I assume that you thought about 'plain' PatientsController (without Admin namespace). Talking about latter - on the one hand you are right and separate controller seems to be redundant, but on the other hand now PatientsController has ~ 150 lines which in my opinion is far from skinny so I created separated one.

Or maybe I misunderstood you and you were talking about renaming Admin::PatientAssigmentsController to Admin::PatientsController (thus having 2 separate PatientsController's in separate namespaces)?

@jordanbyron Owner

There is no such thing as Admin::PatientsController

Ahhh you are right. I'm sorry about that. Let's create an Admin::PatientsController and make sure we move the edit, update, and destroy methods from PatientsController to Admin::PatientsController. That is how I would do it today and it simplifies both controllers.

Admin::PatientAssigmentsController doesn't exist right? Because I don't know what that controller would do. We only need to display patient assignments in Admin::PatientsController#history

@kbl Collaborator
kbl added a note

Done, edit, destroy, history moved to Admin::PatientsController. During testing I've noticed error (same happens also on master branch): https://gist.github.com/1627050

Fix it on this feature branch or maybe in separate pull request?

@jordanbyron Owner

During testing I've noticed error (same happens also on master branch): https://gist.github.com/1627050

File a ticket for the error and give me a bit more context. IE where exactly that error is happening, etc. Thanks :smile:

@kbl Collaborator
kbl added a note

Issue created, details provided in ticket - #69

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
app/controllers/assignment_desk_controller.rb
@@ -14,11 +14,21 @@ def edit
def update
patient = Patient.find(params[:id])
-
- patient.update_attributes(params[:patient])
+ patient.update_attributes({ radiology: params[:patient][:radiology] })
@jordanbyron Owner

This is going to be tricky, but I want to remove the radiology column from Patient and instead create a PatientAssignment for radiology. That way we can track when they went through the radiology department and display that on their history page.

It's tricky because radiology is a special case since it is the only treatment area which patients can be routed to in addition to their final destination. I explained this a bit in my initial response so I would get into it again, but we may have to unpack the best way to tackle this problem together.

@kbl Collaborator
kbl added a note

To be honest I was thinking about same thing during weekend and want to suggest this change (;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
app/controllers/assignment_desk_controller.rb
((8 lines not shown))
flash[:notice] = 'Patient was successfully assigned.'
redirect_to patients_path
end
+
+ private
+
+ def check_in(patient)
@jordanbyron Owner

Since we are only using this check_in method once it probably doesn't need to exist. Just make sure Patient#check_in doesn't fail if given a nil value for area.

So line 18 would look like:

patient.check_in TreatmentArea.find(params[:patient][:checked_in_at])
@kbl Collaborator
kbl added a note

Such line will cause error if params[:patient][:checked_in_at].nil? == true (ActiveRecord::RecordNotFound: Couldn't find MODEL without an ID). So I could do it slightly different by passing id not actual object:

patient.check_in(params[:patient][:checked_in_at])
@jordanbyron Owner

Such line will cause error

You're right. Change that to:

patient.check_in TreatmentArea.find_by_id(params[:patient][:checked_in_at])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
app/models/patient.rb
@@ -103,16 +106,26 @@ def procedures_grouped
end
def check_out(area)
+ raise "Can't check out" if area != checked_in_at
@jordanbyron Owner

We don't want to do this. Patients can be checked out even if they haven't been assigned to a treatment area.

@kbl Collaborator
kbl added a note

Sure, I wasn't certain about this before your initial comment on this pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
app/models/patient.rb
((16 lines not shown))
end
end
+ def check_in(area)
@jordanbyron Owner

The name of this method is wrong. It should probably be assign since that is what it does: It assigns a patient to a given area.

@kbl Collaborator
kbl added a note

Good point. I always struggle, among others, with proper naming ):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
app/models/patient.rb
((16 lines not shown))
end
end
+ def check_in(area)
+ raise 'Already checked in!' if checked_in_at
@jordanbyron Owner

Again we don't want to raise errors if a patient is already assigned to an area. Patients can be re-routed several times throughout their visit, and the system should allow that

@kbl Collaborator
kbl added a note

So lets assume following case:

  1. Check in Radiology & Surgery - creating assignment #1 (#a1 surgery) and #a2 (radiology)
  2. Check in Hygiene - creating #a3
  3. Check in Surgery - creating #a4
  4. Check out Surgery

Is it valid? If so:

  • on which assignment check_out_at should be set? #a1? #a4? or maybe both of them?
  • which of them #a1/#a4 or #a3 should be showed in assignment desk (which radio button should be checked)?
@jordanbyron Owner

Let's make sure we stop using the phrase "Check in" when we really mean "Assigned". It just confuses these discussions and I want to make sure we are both talking about the same thing. To recap: Patients are "Checked in" when they first come to the clinic and their Patient record is created. They are then "Assigned" to a TreatementArea

So the only way a patient is assigned is through the assignment desk: AssignmentDeskController#update. There are two types of scenarios which the assignment desk needs to handle:

  1. A patient hasn't been assigned to any treatment area (patient. assignments.not_checked_out.empty? == true)
  2. A patient is already assigned to a treatment area and possibly radiology

For the first case, all the radio boxes and the radiology box are unchecked when the form is generated. Then the assignment desk volunteer checks one of the treatment area radio boxes and possibly the radiology check box. In that case, a PatientAssignment record is created for the selected TreatmentArea and, if the radiology check box is selected, one for TreatmentArea.radiology.

The second case we need to potentially change where that patient is assigned. So if "John Smith" was originally assigned to "Surgery", but then re-assigned to "Hygiene", his Patient#assign method would first check if he has an existing, open assignment:

def assign(treatment_area)
  return unless treatement_area
  assignment = assignments.not_checked_out.not_radiology.first
  assignment ||= assignments.build

  assignment.treatment_area = treatment_area
  assignment.save
end

So in this case we would never have more than one "non-radiology" treatment area assignment open at a time. And in the end, Patient#check_out would set check_out_at on all open assignments.

NOTE: Patients should be "checked out" of radiology via PatientsController#export_to_dexis_file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
app/models/patient.rb
((16 lines not shown))
end
end
+ def check_in(area)
+ raise 'Already checked in!' if checked_in_at
+ assignments.create(treatment_area: area)
+ end
+
+ def checked_in_at
@jordanbyron Owner

This too is probably not the right name for the data it returns. But more importantly I don't think we need this method.

@kbl Collaborator
kbl added a note

+1 to name.

This method is used in 2 places:

  • in views to check valid radio button (showing to which TreatmentArea user is subscribed)
  • in tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
app/models/patient_assignment.rb
@@ -0,0 +1,11 @@
+class PatientAssignment < ActiveRecord::Base
+
+ belongs_to :patient
+ belongs_to :treatment_area
+
+ def check_out
+ self[:checked_out_at] = Time.now
+ save
@jordanbyron Owner

Those two lines can be replaced with:

update_attribute(:checked_out_at, Time.now)
@kbl Collaborator
kbl added a note

Good point!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
config/routes.rb
@@ -33,6 +33,7 @@
resources :assignment_desk
match '/patients/:id/print' => 'patients#print', :as => :print_chart
match '/patients/:patient_id/export' => 'patients#export_to_dexis_file', :as => :export_to_dexis_file
+ match '/patients/:patient_id/history' => 'admin/patient_assignments#show', :as => :patient_history
@jordanbyron Owner

This route should be in the /admin namespace

@kbl Collaborator
kbl added a note

+1
I've some problems with this so I've put it outside this namespace, I'll change this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
test/test_helper.rb
((2 lines not shown))
-require File.expand_path('../../config/environment', __FILE__)
-require 'rails/test_help'
-
-module TestHelper
- extend self
-
- # December, 26, 1985 + a user-defined time
- def clinic_date(time=nil)
- date = "12/26/1985"
-
- if time
- m, d, y = date.split('/')
- Time.parse "#{[y, m, d] * '-'} #{time}"
- else
- Date.strptime(date, "%m/%d/%Y")
+require 'spork'
@jordanbyron Owner

I asked above why you introduced spork, and if you wanted to add a new test dependency to the project that is something we should talk about first. Any new infrastructure you add to the project I have to learn and maintain.

Additionally, adding that new dependency in with a feature complicates matters as this branch is now doing two things:

  1. Adding patient history to the project
  2. Introducing a new test dependency (Spork)

Please remove spork from this branch, and if you want to discuss adding it to the project create a new ticket in the issue tracker.

@kbl Collaborator
kbl added a note

I'll remove this dependency just before sending final pull request ok? Such dependency speeds up a little bit testing.

@practicingruby Collaborator

For maintenance reasons, it's important to be careful not to introduce things like this mixed in with functional changes. It's reasonable to suggest things that will improve a project's testing environment, but just mixing in tools that you like to use in a functional patch is bad form. As a general rule of thumb for contributing to all open source projects: isolate environment changes from functional changes, and don't be surprised if environment changes are met with "no thanks" :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
test/unit/patient_assignment_test.rb
@@ -0,0 +1,10 @@
+require 'test_helper'
+
+class PatientAssignmentTest < ActiveSupport::TestCase
+
+ def test_factory_should_build_valid_assignment
+ a = Factory.build(:patient_assignment)
+ assert a.valid?
+ end
@jordanbyron Owner

Maybe this is just a placeholder, but this test doesn't test anything specific about the project. All it's testing is that our factories are setup and satisfy validation requirements. Not something we need to be testing.

@kbl Collaborator
kbl added a note

I'm going to fill this with appropriate test cases, but in my opinion such test case if it's failing points out exact place in code causing problems (e.g. adding some validations to PatientAssignment causing other tests to fail)

@practicingruby Collaborator

While I suppose that's reasonable, any time I've introduced a validations-related problem into my tests it's been immediately obvious what the problem was. Have you been in a situation where it was difficult to find the source of the issue without this kind of test? If so, can you provide an example?

@kbl Collaborator
kbl added a note

Assuming that we are doing TDD described situation couldn't happen, in such case this kind of tests are redundant - I've just removed it in my local repo.

@practicingruby Collaborator

Even if you're not doing strict TDD, it's reasonable to assume that you're at least running the tests before committing anything. That means it'd be easy to track down problems to a particular commit. Assuming that something did fly past and break the build, git bisect would find problems like these easily.

I'm on the fence about this. It does a bit more than just testing the framework, but it's such a trivial test that its results might be obvious anyway. What it does do is make the issue clearer, but it also feels pretty pedantic :)

Thanks for explaining, anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jordanbyron
Owner

@kbl I went through most of your code and gave you a TON of feedback :grin: Please don't be overwhelmed I think most of it is fairly easy stuff. If anything doesn't make sense just let me know. Also don't be afraid to ask @sandal or @semmons99 for feedback on your code. Thanks for working on this :smile:

@kbl
Collaborator

I'll create new pull request with refactored code

@kbl kbl closed this
@jordanbyron
Owner

@kbl you didn't need to close this request. Any new commits you push to your fork are automatically added to this pull request. Plus I haven't responded to a bunch of your comments and we'd have to start over on a new request.

I'm going to re-open this pull request and close the new one you sent over so we don't have to start over again from scratch.

@jordanbyron jordanbyron reopened this
@jordanbyron jordanbyron referenced this pull request
Closed

Feature patient history #68

@kbl
Collaborator

Please take a look, I hope that now it's ready to be merged (:

@jordanbyron
Owner

@kbl thanks for kicking butt on this. Hopefully I'll have some time today to look at this and if we're close I'll merge it into master. :tada:

@jordanbyron jordanbyron commented on the diff
app/views/assignment_desk/edit.html.haml
((8 lines not shown))
%h4 Treatment Areas
%p
- - @areas.reject {|a| a.radiology? }.each do |area|
- = f.radio_button :assigned_treatment_area_id, area.id
- = f.label :assigned_treatment_area_id, area.name, :value => area.id
+ - @areas.reject {|ar| ar.radiology? }.each do |area|
+ = radio_button :patient_params, :assigned_to, area.id, checked: @patient.assigned_to?(area)
+ = label :assigned_treatment_area_id, area.name
@jordanbyron Owner

Labels should always be associated with an input element, especially check boxes and radio buttons. This allows users to click the label to toggle the buttons, and not have to position the mouse over the much smaller checkbox.

This even applies to text fields / areas. You can see my fix for this here: 3df3777

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jordanbyron jordanbyron merged commit fca3dc4 into jordanbyron:master
@jordanbyron
Owner

Wahoooo!!!! Thanks for making this feature a reality. I think this ended up being more complicated than we both expected but I'm really glad you kept working and got everything done.

Now we get to party! :balloon: :fireworks: :balloon: :tada: :balloon:

@jordanbyron jordanbyron commented on the diff
app/models/patient.rb
((20 lines not shown))
end
end
+ def assign(assigned_to_area_id, assigned_to_radiology)
@jordanbyron Owner

I ended up re-writing this method. Check out my code in this commit: 654b98f

I don't know if it's much better, but there is definitely a code smell here which I will need to address soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@kbl
Collaborator

Great! Nice to hear that code is approved (:

Do this pull request solves also #26? If not I must misunderstood sth deeply (;

@jordanbyron
Owner

Do this pull request solves also #26?

Yes it does. I forgot we had a separate ticket for that. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 12, 2012
  1. @kbl
  2. @kbl

    basic model setup, migrations

    kbl authored
  3. @kbl

    setting up relations

    kbl authored
  4. @kbl

    missing factory for treatment area

    kbl authored
  5. @kbl
  6. @kbl

    checking out, typos

    kbl authored
Commits on Jan 13, 2012
  1. @kbl

    checking in patient to new area

    kbl authored
  2. @kbl
  3. @kbl

    preventing double check_in

    kbl authored
  4. @kbl
  5. @kbl

    testing saving checkout time

    kbl authored
  6. @kbl

    counting only checked in patients

    kbl authored
  7. @kbl

    done - deleting todo

    kbl authored
  8. @kbl

    working checking in (:

    kbl authored
  9. @kbl

    view blueprint

    kbl authored
  10. @kbl
Commits on Jan 16, 2012
  1. @kbl

    feedback: spork as test dependency

    kbl authored
  2. @kbl
  3. @kbl
  4. @kbl
  5. @kbl
  6. @kbl

    assigning to many treatment areas

    kbl authored
  7. @kbl
Commits on Jan 17, 2012
  1. @kbl
  2. @kbl

    working assignment desk

    kbl authored
  3. @kbl
  4. @kbl
  5. @kbl

    checking out from radiology

    kbl authored
  6. @kbl

    localising dates on history view

    kbl authored
  7. @kbl

    matching checbox 0/1 with booleans

    kbl authored
  8. @kbl

    small code refactor

    kbl authored
  9. @kbl
  10. @kbl

    removing spork dependency

    kbl authored
  11. @kbl
  12. @kbl
Commits on Jan 18, 2012
  1. @kbl

    reasigning from assigment desk

    kbl authored
  2. @kbl
  3. @kbl
  4. @kbl

    unnecessary filter

    kbl authored
This page is out of date. Refresh to see the latest.
Showing with 503 additions and 138 deletions.
  1. +1 −1  Gemfile
  2. +0 −7 app/controllers/admin/maintenance_controller.rb
  3. +50 −0 app/controllers/admin/patients_controller.rb
  4. +2 −7 app/controllers/admin/reports_controller.rb
  5. +14 −9 app/controllers/assignment_desk_controller.rb
  6. +5 −57 app/controllers/patients_controller.rb
  7. +87 −10 app/models/patient.rb
  8. +16 −0 app/models/patient_assignment.rb
  9. +21 −5 app/models/treatment_area.rb
  10. +0 −7 app/views/admin/maintenance/index.html.haml
  11. +7 −0 app/views/admin/patients/_patient_assignment.html.haml
  12. +7 −0 app/views/admin/patients/_patient_flow.html.haml
  13. +15 −0 app/views/admin/patients/edit.html.haml
  14. +24 −0 app/views/admin/patients/history.html.haml
  15. +13 −13 app/views/assignment_desk/edit.html.haml
  16. +0 −15 app/views/patients/edit.html.haml
  17. +3 −2 app/views/patients/index.html.haml
  18. +3 −2 config/routes.rb
  19. +11 −0 db/migrate/20120112105720_create_patient_assignments.rb
  20. +9 −0 db/migrate/20120112111806_remove_assigned_treatment_area_from_patients.rb
  21. +9 −0 db/migrate/20120116134201_remove_radiology_from_patients.rb
  22. +9 −3 db/schema.rb
  23. +6 −0 test/factories/patient_assignment_factory.rb
  24. +6 −0 test/factories/treatment_area_factory.rb
  25. +12 −0 test/unit/patient_assignment_test.rb
  26. +125 −0 test/unit/patient_test.rb
  27. +48 −0 test/unit/treatment_area_test.rb
View
2  Gemfile
@@ -30,4 +30,4 @@ group :test do
gem 'faker'
gem 'colorific', '~> 1.0.0'
gem 'factory_girl_rails'
-end
+end
View
7 app/controllers/admin/maintenance_controller.rb
@@ -25,13 +25,6 @@ def reset
redirect_to admin_maintenance_path
end
- def reset_distribution
- Patient.update_all(:assigned_treatment_area_id => nil, :radiology => false)
-
- flash[:notice] = "Treatment Area Distribution Report Reset"
- redirect_to admin_maintenance_path
- end
-
private
def set_current_tab
View
50 app/controllers/admin/patients_controller.rb
@@ -0,0 +1,50 @@
+module Admin
+ class PatientsController < ApplicationController
+
+ before_filter :admin_required, :set_current_tab
+
+ def edit
+ @patient = Patient.find(params[:id])
+ @patient.build_previous_mom_clinics
+ end
+
+ def update
+ if params[:id] == nil
+ params[:id] = params[:patient_id]
+ end
+
+ @patient = Patient.find(params[:id])
+
+ if @patient.update_attributes(params[:patient])
+
+ if params[:commit] == "Next"
+ redirect_to(:controller => 'exit_surveys', :action => 'new', :id => @patient.id)
+ else
+ flash[:notice] = 'Patient was successfully updated.'
+
+ redirect_to patients_path
+ end
+ else
+ render :action => "edit"
+ end
+ end
+
+ def destroy
+ @patient = Patient.find(params[:id])
+ @patient.destroy
+
+ redirect_to(patients_url)
+ end
+
+ def history
+ @patient = Patient.find(params[:patient_id])
+ end
+
+ private
+
+ def set_current_tab
+ @current_tab = 'patients'
+ end
+
+ end
+end
View
9 app/controllers/admin/reports_controller.rb
@@ -17,13 +17,8 @@ def clinic_summary
end
def treatment_area_distribution
- @areas = TreatmentArea.all(:order => "name")
-
- @current_capacity = @areas.map do |a|
- count = a.patients.count || 0
- count = Patient.count(:conditions => {:radiology => true}) if a.radiology?
- [a.name, count]
- end
+ @areas = TreatmentArea.order('name')
+ @current_capacity = TreatmentArea.current_capacity
end
def post_clinic
View
23 app/controllers/assignment_desk_controller.rb
@@ -3,22 +3,27 @@ class AssignmentDeskController < ApplicationController
def edit
@patient = Patient.find(params[:id])
- @areas = TreatmentArea.all(:order => "name")
+ @is_assigned_to_radiology = @patient.assigned_to?(TreatmentArea.radiology)
- @current_capacity = @areas.map do |a|
- count = a.patients.count || 0
- count = Patient.count(:conditions => {:radiology => true}) if a.radiology?
- [a.name, count]
- end
+ @areas = TreatmentArea.order('name')
+ @current_capacity = TreatmentArea.current_capacity
end
def update
patient = Patient.find(params[:id])
+ patient_params = params[:patient_params]
- patient.update_attributes(params[:patient])
-
- flash[:notice] = 'Patient was successfully assigned.'
+ if patient.assign(patient_params[:assigned_to], checked?(patient_params[:radiology]))
+ flash[:notice] = 'Patient was successfully assigned.'
+ end
redirect_to patients_path
end
+
+ private
+
+ def checked?(checkbox_value)
+ checkbox_value != '0'
+ end
+
end
View
62 app/controllers/patients_controller.rb
@@ -1,9 +1,8 @@
class PatientsController < ApplicationController
+
before_filter :authenticate_user!, :except => [ :lookup_zip, :lookup_city ]
- before_filter :admin_required, :only => [ :edit, :destroy ]
before_filter :date_input
before_filter :find_last_patient, :only => [:new]
- before_filter :set_current_tab
def index
if params[:commit] == "Clear"
@@ -11,7 +10,7 @@ def index
params[:name] = nil
end
- @patients = Patient.search(params[:chart_number], params[:name],params[:page])
+ @patients = Patient.search(params[:chart_number], params[:name], params[:page])
@area = params[:treatment_area_id]
@area ||= session[:treatment_area_id] if session[:treatment_area_id]
@@ -25,14 +24,7 @@ def index
def new
@patient = Patient.new
@patient.survey = Survey.new
-
- build_previous_mom_clinics
- end
-
- def edit
- @patient = Patient.find(params[:id])
-
- build_previous_mom_clinics
+ @patient.build_previous_mom_clinics
end
def print
@@ -53,7 +45,7 @@ def create
@patient_travel_time_minutes = params[:patient_travel_time_minutes]
@patient_travel_time_hours = params[:patient_travel_time_hours]
- build_previous_mom_clinics
+ @patient.build_previous_mom_clinics
render :action => "new"
end
@@ -71,7 +63,7 @@ def export_to_dexis_file
respond_to do |format|
format.html do
@patient.flows.create(:area_id => ClinicArea::XRAY)
- @patient.update_attribute(:radiology, false)
+ @patient.check_out(TreatmentArea.radiology)
redirect_to treatment_area_patient_procedures_path(TreatmentArea.radiology, @patient.id)
end
@@ -81,34 +73,6 @@ def export_to_dexis_file
redirect_to patients_path
end
- def update
- if params[:id] == nil
- params[:id] = params[:patient_id]
- end
-
- @patient = Patient.find(params[:id])
-
- if @patient.update_attributes(params[:patient])
-
- if params[:commit] == "Next"
- redirect_to(:controller => 'exit_surveys', :action => 'new', :id => @patient.id)
- else
- flash[:notice] = 'Patient was successfully updated.'
-
- redirect_to patients_path
- end
- else
- render :action => "edit"
- end
- end
-
- def destroy
- @patient = Patient.find(params[:id])
- @patient.destroy
-
- redirect_to(patients_url)
- end
-
private
def add_procedures_to_patient(patient)
@@ -119,19 +83,6 @@ def add_procedures_to_patient(patient)
end
end
- def build_previous_mom_clinics
- PatientPreviousMomClinic::CLINICS.each do |y, l|
- existing = @patient.previous_mom_clinics.detect do |c|
- c.clinic_year == y && c.location == l
- end
-
- unless existing
- @patient.previous_mom_clinics.build(:clinic_year => y, :location => l)
- end
- end
-
- end
-
def date_input
if params[:date_input]
session[:date_input] = params[:date_input]
@@ -146,7 +97,4 @@ def find_last_patient
end
end
- def set_current_tab
- @current_tab = "patients" if current_user.user_type == UserType::ADMIN
- end
end
View
97 app/models/patient.rb
@@ -1,4 +1,5 @@
class Patient < ActiveRecord::Base
+
REGEXP = {
:time_in_pain => /\A(\d*\.?\d*)\s*(.+)\Z/,
:number_only => /\A(\d*\.?\d*)\Z/,
@@ -27,6 +28,9 @@ class Patient < ActiveRecord::Base
:dependent => :delete_all
has_many :previous_mom_clinics, :class_name => "PatientPreviousMomClinic",
:dependent => :delete_all
+ has_many :assignments, :class_name => 'PatientAssignment',
+ :dependent => :delete_all
+ has_many :treatment_areas, :through => :assignments
has_one :prosthetic, :dependent => :delete
has_one :zipcode, :class_name => "Patient::Zipcode",
@@ -34,7 +38,6 @@ class Patient < ActiveRecord::Base
:primary_key => "zip"
belongs_to :survey, :dependent => :delete
- belongs_to :assigned_treatment_area, :class_name => "TreatmentArea"
accepts_nested_attributes_for :survey
accepts_nested_attributes_for :prosthetic
@@ -58,6 +61,7 @@ class Patient < ActiveRecord::Base
:with => /^[\(\)0-9\- \+\.]{10,20}$/,
:allow_blank => true
validates_numericality_of :travel_time, :greater_than => 0
+
attr_accessor :race_other
attr_reader :time_in_pain
@@ -71,8 +75,8 @@ def self.search(chart_number, name, page)
["id = ?", -1]
end
- Patient.where(conditions).order('id').paginate(:per_page => 30, :page => page)
- end
+ Patient.where(conditions).order('id').paginate(:per_page => 30, :page => page)
+ end
def chart_number
id
@@ -102,17 +106,33 @@ def procedures_grouped
patient_procedures.group_by(&:procedure)
end
- def check_out(area)
- unless area == TreatmentArea.radiology
- self.flows.create(:area_id => ClinicArea::CHECKOUT,
- :treatment_area_id => area.id)
+ def check_out(area_id)
+ current_assignment = assignments.not_checked_out.where(treatment_area_id: area_id).first
+ if current_assignment
+ current_assignment.check_out
- self.update_attributes(:assigned_treatment_area_id => nil,
- :survey_id => nil,
- :radiology => false)
+ unless current_assignment.radiology?
+ self.flows.create(area_id: ClinicArea::CHECKOUT, treatment_area_id: area_id)
+ self.update_attributes(survey_id: nil)
+ end
end
end
+ def assign(assigned_to_area_id, assigned_to_radiology)
@jordanbyron Owner

I ended up re-writing this method. Check out my code in this commit: 654b98f

I don't know if it's much better, but there is definitely a code smell here which I will need to address soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ destroyed = destroy_previous_assignments(assigned_to_area_id, assigned_to_radiology)
+ created = create_new_assignments(assigned_to_area_id, assigned_to_radiology)
+
+ destroyed || created
+ end
+
+ def assigned_to
+ assignments.not_checked_out.map(&:treatment_area)
+ end
+
+ def assigned_to?(area)
+ assigned_to.include?(area)
+ end
+
def export_to_dexis(path)
f = File.new(path, "w")
f.write(["PN=", "#{Date.today.year}#{id}", "\r\n"].join())
@@ -180,6 +200,18 @@ def date_of_birth=(date_of_birth)
end
end
+ def build_previous_mom_clinics
+ PatientPreviousMomClinic::CLINICS.each do |year, location|
+ existing = previous_mom_clinics.detect do |c|
+ c.clinic_year == year && c.location == location
+ end
+
+ unless existing
+ previous_mom_clinics.build(clinic_year: year, location: location)
+ end
+ end
+ end
+
private
def update_survey
@@ -240,4 +272,49 @@ def date_of_birth_entry
return true
end
+
+ def destroy_previous_assignments(assigned_to_area_id, assigned_to_radiology)
+ destroyed = false
+ assignments.not_checked_out.all.each do |assignment|
+
+ case assignment.radiology?
+ when true
+ unless assigned_to_radiology
+ assignment.delete
+ destroyed = true
+ end
+ when false
+ area = assignment.treatment_area
+ is_reassigned = assigned_to_area_id && assigned_to_area_id != area.id
+
+ if is_reassigned
+ assignment.delete
+ destroyed = true
+ end
+ end
+
+ end
+
+ destroyed
+ end
+
+ def create_new_assignments(assigned_to_area_id, assigned_to_radiology)
+ areas_to_assign = []
+ assigned_areas = assigned_to
+
+ if assigned_to_radiology
+ area_radiology = TreatmentArea.radiology
+ areas_to_assign << area_radiology unless assigned_areas.include?(area_radiology)
+ end
+ assigned_areas.delete(area_radiology)
+
+ if assigned_to_area_id && assigned_areas.empty?
+ areas_to_assign << TreatmentArea.find(assigned_to_area_id)
+ end
+
+ areas_to_assign.each { |a| assignments.create(treatment_area: a) }
+
+ not areas_to_assign.empty?
+ end
+
end
View
16 app/models/patient_assignment.rb
@@ -0,0 +1,16 @@
+class PatientAssignment < ActiveRecord::Base
+
+ belongs_to :patient
+ belongs_to :treatment_area
+
+ scope :not_checked_out, where('checked_out_at IS NULL')
+
+ def check_out
+ update_attributes(checked_out_at: Time.new)
+ end
+
+ def radiology?
+ treatment_area.radiology?
+ end
+
+end
View
26 app/models/treatment_area.rb
@@ -1,18 +1,33 @@
class TreatmentArea < ActiveRecord::Base
+
+ RADIOLOGY_NAME = 'Radiology'
+
has_many :procedure_treatment_area_mappings
- has_many :procedures, :through => :procedure_treatment_area_mappings,
- :order => "code"
- has_many :patients, :foreign_key => "assigned_treatment_area_id"
+ has_many :procedures, through: :procedure_treatment_area_mappings,
+ order: 'code'
+ has_many :patient_assignments
+ has_many :patients, through: :patient_assignments,
+ conditions: [
+ 'checked_out_at IS NULL AND patient_assignments.created_at BETWEEN ? AND ?',
+ Time.now.beginning_of_day,
+ Time.now.end_of_day]
accepts_nested_attributes_for :procedure_treatment_area_mappings, :allow_destroy => true,
:reject_if => proc { |attributes| attributes['assigned'] == "0" }
def self.radiology
- TreatmentArea.find(:first, :conditions => {:name => "Radiology"})
+ where(name: RADIOLOGY_NAME).first
+ end
+
+ def self.current_capacity
+ all.map do |area|
+ count = area.patients.count || 0
+ [area.name, count]
+ end
end
def radiology?
- name == "Radiology"
+ name == RADIOLOGY_NAME
end
def patients_treated
@@ -30,4 +45,5 @@ def patients_treated
patients.uniq
end
+
end
View
7 app/views/admin/maintenance/index.html.haml
@@ -20,10 +20,3 @@
:class => "warning",
:confirm => "WARNING: This will destroy all patient information! Are you sure you want to proceed?",
:method => :post
-
-%p
- = link_to 'Reset Treatment Area Distribution Report',
- admin_maintenance_reset_distribution_path,
- :class => "warning",
- :confirm => "This will reset the Treatment Area Distribution Report. Are you sure you want to proceed?",
- :method => :post
View
7 app/views/admin/patients/_patient_assignment.html.haml
@@ -0,0 +1,7 @@
+%tr{class: cycle('even', 'odd')}
+ %td
+ = assignment.treatment_area.name
+ %td
+ = l(assignment.created_at, format: :long)
+ %td
+ = l(assignment.checked_out_at, format: :long) if assignment.checked_out_at
View
7 app/views/admin/patients/_patient_flow.html.haml
@@ -0,0 +1,7 @@
+%tr{:class=> cycle('even', 'odd')}
+ %td
+ = ClinicArea[flow.area_id]
+ %td
+ = flow.treatment_area.name if flow.treatment_area
+ %td
+ = l(flow.created_at, format: :long)
View
15 app/views/admin/patients/edit.html.haml
@@ -0,0 +1,15 @@
+- title 'Edit Patient'
+
+= header do
+ %h1 Edit Patient
+
+= form_for @patient, html: { autocomplete: "off" }, url: admin_patient_path(@patient) do |f|
+ = render partial: 'patients/form', locals: {f: f}
+
+ .input-bottom
+ = f.submit "Update"
+ |
+ = link_to 'Cancel', patients_path, class: 'warning'
+
+
+
View
24 app/views/admin/patients/history.html.haml
@@ -0,0 +1,24 @@
+%h3
+ = "#{@patient.full_name} | Chart # #{@patient.id}"
+
+%h4 Treatment areas
+
+%table
+ %thead
+ %tr
+ %th Treatment area
+ %th Checked in
+ %th Checked out
+ %tbody
+ = render partial: 'patient_assignment', collection: @patient.assignments, as: 'assignment'
+
+%h4 Flows
+
+%table
+ %thead
+ %tr
+ %th Action
+ %th Treatment area
+ %th Action time
+ %tbody
+ = render partial: 'patient_flow', collection: @patient.flows, as: 'flow'
View
26 app/views/assignment_desk/edit.html.haml
@@ -5,27 +5,27 @@
%br
= "#{@patient.full_name} | Chart # #{@patient.id}"
-= form_for @patient, :url => assignment_desk_path(@patient) do |f|
- %div.input-left.no_border{:style => "width: 200px;"}
+= form_tag assignment_desk_path(@patient), method: :put do
+ .input-left.no_border{:style => 'width: 200px;'}
%h4 Treatment Areas
%p
- - @areas.reject {|a| a.radiology? }.each do |area|
- = f.radio_button :assigned_treatment_area_id, area.id
- = f.label :assigned_treatment_area_id, area.name, :value => area.id
+ - @areas.reject {|ar| ar.radiology? }.each do |area|
+ = radio_button :patient_params, :assigned_to, area.id, checked: @patient.assigned_to?(area)
+ = label :assigned_treatment_area_id, area.name
@jordanbyron Owner

Labels should always be associated with an input element, especially check boxes and radio buttons. This allows users to click the label to toggle the buttons, and not have to position the mouse over the much smaller checkbox.

This even applies to text fields / areas. You can see my fix for this here: 3df3777

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
%br
%p
- = f.check_box :radiology
- = f.label :radiology, "Radiology"
+ = check_box :patient_params, :radiology, checked: @is_assigned_to_radiology
+ = label :radiology, 'Radiology'
- %div.input-right.border
- = bar_graph "treatment_area_graph",
+ .input-right.border
+ = bar_graph 'treatment_area_graph',
@current_capacity,
- { :style => "width:590px;height:200px" }
+ { style: 'width: 590px; height: 200px' }
%br
- %div.input-bottom{:class => "check_out"}
- = f.submit "Assign"
- = link_to "Cancel", patients_path, :class => "warning"
+ .input-bottom{class: 'check_out'}
+ = submit_tag 'Assign'
+ = link_to 'Cancel', patients_path, class: 'warning'
View
15 app/views/patients/edit.html.haml
@@ -1,15 +0,0 @@
-- title 'Edit Patient'
-
-= header do
- %h1 Edit Patient
-
-= form_for @patient, :html => { :autocomplete => "off" } do |f|
- = render :partial => 'form', :locals => {:f => f}
-
- .input-bottom
- = f.submit "Update"
- |
- = link_to 'Cancel', patients_path, :class => 'warning'
-
-
-
View
5 app/views/patients/index.html.haml
@@ -56,9 +56,10 @@
%th{:scope => "row"}= link_to "Assign", edit_assignment_desk_path(patient)
- if current_user.user_type == UserType::ADMIN
- %td= link_to 'Edit', edit_patient_path(patient)
+ %td= link_to 'History', admin_patient_history_path(patient_id: patient.id)
+ %td= link_to 'Edit', edit_admin_patient_path(patient)
%td= link_to 'Print', {:controller => 'patients', :action => 'print', :id => patient.id}, :popup => true
- %td= link_to 'Destroy', patient, :confirm => 'Are you sure?', :method => :delete
+ %td= link_to 'Destroy', admin_patient_path(patient), :confirm => 'Are you sure?', :method => :delete
= will_paginate @patients
View
5 config/routes.rb
@@ -24,7 +24,7 @@
match '/pharmacy/check_out/:patient_id' => 'pharmacy#check_out', :as => :pharmacy_check_out
match '/pharmacy/finalize/:patient_id' => 'pharmacy#check_out_complete', :as => :pharmacy_finalize
- resources :patients
+ resources :patients, except: [:edit, :update, :destroy]
match '/autocomplete/city.json' => 'autocomplete#city', :as => :autocomplete_city
match '/autocomplete/zip.json' => 'autocomplete#zip', :as => :autocomplete_zip
@@ -41,6 +41,7 @@
resources :pre_meds
resources :prescriptions
resources :users
+ resources :patients, only: [:edit, :update, :destroy]
resources :support_requests do
collection do
delete :destroy_all
@@ -53,6 +54,6 @@
match '/reports/export_patients' => 'reports#export_patients', :as => :export_patients
match '/maintenance' => 'maintenance#index', :as => :maintenance
match '/maintenance/reset' => 'maintenance#reset', :as => :maintenance_reset
- match '/maintenance/reset_distribution' => 'maintenance#reset_distribution', :as => :maintenance_reset_distribution
+ match '/patients/:patient_id/history' => 'patients#history', :as => :patient_history
end
end
View
11 db/migrate/20120112105720_create_patient_assignments.rb
@@ -0,0 +1,11 @@
+class CreatePatientAssignments < ActiveRecord::Migration
+ def change
+ create_table :patient_assignments do |t|
+ t.references :patient
+ t.references :treatment_area
+ t.datetime :checked_out_at
+
+ t.timestamps
+ end
+ end
+end
View
9 db/migrate/20120112111806_remove_assigned_treatment_area_from_patients.rb
@@ -0,0 +1,9 @@
+class RemoveAssignedTreatmentAreaFromPatients < ActiveRecord::Migration
+ def up
+ remove_column :patients, :assigned_treatment_area_id
+ end
+
+ def down
+ add_column :patients, :assigned_treatment_area_id, :integer
+ end
+end
View
9 db/migrate/20120116134201_remove_radiology_from_patients.rb
@@ -0,0 +1,9 @@
+class RemoveRadiologyFromPatients < ActiveRecord::Migration
+ def up
+ remove_column :patients, :radiology
+ end
+
+ def down
+ add_column :patients, :radiology, :boolean
+ end
+end
View
12 db/schema.rb
@@ -11,7 +11,15 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20111021224603) do
+ActiveRecord::Schema.define(:version => 20120116134201) do
+
+ create_table "patient_assignments", :force => true do |t|
+ t.integer "patient_id"
+ t.integer "treatment_area_id"
+ t.datetime "checked_out_at"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
create_table "patient_flows", :force => true do |t|
t.integer "treatment_area_id"
@@ -95,9 +103,7 @@
t.datetime "created_at"
t.datetime "updated_at"
t.integer "survey_id"
- t.integer "assigned_treatment_area_id"
t.string "phone"
- t.boolean "radiology", :default => false
end
create_table "pre_meds", :force => true do |t|
View
6 test/factories/patient_assignment_factory.rb
@@ -0,0 +1,6 @@
+FactoryGirl.define do
+ factory :patient_assignment do
+ patient
+ treatment_area
+ end
+end
View
6 test/factories/treatment_area_factory.rb
@@ -0,0 +1,6 @@
+FactoryGirl.define do
+ factory :treatment_area do |f|
+ f.sequence(:name) { |n| "Area \##{n}" }
+ capacity 20
+ end
+end
View
12 test/unit/patient_assignment_test.rb
@@ -0,0 +1,12 @@
+require 'test_helper'
+
+class PatientAssignmentTest < ActiveSupport::TestCase
+
+ def test_should_return_not_checked_out_entries
+ a1 = Factory(:patient_assignment)
+ a2 = Factory(:patient_assignment, checked_out_at: Time.now)
+
+ assert_equal [a1], PatientAssignment.not_checked_out
+ end
+
+end
View
125 test/unit/patient_test.rb
@@ -2,6 +2,11 @@
class PatientTest < ActiveSupport::TestCase
+ def setup
+ super
+ @radiology = Factory(:treatment_area, name: TreatmentArea::RADIOLOGY_NAME)
+ end
+
def test_should_not_allow_more_than_2_digits_in_state_field
patient = Factory.build(:patient, :state => "CTZ")
@@ -127,6 +132,126 @@ def test_text_entry_for_date_of_birth_should_not_accept_invalid_date_formats
assert !patient.save,
"Saved patient with an invalid date_of_birth value"
+ end
+
+ def test_should_properly_assign_treatment_area
+ patient = Factory(:patient)
+ area = Factory(:treatment_area)
+
+ patient.assign(area.id, false)
+
+ assert_equal 1, patient.assignments.count
+ assert_equal area, patient.assignments[0].treatment_area
+ end
+
+ def test_should_return_area_to_which_patient_is_assigned
+ patient = Factory(:patient)
+ area = Factory(:treatment_area)
+
+ patient.assign(area.id, false)
+
+ assert_equal area, patient.assigned_to[0]
+ end
+
+ def test_shoud_allow_for_assigning_to_multiple_areas
+ patient = Factory(:patient)
+ area = Factory(:treatment_area)
+
+ patient.assign(area.id, true)
+
+ assert_equal [@radiology, area], patient.assigned_to
+ end
+
+ def test_shoud_allow_for_assigning_to_radiology_only
+ patient = Factory(:patient)
+
+ patient.assign(nil, true)
+
+ assert_equal [@radiology], patient.assigned_to
+ end
+
+ def test_should_allow_check_out
+ area = Factory(:treatment_area)
+ patient = Factory(:patient)
+
+ patient.assign(area.id, false)
+ assert_equal area, patient.assigned_to[0]
+
+ patient.check_out(area.id)
+ assert patient.assigned_to.empty?
+ end
+
+ def test_should_save_check_out_time
+ area = Factory(:treatment_area)
+ patient = Factory(:patient)
+ assert patient.assign(area.id, false)
+ assert_nil patient.assignments.last.checked_out_at
+
+ patient.check_out(area.id)
+ assert_not_nil patient.assignments.last.checked_out_at
+ end
+
+ def test_should_return_false_if_assignment_wasnt_successfull
+ patient = Factory(:patient)
+ assert_equal false, patient.assign(nil, false)
+ end
+
+ def test_should_check_if_patient_is_assigned_to_area
+ patient = Factory(:patient)
+ area = Factory(:treatment_area)
+
+ patient.assign(area.id, false)
+
+ assert patient.assigned_to?(area)
+ assert_equal false, patient.assigned_to?(Factory(:treatment_area))
+ end
+
+ def test_shoud_remove_radiology_assignment
+ patient = Factory(:patient)
+
+ patient.assign(nil, true)
+ assert_equal 1, patient.assigned_to.size
+
+ patient.assign(nil, false)
+ assert patient.assigned_to.empty?
end
+
+ def test_should_chenge_assigned_area
+ patient = Factory(:patient)
+ area1 = Factory(:treatment_area)
+ area2 = Factory(:treatment_area)
+
+ patient.assign(area1.id, false)
+ assert_equal area1, patient.assigned_to[0]
+
+ patient.assign(area2.id, false)
+ assert_equal area2, patient.assigned_to[0]
+ end
+
+ def test_reassinging_should_return_true
+ patient = Factory(:patient)
+ area1 = Factory(:treatment_area)
+ area2 = Factory(:treatment_area)
+
+ assert patient.assign(area1.id, false)
+ assert patient.assign(area1.id, true)
+ assert patient.assign(area2.id, true)
+ assert_equal false, patient.assign(area2.id, true)
+ assert patient.assign(area2.id, false)
+ end
+
+ def test_shouldnt_destroy_checked_out_assignments
+ patient = Factory(:patient)
+ area1 = Factory(:treatment_area)
+ area2 = Factory(:treatment_area)
+
+ patient.assign(area1.id, false)
+ patient.check_out(area1.id)
+ assert_equal 1, patient.assignments.count
+
+ patient.assign(area2.id, false)
+ assert_equal 2, patient.assignments.count
+ end
+
end
View
48 test/unit/treatment_area_test.rb
@@ -0,0 +1,48 @@
+require 'test_helper'
+
+class TreatmentAreaTest < ActiveSupport::TestCase
+
+ def test_should_count_only_checked_in_patients
+ area = Factory(:treatment_area)
+ p1 = Factory(:patient)
+ p2 = Factory(:patient)
+ p3 = Factory(:patient)
+
+ [p1, p2, p3].each { |p| p.assign(area.id, false) }
+
+ assert_equal 3, area.patients.count
+
+ p3.check_out(area.id)
+ assert_equal 2, area.patients.count
+ end
+
+ def test_should_return_radiology_from_named_scope
+ area = Factory(:treatment_area, name: TreatmentArea::RADIOLOGY_NAME)
+ assert_equal area, TreatmentArea.radiology
+ end
+
+ # TODO kbl
+ # again time zone issue ):
+ # on rails console everything is ok, but here with my setup test fails
+ def test_should_count_only_patients_checked_in_in_the_same_day
+ area = Factory(:treatment_area)
+ p1 = Factory(:patient, created_at: Time.zone.now - 2.day)
+ p2 = Factory(:patient)
+ p3 = Factory(:patient)
+
+ [p1, p2, p3].each { |p| p.assign(area.id, false) }
+
+ assert_equal 2, area.patients.count
+ end
+
+ def test_should_properly_count_current_capacity
+ area = Factory(:treatment_area)
+ second_area = Factory(:treatment_area)
+
+ 3.times { Factory(:patient).assign(area.id, false) }
+ 2.times { Factory(:patient).assign(second_area.id, false) }
+
+ assert_equal [[area.name, 3], [second_area.name, 2]], TreatmentArea.current_capacity
+ end
+
+end
Something went wrong with that request. Please try again.