diff --git a/Gemfile b/Gemfile
index b462492..378ebd5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,12 +1,12 @@
source 'https://rubygems.org'
-gem 'rails', '~> 3.1'
+ruby '1.9.3'
+
+gem 'rails', '~> 3.2'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
-gem 'sqlite3'
-
# Use unicorn as the web server
# gem 'unicorn'
@@ -28,16 +28,17 @@ gem 'sqlite3'
# and rake tasks are available in development mode:
group :development, :test do
# RSpec
- gem "rspec", "~> 2.6"
- gem "rspec-rails", "~> 2.6"
+ gem "rspec"
+ gem "rspec-rails"
# Cucmber
gem "cucumber", "~> 1.0"
gem "cucumber-rails", "~> 1.0", :require => false
gem "database_cleaner"
+ gem "factory_girl_rails", ">= 4.1"
gem "jasmine", "~> 1"
- gem "ruby-debug19"
+ gem "debugger"
gem "heroku"
@@ -45,13 +46,17 @@ group :development, :test do
gem "guard-coffeescript"
end
+# Mogoid
+gem 'mongoid', ">= 3.1"
+
+gem 'geocoder'
gem 'googlecharts'
# Use JQuery not prototype
gem 'jquery-rails'
# Gems required for user authentication
-gem 'devise'
+gem 'devise', ">= 2.2"
gem 'cancan'
gem 'css3buttons'
diff --git a/Gemfile.lock b/Gemfile.lock
index 8c4547a..5dea252 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,39 +1,37 @@
GEM
remote: https://rubygems.org/
specs:
- actionmailer (3.1.1)
- actionpack (= 3.1.1)
- mail (~> 2.3.0)
- actionpack (3.1.1)
- activemodel (= 3.1.1)
- activesupport (= 3.1.1)
+ actionmailer (3.2.13)
+ actionpack (= 3.2.13)
+ mail (~> 2.5.3)
+ actionpack (3.2.13)
+ activemodel (= 3.2.13)
+ activesupport (= 3.2.13)
builder (~> 3.0.0)
erubis (~> 2.7.0)
- i18n (~> 0.6)
- rack (~> 1.3.2)
- rack-cache (~> 1.1)
- rack-mount (~> 0.8.2)
+ journey (~> 1.0.4)
+ rack (~> 1.4.5)
+ rack-cache (~> 1.2)
rack-test (~> 0.6.1)
- sprockets (~> 2.0.2)
- activemodel (3.1.1)
- activesupport (= 3.1.1)
+ sprockets (~> 2.2.1)
+ activemodel (3.2.13)
+ activesupport (= 3.2.13)
builder (~> 3.0.0)
- i18n (~> 0.6)
- activerecord (3.1.1)
- activemodel (= 3.1.1)
- activesupport (= 3.1.1)
- arel (~> 2.2.1)
+ activerecord (3.2.13)
+ activemodel (= 3.2.13)
+ activesupport (= 3.2.13)
+ arel (~> 3.0.2)
tzinfo (~> 0.3.29)
- activeresource (3.1.1)
- activemodel (= 3.1.1)
- activesupport (= 3.1.1)
- activesupport (3.1.1)
+ activeresource (3.2.13)
+ activemodel (= 3.2.13)
+ activesupport (= 3.2.13)
+ activesupport (3.2.13)
+ i18n (= 0.6.1)
multi_json (~> 1.0)
- addressable (2.2.6)
- archive-tar-minitar (0.5.2)
- arel (2.2.1)
+ addressable (2.3.4)
+ arel (3.0.2)
bcrypt-ruby (3.0.1)
- builder (3.0.0)
+ builder (3.0.4)
cancan (1.6.7)
capybara (1.1.1)
mime-types (>= 1.16)
@@ -48,7 +46,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.1.2)
- columnize (0.3.4)
+ columnize (0.3.6)
css3buttons (1.0.1)
actionpack (>= 3.0.0)
cucumber (1.1.1)
@@ -62,15 +60,29 @@ GEM
cucumber (>= 1.1.1)
nokogiri (>= 1.5.0)
database_cleaner (0.6.7)
- devise (1.4.9)
+ debugger (1.6.0)
+ columnize (>= 0.3.1)
+ debugger-linecache (~> 1.2.0)
+ debugger-ruby_core_source (~> 1.2.1)
+ debugger-linecache (1.2.0)
+ debugger-ruby_core_source (1.2.2)
+ devise (2.2.4)
bcrypt-ruby (~> 3.0)
- orm_adapter (~> 0.0.3)
- warden (~> 1.0.3)
- diff-lcs (1.1.3)
+ orm_adapter (~> 0.1)
+ railties (~> 3.1)
+ warden (~> 1.2.1)
+ diff-lcs (1.2.4)
erubis (2.7.0)
+ excon (0.23.0)
execjs (1.2.9)
multi_json (~> 1.0)
+ factory_girl (4.1.0)
+ activesupport (>= 3.0.0)
+ factory_girl_rails (4.1.0)
+ factory_girl (~> 4.1.0)
+ railties (>= 3.0.0)
ffi (1.0.9)
+ geocoder (1.1.8)
gherkin (2.6.2)
json (>= 1.4.6)
googlecharts (1.6.8)
@@ -79,114 +91,116 @@ GEM
guard-coffeescript (0.5.2)
coffee-script (>= 2.2.0)
guard (>= 0.8.3)
- haml (3.1.3)
- heroku (2.11.1)
+ haml (4.0.3)
+ tilt
+ heroku (2.39.4)
+ heroku-api (~> 0.3.7)
launchy (>= 0.3.2)
+ netrc (~> 0.7.7)
rest-client (~> 1.6.1)
rubyzip
- term-ansicolor (~> 1.0.5)
- hike (1.2.1)
- i18n (0.6.0)
+ heroku-api (0.3.12)
+ excon (~> 0.23.0)
+ hike (1.2.2)
+ i18n (0.6.1)
jasmine (1.1.2)
jasmine-core (>= 1.1.0)
rack (>= 1.1)
rspec (>= 1.3.1)
selenium-webdriver (>= 0.1.3)
jasmine-core (1.1.0)
+ journey (1.0.4)
jquery-rails (1.0.16)
railties (~> 3.0)
thor (~> 0.14)
- json (1.6.1)
+ json (1.8.0)
json_pure (1.6.1)
- launchy (2.0.5)
- addressable (~> 2.2.6)
- linecache19 (0.5.12)
- ruby_core_source (>= 0.1.4)
- mail (2.3.0)
- i18n (>= 0.4.0)
+ launchy (2.3.0)
+ addressable (~> 2.3)
+ mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
- mime-types (1.17.2)
- multi_json (1.0.3)
+ mime-types (1.23)
+ mongoid (3.1.4)
+ activemodel (~> 3.2)
+ moped (~> 1.4)
+ origin (~> 1.0)
+ tzinfo (~> 0.3.22)
+ moped (1.5.0)
+ multi_json (1.7.3)
+ netrc (0.7.7)
nokogiri (1.5.0)
- orm_adapter (0.0.5)
+ origin (1.1.0)
+ orm_adapter (0.4.0)
polyglot (0.3.3)
- rack (1.3.5)
- rack-cache (1.1)
+ rack (1.4.5)
+ rack-cache (1.2)
rack (>= 0.4)
- rack-mount (0.8.3)
- rack (>= 1.0.0)
- rack-ssl (1.3.2)
+ rack-ssl (1.3.3)
rack
- rack-test (0.6.1)
+ rack-test (0.6.2)
rack (>= 1.0)
- rails (3.1.1)
- actionmailer (= 3.1.1)
- actionpack (= 3.1.1)
- activerecord (= 3.1.1)
- activeresource (= 3.1.1)
- activesupport (= 3.1.1)
+ rails (3.2.13)
+ actionmailer (= 3.2.13)
+ actionpack (= 3.2.13)
+ activerecord (= 3.2.13)
+ activeresource (= 3.2.13)
+ activesupport (= 3.2.13)
bundler (~> 1.0)
- railties (= 3.1.1)
- railties (3.1.1)
- actionpack (= 3.1.1)
- activesupport (= 3.1.1)
+ railties (= 3.2.13)
+ railties (3.2.13)
+ actionpack (= 3.2.13)
+ activesupport (= 3.2.13)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
- thor (~> 0.14.6)
- rake (0.9.2.2)
- rdoc (3.11)
+ thor (>= 0.14.6, < 2.0)
+ rake (10.0.4)
+ rdoc (3.12.2)
json (~> 1.4)
rest-client (1.6.7)
mime-types (>= 1.16)
- rspec (2.7.0)
- rspec-core (~> 2.7.0)
- rspec-expectations (~> 2.7.0)
- rspec-mocks (~> 2.7.0)
- rspec-core (2.7.1)
- rspec-expectations (2.7.0)
- diff-lcs (~> 1.1.2)
- rspec-mocks (2.7.0)
- rspec-rails (2.7.0)
- actionpack (~> 3.0)
- activesupport (~> 3.0)
- railties (~> 3.0)
- rspec (~> 2.7.0)
- ruby-debug-base19 (0.11.25)
- columnize (>= 0.3.1)
- linecache19 (>= 0.5.11)
- ruby_core_source (>= 0.1.4)
- ruby-debug19 (0.11.6)
- columnize (>= 0.3.1)
- linecache19 (>= 0.5.11)
- ruby-debug-base19 (>= 0.11.19)
- ruby_core_source (0.1.5)
- archive-tar-minitar (>= 0.5.2)
- rubyzip (0.9.4)
+ rspec (2.13.0)
+ rspec-core (~> 2.13.0)
+ rspec-expectations (~> 2.13.0)
+ rspec-mocks (~> 2.13.0)
+ rspec-core (2.13.1)
+ rspec-expectations (2.13.0)
+ diff-lcs (>= 1.1.3, < 2.0)
+ rspec-mocks (2.13.1)
+ rspec-rails (2.13.2)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ railties (>= 3.0)
+ rspec-core (~> 2.13.0)
+ rspec-expectations (~> 2.13.0)
+ rspec-mocks (~> 2.13.0)
+ rubyzip (0.9.9)
sass (3.1.10)
selenium-webdriver (2.10.0)
childprocess (>= 0.2.1)
ffi (= 1.0.9)
json_pure
rubyzip
- sprockets (2.0.3)
+ sprockets (2.2.2)
hike (~> 1.2)
+ multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
- sqlite3 (1.3.4)
- term-ansicolor (1.0.7)
+ term-ansicolor (1.2.2)
+ tins (~> 0.8)
therubyracer-heroku (0.8.1.pre3)
thor (0.14.6)
- tilt (1.3.3)
- treetop (1.4.10)
+ tilt (1.4.1)
+ tins (0.8.0)
+ treetop (1.4.12)
polyglot
polyglot (>= 0.3.1)
- tzinfo (0.3.30)
+ tzinfo (0.3.37)
uglifier (1.0.4)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)
- warden (1.0.6)
+ warden (1.2.1)
rack (>= 1.0)
xpath (0.1.4)
nokogiri (~> 1.3)
@@ -201,7 +215,10 @@ DEPENDENCIES
cucumber (~> 1.0)
cucumber-rails (~> 1.0)
database_cleaner
- devise
+ debugger
+ devise (>= 2.2)
+ factory_girl_rails (>= 4.1)
+ geocoder
googlecharts
guard
guard-coffeescript
@@ -209,11 +226,10 @@ DEPENDENCIES
heroku
jasmine (~> 1)
jquery-rails
- rails (~> 3.1)
- rspec (~> 2.6)
- rspec-rails (~> 2.6)
- ruby-debug19
+ mongoid (>= 3.1)
+ rails (~> 3.2)
+ rspec
+ rspec-rails
sass
- sqlite3
therubyracer-heroku (= 0.8.1.pre3)
uglifier
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..455f398
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2011-2013 Simon Funke, Peter Hamilton, Kushal Pisavadia and
+Florian Rathgeber
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile b/Makefile
index 3678491..bb087a7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,5 @@
default:
- rake db:drop
- rake db:create
- rake db:migrate
- rake db:fixtures:load
+ rake db:mongoid:create_indexes
rake bootstrap:all
rake test:units
rake cucumber
diff --git a/app/assets/javascripts/application.coffee b/app/assets/javascripts/application.coffee
index 9714ad5..920633f 100644
--- a/app/assets/javascripts/application.coffee
+++ b/app/assets/javascripts/application.coffee
@@ -59,7 +59,7 @@ parseHospitalJSON = (hospitalJsonObjects, map = EMG.map) ->
EMG.fit_zoom()
resizeContentToWindow = ->
- $('#main').height($(window).height() - 80)
+ $('#main').height($(window).height() - 160)
bindFilterButtons = ->
$('#hospital_filter_button_distance').bind 'click', (event) =>
diff --git a/app/assets/javascripts/geolocation_handler.coffee b/app/assets/javascripts/geolocation_handler.coffee
index 11d8f68..63bb1e1 100644
--- a/app/assets/javascripts/geolocation_handler.coffee
+++ b/app/assets/javascripts/geolocation_handler.coffee
@@ -8,10 +8,7 @@
#Initiated process of setting the user's location
locateUser: ->
- if this.browserGeolocationEnabled()
- this.setLocationUsingBrowser()
- else
- this.locateWithPostcode()
+ this.setLocationUsingBrowser()
# Centers the map on the current user's recorded location
centerMapOnLocation: ->
@@ -81,6 +78,7 @@
# Zooms in to current location and initiates prompt to get user to verify
# the suggested location. Opens the facebox and binds the buttons
verifyLocation: ->
+ $.facebox.close()
this.centerMapOnLocation()
EMG.map.setZoom(16)
if !EMG.loadHospitals()
@@ -130,7 +128,7 @@
this.setLocation {'lat': position.coords.latitude, 'lon': position.coords.longitude, 'postcode': ''}
this.verifyLocation()
, =>
- $.facebox("Error: The Geolocation service failed.")
+ this.locateWithPostcode()
else
- $.facebox("Error: Your browser doesn't support geolocation.")
+ this.locateWithPostcode()
diff --git a/app/assets/javascripts/location.coffee b/app/assets/javascripts/location.coffee
index d866fa8..509deda 100644
--- a/app/assets/javascripts/location.coffee
+++ b/app/assets/javascripts/location.coffee
@@ -1,8 +1,9 @@
@module "EMG", ->
class @Location
constructor: (json) ->
- @lat = json.latitude
- @lon = json.longitude
+ @id = json._id
+ @lat = json.location[1]
+ @lon = json.location[0]
@name = json.name
@postcode = json.postcode
@distance = json.distance
@@ -14,9 +15,10 @@
@infowindow = new google.maps.InfoWindow
content: "
" + @name + "
" +
@postcode + "" +
- "Distance: " + @distance/1000 + " km" +
+ "Distance: " + @distance + " km" +
"Current waiting time: " + @delay + " min
" +
- "Updated " + @last_updated_at
+ "Updated " + @last_updated_at +
+ "Last week's waiting time"
getLocation: ->
return {
@@ -27,6 +29,9 @@
getHashcode: ->
return @hashcode
+ getId: ->
+ @id
+
getName: ->
@name
diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss
index 432732a..5500776 100644
--- a/app/assets/stylesheets/style.scss
+++ b/app/assets/stylesheets/style.scss
@@ -4,7 +4,7 @@
#= require html5-boilerplate
/* Core styles for body/containers */
-html { height: 100% }
+html { height: 100%; overflow: hidden }
body { height: 100%; margin: 0; padding: 0 }
#container{ width: 100%; height: 100%; position: relative}
#main{ width: 100%; height: 100%; position: relative;}
@@ -19,28 +19,45 @@ body, select, input, textarea {
font-variant: normal;
font-weight: 300;
}
+::-webkit-scrollbar {
+ display: none;
+}
.clear{clear:both}
/* Fluid Layout */
-#header{min-height: 90px;}
+#header{
+ min-height: 120px;
+ height:90px;
+ background: hsla(207, 45%, 55%, 1);
+}
#wrapper{float:left;width:100%;}
-#map_canvas{float:left;width:100%}
-#sidebar{float:right;width:300px;margin: 0;}
-#footer{clear:both;width:100%}
-
-/* Fluid Layout Styles */
-#header{height:90px; background: hsla(207, 45%, 55%, 1)}
-#map_canvas p{line-height:1.4}
-#sidebar{background:#FFF}
-#footer{background: #333;color: #FFF}
-#footer p{margin:0;padding:5px 10px}
+#map_canvas{
+ float:left;
+ width:100%;
+ p {
+ line-height:1.4;
+ }
+}
+#footer{
+ clear:both;
+ width:100%;
+ background: #333;
+ color: #FFF;
+ p {
+ margin:0;
+ padding:5px 10px;
+ }
+}
#sidebar{
- float: right;
- height: 100%;
- position: absolute;
- right: 0;
- overflow: scroll;
+ background:#FFF;
+ float: right;
+ width:280px;
+ height: 100%;
+ margin: 0;
+ position: absolute;
+ right: 0;
+ overflow-x: hidden;
}
/* Buttons */
@@ -93,14 +110,22 @@ body, select, input, textarea {
.header_desc{
font-size: 24px;
float:left;
- padding-top: 45px;
}
.header_buttons{
+ position: absolute;
+ top: 0;
+ right: 0;
padding: 5px;
float: right;
}
+.button-group{
+ display: block;
+ float: right;
+ clear: both;
+}
+
.header_text{
color: hsla(0, 0%, 95%, .85);
font-size: 16px;
@@ -110,7 +135,8 @@ body, select, input, textarea {
}
#hospital_filters{
- padding: 15px;
+ margin: 15px;
+ height: 30px;
.hospital_filter_button{
float: left;
padding: 5px;
@@ -131,7 +157,7 @@ body, select, input, textarea {
#hospital_list{
- margin: 15px;
+ margin: 0;
border-top: 1px #E5E5E5 solid;
li{
@@ -320,7 +346,4 @@ body, select, input, textarea {
.message {
text-align: center;
padding: 10px;
- margin-bottom: 10px;
}
-
-
diff --git a/app/controllers/delays_controller.rb b/app/controllers/delays_controller.rb
index d7c7e11..53169a9 100644
--- a/app/controllers/delays_controller.rb
+++ b/app/controllers/delays_controller.rb
@@ -18,11 +18,11 @@ def get_hospital
# GET /delays
# GET /delays.xml
def index
- nb_days = 7
- start = nb_days.days.ago
- dates = [start.to_f] + Delay.all(:conditions => {:created_at => start..0.days.ago, :hospital_id => get_hospital.id}).map{|d| d.created_at.to_f}.reverse + [0.days.ago.to_f] # We add to additional nodes at the beginning and the end of the timespan
+ start = 7.days.ago
+ delays = @hospital.delays.where(:created_at => start..0.days.ago)
+ dates = [start.to_f] + delays.map{|d| d.created_at.to_f}.reverse + [0.days.ago.to_f] # We add to additional nodes at the beginning and the end of the timespan
dates2 = dates.map{|d| (d-start.to_f)/20}
- delays = Delay.all(:conditions => {:created_at => start..0.days.ago, :hospital_id => get_hospital.id}, :select => :minutes).map(&:minutes).reverse
+ delays = delays.map(&:minutes).reverse
if delays.empty?
delays2 = [0.0, 0.1]
else
@@ -30,12 +30,12 @@ def index
end
dates2 = dates2.collect { |d| d * delays2.max / dates2.max if dates2.max > 0 }
data = [dates2, delays2]
- wdays = ['Sun', 'Sat', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
+ wdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
(0..Date.today.wday+1).each do |i|
d = wdays.shift
wdays.push(d)
end
- @graph_url = Gchart.line_xy(:size => '500x300',
+ @graph_url = Gchart.line_xy(:size => '500x300',
:title => "Last weeks waiting time",
:data => data,
:axis_with_label => 'x,y',
diff --git a/app/controllers/hospitals_controller.rb b/app/controllers/hospitals_controller.rb
index 148c5c6..c7b3dbd 100644
--- a/app/controllers/hospitals_controller.rb
+++ b/app/controllers/hospitals_controller.rb
@@ -3,16 +3,21 @@
class HospitalsController < ApplicationController
def index
- max_distance = 500000
+ max_distance = 5 # Distance in kilometers
max_results = 20
+ units = 'km'
- location = {lat: params[:lat].to_f, lon: params[:lon].to_f, radius: (params[:radius] || max_distance).to_i}
+ if params[:postcode] then
+ lat, lng = Geocoder.search(params[:postcode]).first.coordinates
+ else
+ lat, lng = params[:lat].to_f, params[:lon].to_f
+ end
- @hospitals = Hospital.find_hospitals_sorted(location[:lat],
- location[:lon],
- location[:radius],
+ @hospitals = Hospital.find_hospitals_sorted(lat, lng,
+ (params[:radius] || max_distance).to_f,
params[:sort],
- (params[:max_results] || max_results).to_i)
+ (params[:max_results] || max_results).to_i,
+ params[:units] || units)
respond_to do |format|
format.html # index.html.haml
format.json { render :json => @hospitals.as_json(:mobile => params[:mobile]) }
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index ea13482..57acf6d 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -17,7 +17,7 @@ def index
# GET /users/1/show
# GET /users/1/show.xml
- def show
+ def show
@user = User.find(params[:id])
respond_to do |format|
@@ -33,7 +33,7 @@ def edit_hospital
if not @user.hospital.nil?
@hospital = Hospital.new(:id => 0, :name => @user.hospital.name)
end
- @hospitals = Hospital.find(:all, :order => "name").map {|p| [p.name, p.id] }
+ @hospitals = Hospital.order_by(:name.asc).map {|p| [p.name, p.id] }
respond_to do |format|
format.html # index.html.erb
@@ -56,7 +56,7 @@ def edit_role
# POST /users/1/update_role
def update_role
@user = User.find(params[:id])
- @user.roles = [Role.find_by_id(params["post"]["id"])]
+ @user.roles = [Role.find_by_id(params["post"]["id"])]
respond_to do |format|
if @user.save
diff --git a/app/models/delay.rb b/app/models/delay.rb
index fa44fca..14cc5cf 100644
--- a/app/models/delay.rb
+++ b/app/models/delay.rb
@@ -1,16 +1,14 @@
-class Delay < ActiveRecord::Base
- belongs_to :hospital
- after_initialize :init
+class Delay
+ include Mongoid::Document
+
+ embedded_in :hospital
+ field :minutes, type: Float, default: 0.0
+ field :created_at, type: Time, default: ->{ Time.now }
+ field :updated_at, type: Time, default: ->{ Time.now }
validates_numericality_of :minutes, :greater_than_or_equal_to => 0
validates_presence_of :hospital
- default_scope :order => 'created_at DESC'
-
- def init
- self.minutes ||= 0.0 #will set the default value only if it's nil
- self.created_at ||= Time.now
- self.updated_at ||= Time.now
- end
+ default_scope order_by(:created_at => :desc)
end
diff --git a/app/models/hospital.rb b/app/models/hospital.rb
index cbdc08b..76d52e6 100644
--- a/app/models/hospital.rb
+++ b/app/models/hospital.rb
@@ -1,64 +1,66 @@
-class Hospital < ActiveRecord::Base
+class Hospital
+ include Mongoid::Document
include ActionView::Helpers::DateHelper
- has_many :delays
- has_many :users
- validates_presence_of :latitude, :longitude, :name
+ embeds_many :delays
+ has_many :users
+ field :source_uri, type: String
+ field :name, type: String
+ field :updated, type: Date
+ field :odscode, type: String
+ field :postcode, type: String
+ field :location, type: Array
+ index({ location: "2d" })
+
+ validates_presence_of :location, :name
validates_uniqueness_of :odscode
- validates_numericality_of :longitude, :greater_than_or_equal_to => -180.0, :less_than_or_equal_to => 180.0
- validates_numericality_of :latitude, :greater_than_or_equal_to => -90.0, :less_than_or_equal_to => 90.0
-
- attr_accessor :distance
+ validate :validate_location
+ def validate_location
+ errors.add(:location, 'invalid location') unless !self.location.nil? and
+ self.location.length == 2 and
+ self.location[0].is_a?(Numeric) and
+ (-180..180).include?(self.location[0]) and
+ self.location[1].is_a?(Numeric) and
+ (-90..90).include?(self.location[1])
+ end
def as_json(options={})
if options[:mobile]
return super(:only => [:odscode], :methods => [:delay])
end
- j = super(:only => [:odscode, :postcode, :name, :longitude, :latitude, :distance],
+ j = super(:only => [:_id, :odscode, :postcode, :name, :location, :distance],
:methods => [:delay, :last_updated_at])
- j[:distance] = "%.f" % self.distance
+ j[:distance] = self.geo_near_distance.round(2) if self.geo_near_distance
return j
end
def compute_distance(lat, lon)
- lat2 = self.latitude
- lon2 = self.longitude
- distance = Hospital.compute_distance(lat, lon, lat2, lon2)
+ distance = Hospital.compute_distance(lat, lon, self.location[1], self.location[0])
end
- def self.find_hospitals_sorted(lat, lon, max_distance, sort, max_results)
- hospitals = Hospital.find_hospitals_near_latlon(lat, lon, max_distance, max_results)
+ def self.find_hospitals_sorted(lat, lon, max_distance, sort, max_results=20, units='km')
+ hospitals = Hospital.find_hospitals_near_latlon(lat, lon, max_distance, max_results, units)
case sort
- when "agony" # Our custom ranking algorithm
- # FIXME: replace by some smart algorithm when we have one
- # Weigh delay against distance, assuming you travel 100m / min
- hospitals.sort!{|a,b| a.delay*100+a.distance <=> b.delay*100+b.distance}
+ when "distance" # By distance
+ hospitals # No need to sort, as the sorting by distance is the default
when "wait" # By wait time
hospitals.sort!{|a,b| a.delay <=> b.delay}
- else # By distance
- hospitals # No need to sort, as the sorting by distance is the default
+ else # Our custom ranking algorithm
+ # FIXME: replace by some smart algorithm when we have one
+ # Weigh delay against distance, assuming you travel 100m / min
+ hospitals.sort!{|a,b| a.delay+10*a.geo_near_distance <=> b.delay+10*b.geo_near_distance}
end
end
- # Max distance must be provided in meter
- def self.find_hospitals_near_latlon(lat, lon, max_distance=500000, max_results=20)
- hospitals_bb = Hospital.find_hospitals_in_bb(lat, lon, max_distance)
-
- # Precompute the distance for these hospitals
- hospitals = []
- hospitals_bb.each do |hospital|
- hospital.distance = hospital.compute_distance(lat, lon)
- if hospital.distance <= max_distance
- hospitals.push(hospital)
- end
- end
-
- return hospitals
+ # Max distance must be provided in the given units (km or mi)
+ def self.find_hospitals_near_latlon(lat, lon, max_distance=5, max_results=20, units='km')
+ earth_radius = units == 'km' ? 6371.0 : 3959.0
+ Hospital.limit(max_results).geo_near([lon, lat]).spherical.distance_multiplier(earth_radius).max_distance(max_distance/earth_radius).unique(true).to_a
end
- def self.find_hospitals_in_bb(lat, lon, max_distance)
+ def self.find_hospitals_in_bb(lat, lon, max_distance, max_results)
earth_radius = 6371000.0
earth_radius_at_lat = Math.cos(Hospital.to_rad(lat))*earth_radius
@@ -69,7 +71,7 @@ def self.find_hospitals_in_bb(lat, lon, max_distance)
delta_max_lon = Hospital.to_deg(Float(max_distance)/earth_radius_at_lat)
delta_max_lat = Hospital.to_deg(Float(max_distance)/earth_radius)
- hospitals_bb = Hospital.where(:longitude => (lon-delta_max_lon..lon+delta_max_lon), :latitude => (lat-delta_max_lat..lat+delta_max_lat))
+ hospitals_bb = Hospital.within_box(:location => [ [lon-delta_max_lon, lat-delta_max_lat], [lon+delta_max_lon, lat+delta_max_lat] ]).limit(max_results)
return hospitals_bb
end
@@ -79,7 +81,7 @@ def current_delay
end
def delay
- self.current_delay.try(:minutes) or 0
+ (self.current_delay.try(:minutes) or 0).round(2)
end
def last_updated_at
@@ -90,6 +92,14 @@ def last_updated_at
end
end
+ def latitude
+ self.location[1]
+ end
+
+ def longitude
+ self.location[0]
+ end
+
### Class methods ###
def self.to_rad(ang)
diff --git a/app/models/role.rb b/app/models/role.rb
index 33cd819..bdbc0c9 100644
--- a/app/models/role.rb
+++ b/app/models/role.rb
@@ -1,3 +1,5 @@
-class Role < ActiveRecord::Base
+class Role
+ include Mongoid::Document
+
has_and_belongs_to_many :users
end
diff --git a/app/models/roles_user.rb b/app/models/roles_user.rb
index 3918403..ba92cbf 100644
--- a/app/models/roles_user.rb
+++ b/app/models/roles_user.rb
@@ -1,4 +1,6 @@
-class RolesUser < ActiveRecord::Base
+class RolesUser
+ include Mongoid::Document
+
belongs_to :user
belongs_to :role
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 306de4b..ffbdf72 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,4 +1,5 @@
-class User < ActiveRecord::Base
+class User
+ include Mongoid::Document
has_and_belongs_to_many :roles
belongs_to :hospital
@@ -8,17 +9,38 @@ class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
+ ## Database authenticatable
+ field :email, :type => String, :default => ""
+ field :encrypted_password, :type => String, :default => ""
+
+ validates_presence_of :email
+ validates_presence_of :encrypted_password
+
+ ## Recoverable
+ field :reset_password_token, :type => String
+ field :reset_password_sent_at, :type => Time
+
+ ## Rememberable
+ field :remember_created_at, :type => Time
+
+ ## Trackable
+ field :sign_in_count, :type => Integer, :default => 0
+ field :current_sign_in_at, :type => Time
+ field :last_sign_in_at, :type => Time
+ field :current_sign_in_ip, :type => String
+ field :last_sign_in_ip, :type => String
+
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
after_create :set_default_role
def role?(role)
- return !!self.roles.find_by_name(role.to_s)
+ return !!self.roles.find_by(name: role.to_s)
end
def set_default_role
- RolesUser.create(:user_id => self.id, :role_id => Role.find_by_name('guest').id)
+ RolesUser.create(:user_id => self.id, :role_id => Role.find_by(name: 'guest').id)
end
end
diff --git a/app/views/layouts/_flashes.html.haml b/app/views/layouts/_flashes.html.haml
index 68bd151..ea21f52 100644
--- a/app/views/layouts/_flashes.html.haml
+++ b/app/views/layouts/_flashes.html.haml
@@ -1,4 +1,9 @@
#flash
+ .message{ :class => 'notice' }
+ %strong Disclaimer:
+ %em This site is a prototype in pre-alpha stage and does not show real live data!
+ %span Open source, MIT licensed.
+ %a{:href => "https://github.com/e-mergency/site"} Get the code!
- flash.each do |key, value|
.message{ :class => key }
- %p= value
\ No newline at end of file
+ %p= value
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 4ab938f..6b4d076 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -20,6 +20,6 @@
-# All JavaScript at the bottom, except for Modernizr and Respond.
-# Modernizr enables HTML5 elements & feature detects; Respond is a polyfill for min/max-width CSS3 Media Queries
-= javascript_include_tag 'modernizr.min', 'respond.min'
+= javascript_include_tag 'modernizr.min', 'respond.min', 'jquery.min'
= csrf_meta_tag
diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml
index 54acd55..2267047 100644
--- a/app/views/layouts/_header.html.haml
+++ b/app/views/layouts/_header.html.haml
@@ -3,7 +3,7 @@
.title
e-mergency
.header_desc
- Find out the waiting time of hospitals near you.
+ Find out the waiting time of urgent care services near you.
- if current_user
.header_buttons
.header_text
@@ -11,6 +11,7 @@
= button_group do
- if current_user.roles.length>0 and (current_user.roles[0].name == 'hospital' or current_user.roles[0].name == 'admin' ) and not current_user.hospital.nil?
= button_link_to "Update waiting time for #{current_user.hospital.name}", new_hospital_delay_path(current_user.hospital)
+ = button_group do
- if current_user.roles.length>0 and current_user.roles[0].name == 'admin'
= button_link_to "Administration", user_registration_path
= button_link_to 'Sign out', destroy_user_session_path, :method => :delete
diff --git a/app/views/layouts/_javascripts.html.haml b/app/views/layouts/_javascripts.html.haml
index 945a753..17dd356 100644
--- a/app/views/layouts/_javascripts.html.haml
+++ b/app/views/layouts/_javascripts.html.haml
@@ -1,7 +1,4 @@
-:javascript
- window.jQuery || document.write("