Initial commit
This commit is contained in:
commit
0f66eee72b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
dev.db
|
||||||
|
.bundle
|
16
Gemfile
Normal file
16
Gemfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
source 'https://rubygems.org'
|
||||||
|
ruby '1.9.3'
|
||||||
|
gem 'sinatra'
|
||||||
|
gem 'activerecord'
|
||||||
|
gem 'sinatra-activerecord'
|
||||||
|
gem 'httparty'
|
||||||
|
gem 'geocoder'
|
||||||
|
gem 'algorithms'
|
||||||
|
gem 'levenshtein-ffi', :require => 'levenshtein'
|
||||||
|
|
||||||
|
group :development, :test do
|
||||||
|
gem 'sqlite3'
|
||||||
|
end
|
||||||
|
group :production, :staging do
|
||||||
|
gem 'pg'
|
||||||
|
end
|
65
Gemfile.lock
Normal file
65
Gemfile.lock
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
activemodel (4.0.2)
|
||||||
|
activesupport (= 4.0.2)
|
||||||
|
builder (~> 3.1.0)
|
||||||
|
activerecord (4.0.2)
|
||||||
|
activemodel (= 4.0.2)
|
||||||
|
activerecord-deprecated_finders (~> 1.0.2)
|
||||||
|
activesupport (= 4.0.2)
|
||||||
|
arel (~> 4.0.0)
|
||||||
|
activerecord-deprecated_finders (1.0.3)
|
||||||
|
activesupport (4.0.2)
|
||||||
|
i18n (~> 0.6, >= 0.6.4)
|
||||||
|
minitest (~> 4.2)
|
||||||
|
multi_json (~> 1.3)
|
||||||
|
thread_safe (~> 0.1)
|
||||||
|
tzinfo (~> 0.3.37)
|
||||||
|
algorithms (0.6.1)
|
||||||
|
arel (4.0.1)
|
||||||
|
atomic (1.1.14)
|
||||||
|
builder (3.1.4)
|
||||||
|
ffi (1.1.5)
|
||||||
|
geocoder (1.1.9)
|
||||||
|
httparty (0.12.0)
|
||||||
|
json (~> 1.8)
|
||||||
|
multi_xml (>= 0.5.2)
|
||||||
|
i18n (0.6.9)
|
||||||
|
json (1.8.1)
|
||||||
|
levenshtein-ffi (1.0.3)
|
||||||
|
ffi
|
||||||
|
ffi (~> 1.1.5)
|
||||||
|
minitest (4.7.5)
|
||||||
|
multi_json (1.8.4)
|
||||||
|
multi_xml (0.5.5)
|
||||||
|
pg (0.17.1)
|
||||||
|
rack (1.5.2)
|
||||||
|
rack-protection (1.5.1)
|
||||||
|
rack
|
||||||
|
sinatra (1.4.4)
|
||||||
|
rack (~> 1.4)
|
||||||
|
rack-protection (~> 1.4)
|
||||||
|
tilt (~> 1.3, >= 1.3.4)
|
||||||
|
sinatra-activerecord (1.2.3)
|
||||||
|
activerecord (>= 3.0)
|
||||||
|
sinatra (~> 1.0)
|
||||||
|
sqlite3 (1.3.8)
|
||||||
|
thread_safe (0.1.3)
|
||||||
|
atomic
|
||||||
|
tilt (1.4.1)
|
||||||
|
tzinfo (0.3.38)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
activerecord
|
||||||
|
algorithms
|
||||||
|
geocoder
|
||||||
|
httparty
|
||||||
|
levenshtein-ffi
|
||||||
|
pg
|
||||||
|
sinatra
|
||||||
|
sinatra-activerecord
|
||||||
|
sqlite3
|
18
README.md
Normal file
18
README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Clear Transit Server
|
||||||
|
====================
|
||||||
|
|
||||||
|
What is this?
|
||||||
|
-------------
|
||||||
|
This is a server component of Clear Transit. An Android and Google Glass application for retrieving transit times from the NextBus Api
|
||||||
|
|
||||||
|
The Android client for this Heroku app is located at [Clear Transit](https://github.com/IamTheFij/ClearTransit")
|
||||||
|
|
||||||
|
I'm really not much of a Ruby dev, so this may be a little hacky. It's designed to run on Heroku using a Postgres DB.
|
||||||
|
The initial database is built by running: `ruby ./build_agency_bounds.rb` or, if it fails midway, running `ruby ./continue_agency_bounds.rb`.
|
||||||
|
|
||||||
|
Heroku button
|
||||||
|
-------------
|
||||||
|
This buttons should install the server on Heroku. Should... no promises.
|
||||||
|
|
||||||
|
[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy?template=https://github.com/IamTheFij/ClearTransitServer)
|
||||||
|
|
2
Rakefile
Normal file
2
Rakefile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
require './routes.rb'
|
||||||
|
require 'sinatra/activerecord/rake'
|
18
app.json
Normal file
18
app.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "Clear Transit",
|
||||||
|
"description": "Clear Transit web server",
|
||||||
|
"keywords": [
|
||||||
|
"transit",
|
||||||
|
"Sinatra",
|
||||||
|
"Ruby"
|
||||||
|
],
|
||||||
|
"website": "https://github.com/IamTheFij/ClearTransit-Web",
|
||||||
|
"repository": "https://github.com/IamTheFij/ClearTransit-Web",
|
||||||
|
"success_url": "/working",
|
||||||
|
"scripts": {
|
||||||
|
"postdeploy": "ruby ./build_agency_bounds.rb"
|
||||||
|
},
|
||||||
|
"addons": [
|
||||||
|
"heroku-postgresql:hobby-dev"
|
||||||
|
]
|
||||||
|
}
|
48
build_agency_bounds.rb
Normal file
48
build_agency_bounds.rb
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
require './environments.rb'
|
||||||
|
require './models/active.rb'
|
||||||
|
require './lib/nextbus.rb'
|
||||||
|
require './lib/utils.rb'
|
||||||
|
|
||||||
|
agencies = {}
|
||||||
|
|
||||||
|
AgencyBound.all.each do |bound|
|
||||||
|
bound.lat_min = nil
|
||||||
|
bound.lat_max = nil
|
||||||
|
bound.lon_min = nil
|
||||||
|
bound.lon_max = nil
|
||||||
|
agencies[bound.agency] = bound
|
||||||
|
end
|
||||||
|
|
||||||
|
NextBus.agency_list.parsed_response['agency'].each do |agency|
|
||||||
|
unless agencies.has_key?(agency['tag'])
|
||||||
|
agencies[agency['tag']] = AgencyBound.new(
|
||||||
|
agency: agency['tag'],
|
||||||
|
lat_min: nil,
|
||||||
|
lat_max: nil,
|
||||||
|
lon_min: nil,
|
||||||
|
lon_max: nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
agencies.each_value do |bound|
|
||||||
|
routes = NextBus.routes(bound.agency).parsed_response['route']
|
||||||
|
print "#{routes}\n"
|
||||||
|
unless routes.nil?
|
||||||
|
unless routes.kind_of?(Array)
|
||||||
|
routes = [ routes ]
|
||||||
|
end
|
||||||
|
routes.each do |route|
|
||||||
|
route_config = NextBus.route_config(bound.agency, route['tag']).parsed_response
|
||||||
|
#print "Route Config:\n#{route_config}\n"
|
||||||
|
route_config = route_config['route']
|
||||||
|
print "Agency: #{bound.agency} Route: #{route['tag']} Bound Min: #{bound.lat_min}, Config Min #{route_config['latMin']}\n"
|
||||||
|
bound.lat_min = min_val(bound.lat_min, route_config['latMin'].to_f)
|
||||||
|
bound.lat_max = max_val(bound.lat_max, route_config['latMax'].to_f)
|
||||||
|
bound.lon_min = min_val(bound.lon_min, route_config['lonMin'].to_f)
|
||||||
|
bound.lon_max = max_val(bound.lon_max, route_config['lonMax'].to_f)
|
||||||
|
end
|
||||||
|
bound.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
126
cleartransit.rb
Normal file
126
cleartransit.rb
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
require 'sinatra'
|
||||||
|
#require 'sinatra/activerecord'
|
||||||
|
require 'json'
|
||||||
|
require 'geocoder'
|
||||||
|
require 'algorithms'
|
||||||
|
require 'levenshtein'
|
||||||
|
|
||||||
|
require './environments.rb'
|
||||||
|
require './models/active.rb'
|
||||||
|
require './lib/nextbus.rb'
|
||||||
|
require './lib/utils.rb'
|
||||||
|
|
||||||
|
# Uses bounds of the user and returns any agencies they are in the bounds of
|
||||||
|
def get_contained_agencies(lat, lon)
|
||||||
|
agencies = []
|
||||||
|
|
||||||
|
AgencyBound.all.each do |bound|
|
||||||
|
if lat.between?(bound.lat_min, bound.lat_max) and lon.between?(bound.lon_min, bound.lon_max)
|
||||||
|
agencies.push(bound.agency)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return agencies
|
||||||
|
end
|
||||||
|
|
||||||
|
# Takes user input and agency and tries to find the most likely intended route
|
||||||
|
def find_best_routes(agency, user_route)
|
||||||
|
|
||||||
|
# Remove Line from end of user_route
|
||||||
|
user_route = user_route.downcase
|
||||||
|
user_route_words = user_route.split
|
||||||
|
if user_route_words.last == 'line'
|
||||||
|
user_route_words.pop
|
||||||
|
end
|
||||||
|
user_route = user_route_words.join(' ')
|
||||||
|
|
||||||
|
# TODO: Remove before Prod
|
||||||
|
routes = NextBus.routes(agency).parsed_response
|
||||||
|
#routes = {
|
||||||
|
# 'route' => [
|
||||||
|
# {
|
||||||
|
# 'title' => 'F - Market Warves',
|
||||||
|
# 'tag' => 'F'
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# 'title' => 'P - ',
|
||||||
|
# 'tag' => 'F'
|
||||||
|
# }
|
||||||
|
# ]
|
||||||
|
#}
|
||||||
|
|
||||||
|
probable_routes = Containers::MinHeap.new
|
||||||
|
|
||||||
|
routes['route'].each do |route|
|
||||||
|
dist = min_val(Levenshtein.distance(user_route, route['title'].downcase), Levenshtein.distance(user_route, route['tag'].downcase))
|
||||||
|
route_match = RouteMatch.new(
|
||||||
|
user_route: user_route,
|
||||||
|
match_route: route['tag'],
|
||||||
|
match_title: route['title'],
|
||||||
|
agency: agency,
|
||||||
|
# TODO: Depending on App implementation, default to false and set true or oposite
|
||||||
|
accepted: false,
|
||||||
|
distance: dist
|
||||||
|
)
|
||||||
|
probable_routes.push(dist, route_match)
|
||||||
|
end
|
||||||
|
|
||||||
|
routes = []
|
||||||
|
while not probable_routes.empty? and routes.length < 10
|
||||||
|
routes.push(probable_routes.pop)
|
||||||
|
end
|
||||||
|
|
||||||
|
return routes
|
||||||
|
end
|
||||||
|
|
||||||
|
# Takes an agency, route, and lat-lon and returns nearest predictions
|
||||||
|
def get_nearest_predictions(agency, route, lat, lon)
|
||||||
|
current_location = [lat, lon]
|
||||||
|
print current_location
|
||||||
|
route_config = NextBus.route_config(agency, route).parsed_response
|
||||||
|
|
||||||
|
min_stops = Containers::MinHeap.new
|
||||||
|
|
||||||
|
route_config['route']['stop'].each do |stop|
|
||||||
|
dist = Geocoder::Calculations.distance_between(current_location, [stop['lat'], stop['lon']])
|
||||||
|
|
||||||
|
min_stops.push(dist, stop)
|
||||||
|
end
|
||||||
|
|
||||||
|
#stop_directions = build_direction_map(route_config)
|
||||||
|
|
||||||
|
stops = [], route_stops = []
|
||||||
|
|
||||||
|
while not min_stops.empty? and stops.length < 5
|
||||||
|
stop = min_stops.pop
|
||||||
|
# Build array out of the 5 closest stops
|
||||||
|
stops.push(stop)
|
||||||
|
# Push route stops for fetching predictions
|
||||||
|
route_stops.push({:route => route, :stop => stop['tag']})
|
||||||
|
end
|
||||||
|
|
||||||
|
min_stops.clear
|
||||||
|
|
||||||
|
prediction_multiple = NextBus.prediction_multiple(agency, route_stops).parsed_response
|
||||||
|
|
||||||
|
# TODO: Possibly adjust sort order here if these appear to be wrong
|
||||||
|
|
||||||
|
return prediction_multiple
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_direction_map(route_config)
|
||||||
|
# TODO: DEPRECIATE
|
||||||
|
stop_directions = {}
|
||||||
|
|
||||||
|
route_config['route']['direction'].each do |direction|
|
||||||
|
direction['stop'].each do |stop|
|
||||||
|
unless stop_directions[stop['tag']]
|
||||||
|
stop_directions[stop['tag']] = []
|
||||||
|
end
|
||||||
|
stop_directions[stop['tag']].push(direction['name'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stop_directions
|
||||||
|
end
|
||||||
|
|
54
continue_agency_bounds.rb
Normal file
54
continue_agency_bounds.rb
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
require './environments.rb'
|
||||||
|
require './models/active.rb'
|
||||||
|
require './lib/nextbus.rb'
|
||||||
|
require './lib/utils.rb'
|
||||||
|
|
||||||
|
agencies = {}
|
||||||
|
|
||||||
|
AgencyBound.all.each do |bound|
|
||||||
|
if bound.lat_max != nil
|
||||||
|
agencies[bound.agency] = bound
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
NextBus.agency_list.parsed_response['agency'].each do |agency|
|
||||||
|
if agencies.has_key?(agency['tag'])
|
||||||
|
agencies.delete(agency['tag'])
|
||||||
|
else
|
||||||
|
agencies[agency['tag']] = AgencyBound.new(
|
||||||
|
agency: agency['tag'],
|
||||||
|
lat_min: nil,
|
||||||
|
lat_max: nil,
|
||||||
|
lon_min: nil,
|
||||||
|
lon_max: nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print "Agencies to get? #{agencies}"
|
||||||
|
|
||||||
|
if agencies.empty?
|
||||||
|
print "No new agencies"
|
||||||
|
end
|
||||||
|
|
||||||
|
agencies.each_value do |bound|
|
||||||
|
routes = NextBus.routes(bound.agency).parsed_response['route']
|
||||||
|
print "#{routes}\n"
|
||||||
|
unless routes.nil?
|
||||||
|
unless routes.kind_of?(Array)
|
||||||
|
routes = [ routes ]
|
||||||
|
end
|
||||||
|
routes.each do |route|
|
||||||
|
route_config = NextBus.route_config(bound.agency, route['tag']).parsed_response
|
||||||
|
#print "Route Config:\n#{route_config}\n"
|
||||||
|
route_config = route_config['route']
|
||||||
|
print "Agency: #{bound.agency} Route: #{route['tag']} Bound Min: #{bound.lat_min}, Config Min #{route_config['latMin']}\n"
|
||||||
|
bound.lat_min = min_val(bound.lat_min, route_config['latMin'].to_f)
|
||||||
|
bound.lat_max = max_val(bound.lat_max, route_config['latMax'].to_f)
|
||||||
|
bound.lon_min = min_val(bound.lon_min, route_config['lonMin'].to_f)
|
||||||
|
bound.lon_max = max_val(bound.lon_max, route_config['lonMax'].to_f)
|
||||||
|
end
|
||||||
|
bound.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
27
db/migrate/20140118012229_initial_db.rb
Normal file
27
db/migrate/20140118012229_initial_db.rb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
class InitialDb < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
create_table :route_matches do |t|
|
||||||
|
t.string :user_route
|
||||||
|
t.string :match_route
|
||||||
|
t.string :match_title
|
||||||
|
t.integer :distance
|
||||||
|
t.string :agency
|
||||||
|
t.boolean :accepted
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :agency_bounds do |t|
|
||||||
|
t.string :agency
|
||||||
|
t.float :lat_max
|
||||||
|
t.float :lat_min
|
||||||
|
t.float :lon_max
|
||||||
|
t.float :lon_min
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_table :route_matches
|
||||||
|
drop_table :agency_bounds
|
||||||
|
end
|
||||||
|
end
|
36
db/schema.rb
Normal file
36
db/schema.rb
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# This file is auto-generated from the current state of the database. Instead
|
||||||
|
# of editing this file, please use the migrations feature of Active Record to
|
||||||
|
# incrementally modify your database, and then regenerate this schema definition.
|
||||||
|
#
|
||||||
|
# Note that this schema.rb definition is the authoritative source for your
|
||||||
|
# database schema. If you need to create the application database on another
|
||||||
|
# system, you should be using db:schema:load, not running all the migrations
|
||||||
|
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
||||||
|
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
||||||
|
#
|
||||||
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
|
ActiveRecord::Schema.define(version: 20140118012229) do
|
||||||
|
|
||||||
|
create_table "agency_bounds", force: true do |t|
|
||||||
|
t.string "agency"
|
||||||
|
t.float "lat_max"
|
||||||
|
t.float "lat_min"
|
||||||
|
t.float "lon_max"
|
||||||
|
t.float "lon_min"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "route_matches", force: true do |t|
|
||||||
|
t.string "user_route"
|
||||||
|
t.string "match_route"
|
||||||
|
t.string "match_title"
|
||||||
|
t.integer "distance"
|
||||||
|
t.string "agency"
|
||||||
|
t.boolean "accepted"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
29
environments.rb
Normal file
29
environments.rb
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
require 'cgi'
|
||||||
|
require 'uri'
|
||||||
|
require 'sinatra'
|
||||||
|
require 'sinatra/activerecord'
|
||||||
|
|
||||||
|
configure :development do
|
||||||
|
set :database, 'sqlite:///dev.db'
|
||||||
|
set :show_exceptions, true
|
||||||
|
end
|
||||||
|
|
||||||
|
configure :staging, :production do
|
||||||
|
begin
|
||||||
|
db = URI.parse(ENV["DATABASE_URL"])
|
||||||
|
rescue URI::InvalidURIError
|
||||||
|
raise "Invalid DATABASE_URL"
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveRecord::Base.establish_connection(
|
||||||
|
:adapter => db.scheme == 'postgres' ? 'postgresql' : db.scheme,
|
||||||
|
:encoding => 'unicode',
|
||||||
|
:pool => 5,
|
||||||
|
:database => db.path[1..-1],
|
||||||
|
:username => db.user,
|
||||||
|
:password => db.password,
|
||||||
|
:host => db.host,
|
||||||
|
:port => db.port
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
41
lib/nextbus.rb
Normal file
41
lib/nextbus.rb
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
require 'httparty'
|
||||||
|
|
||||||
|
class NextBus
|
||||||
|
include HTTParty
|
||||||
|
|
||||||
|
def self.get_command(command, query={})
|
||||||
|
# Set the command
|
||||||
|
query[:command] = command
|
||||||
|
get('http://webservices.nextbus.com/service/publicJSONFeed', :query => query)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.agency_list
|
||||||
|
get_command('agencyList')
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.routes(agency)
|
||||||
|
get_command('routeList', { :a => agency })
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.route_config(agency, route)
|
||||||
|
get_command('routeConfig', { :a => agency, :r => route })
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.prediction(agency, route, stop, short_titles=true)
|
||||||
|
get_command('predictions', { :a => agency, :r => route, :s => stop, :useShortTitles => short_titles })
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.prediction_multiple(agency, route_stops=[])
|
||||||
|
endpoint = 'http://webservices.nextbus.com/service/publicJSONFeed'
|
||||||
|
endpoint += '?command=predictionsForMultiStops'
|
||||||
|
endpoint += '&a=' + agency
|
||||||
|
route_stops.each do |route_stop|
|
||||||
|
endpoint += '&stops=' + route_stop[:route] + '%7C' + route_stop[:stop]
|
||||||
|
end
|
||||||
|
|
||||||
|
print endpoint
|
||||||
|
|
||||||
|
get(endpoint)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
35
lib/utils.rb
Normal file
35
lib/utils.rb
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
def min_val(v1, v2)
|
||||||
|
if v2 == nil
|
||||||
|
return v1
|
||||||
|
elsif v1 == nil
|
||||||
|
return v2
|
||||||
|
elsif v1 < v2
|
||||||
|
return v1
|
||||||
|
else
|
||||||
|
return v2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def max_val(v1, v2)
|
||||||
|
if v2 == nil
|
||||||
|
return v1
|
||||||
|
elsif v1 == nil
|
||||||
|
return v2
|
||||||
|
elsif v1 > v2
|
||||||
|
return v1
|
||||||
|
else
|
||||||
|
return v2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_number?(object)
|
||||||
|
true if Float(object) rescue false
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_f_nil(s)
|
||||||
|
begin
|
||||||
|
return Float(s)
|
||||||
|
rescue
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
5
models/active.rb
Normal file
5
models/active.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Require individual models here
|
||||||
|
require 'active_record'
|
||||||
|
|
||||||
|
require './models/agency_bounds.rb'
|
||||||
|
require './models/route_matches.rb'
|
8
models/agency_bounds.rb
Normal file
8
models/agency_bounds.rb
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Table for storing boundries of given agencies to auto-detect
|
||||||
|
class AgencyBound < ActiveRecord::Base
|
||||||
|
validates :agency, :lat_max, :lat_min, :lon_max, :lon_min, presence: true
|
||||||
|
# validates :lat_max, presence: true
|
||||||
|
# validates :lat_min, presence: true
|
||||||
|
# validates :lon_max, presence: true
|
||||||
|
# validates :lon_min, presence: true
|
||||||
|
end
|
9
models/route_matches.rb
Normal file
9
models/route_matches.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Table for storing the user input and levenshtein match
|
||||||
|
# Will be used to assess accuracy and user acceptance
|
||||||
|
class RouteMatch < ActiveRecord::Base
|
||||||
|
validates :user_route, :match_route, :distance, presence: true
|
||||||
|
# validates :match_route, presence: true
|
||||||
|
# validates :distance, presence: true
|
||||||
|
# TODO: Default value for accepted?
|
||||||
|
end
|
||||||
|
|
168
routes.rb
Normal file
168
routes.rb
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
require 'sinatra'
|
||||||
|
require 'sinatra/activerecord'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
require './cleartransit.rb'
|
||||||
|
require './lib/nextbus.rb'
|
||||||
|
|
||||||
|
get '/' do
|
||||||
|
# TODO: Provide link to download the app
|
||||||
|
'Hello world! Go check out the repo: <a href="https://github.com/IamTheFij/CanHazMuni-Web">CanHazMuni-Web</a>'
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/working' do
|
||||||
|
'Hello world! Go check out the repo: <a href="https://github.com/IamTheFij/CanHazMuni-Web">CanHazMuni-Web</a>'
|
||||||
|
end
|
||||||
|
|
||||||
|
# List all agencies
|
||||||
|
get '/agency' do
|
||||||
|
status 200
|
||||||
|
body(NextBus.agency_list.to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
# List all routes in an agency
|
||||||
|
get '/agency/:agency/route' do |agency|
|
||||||
|
status 200
|
||||||
|
body(NextBus.routes(agency).to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
# List all stops in a route
|
||||||
|
get '/agency/:agency/route/:route/stop' do |agency, route|
|
||||||
|
status 200
|
||||||
|
body(NextBus.route_config(agency, route).to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get predictions for a given stop
|
||||||
|
get '/agency/:agency/route/:route/stop/:stop' do |agency, route, stop|
|
||||||
|
status 200
|
||||||
|
body(NextBus.prediction(agency, route, stop).to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get predictions for nearest stop on given agency and route
|
||||||
|
get '/agency/:agency/route/:route/nearest' do |agency, route|
|
||||||
|
# will match /agency/sf-muni/route/F/nearest?lat=37.804016399999995&lon=-122.40376609999998
|
||||||
|
lat = to_f_nil(params[:lat])
|
||||||
|
lon = to_f_nil(params[:lon])
|
||||||
|
if params[:lat].nil? or lon.nil? or route.nil?
|
||||||
|
status 404
|
||||||
|
else
|
||||||
|
status 200
|
||||||
|
body(get_nearest_predictions(agency, route, lat, lon).to_json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Primary feature
|
||||||
|
# Usine Lat, Lon, determine nearest agency, and stops and provide predictions
|
||||||
|
=begin rdoc
|
||||||
|
get '/icanhaz' do
|
||||||
|
# will match /icanhaz?lat=37.804016399999995&lon=-122.40376609999998&route=F%20line
|
||||||
|
|
||||||
|
if params[:lat].nil? or params[:lon].nil? or params[:route].nil?
|
||||||
|
status 404
|
||||||
|
else
|
||||||
|
status 200
|
||||||
|
# TODO: Implement
|
||||||
|
body({:thing1 => 'value'}.to_json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
=end
|
||||||
|
|
||||||
|
# Usine Lat, Lon and given agency, and user route and provide predictions
|
||||||
|
get '/icanhaz/:agency' do |agency|
|
||||||
|
# will match /icanhaz/sf-muni?lat=37.804016399999995&lon=-122.40376609999998&route=F%20line
|
||||||
|
lat = to_f_nil(params[:lat])
|
||||||
|
lon = to_f_nil(params[:lon])
|
||||||
|
if agency.nil? or lat.nil? or lon.nil? or params[:route].nil?
|
||||||
|
body("Error: Post convert agency: #{agency} lat: #{lat} lon: #{lon} route: #{params[:route]}")
|
||||||
|
status 404
|
||||||
|
else
|
||||||
|
status 200
|
||||||
|
|
||||||
|
# Use only the best route for now
|
||||||
|
route = find_best_routes(agency, params[:route])[0]
|
||||||
|
|
||||||
|
body(get_nearest_predictions(agency, route[:tag], params[:lat], params[:lon]).to_json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/icanhaz/agency/route' do
|
||||||
|
# will match /icanhaz/agency/route?lat=37.804016399999995&lon=-122.40376609999998&route=F%20line
|
||||||
|
lat = to_f_nil(params[:lat])
|
||||||
|
lon = to_f_nil(params[:lon])
|
||||||
|
if lat.nil? or lon.nil? or params[:route].nil?
|
||||||
|
body("Error: Post convert lat: #{lat} lon: #{lon} route: #{params[:route]}")
|
||||||
|
status 404
|
||||||
|
else
|
||||||
|
agencies = get_contained_agencies(lat, lon)
|
||||||
|
|
||||||
|
if agencies.empty?
|
||||||
|
# TODO: Find better status code for something like this
|
||||||
|
status 404
|
||||||
|
|
||||||
|
body("No service area found")
|
||||||
|
else
|
||||||
|
status 200
|
||||||
|
|
||||||
|
agency = agencies[0]
|
||||||
|
user_route = params[:route];
|
||||||
|
|
||||||
|
# Get the most likely route in the agency
|
||||||
|
# TODO: Match all the results returned to the RouteMatch class and insert all.
|
||||||
|
# This will be super useful to provide a scroll list for the user to pick
|
||||||
|
# alternate matches. The client can then mark which was actually matched.
|
||||||
|
# When doing this, will probably want a unique "session" Id as well
|
||||||
|
route_match = find_best_routes(agency, user_route)[0]
|
||||||
|
route_match.save
|
||||||
|
|
||||||
|
body(route_match.to_json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Usine given agency, and user route, guess the route
|
||||||
|
get '/icanhaz/:agency/route' do |agency|
|
||||||
|
# will match /icanhaz/sf-muni/route?route=F%20line
|
||||||
|
if agency.nil? or params[:route].nil?
|
||||||
|
status 404
|
||||||
|
else
|
||||||
|
status 200
|
||||||
|
|
||||||
|
user_route = params[:route];
|
||||||
|
|
||||||
|
# Get the most likely route in the agency
|
||||||
|
# TODO: Match all the results returned to the RouteMatch class and insert all.
|
||||||
|
# This will be super useful to provide a scroll list for the user to pick
|
||||||
|
# alternate matches. The client can then mark which was actually matched.
|
||||||
|
# When doing this, will probably want a unique "session" Id as well
|
||||||
|
route_match = find_best_routes(agency, user_route)[0]
|
||||||
|
route_match.save
|
||||||
|
|
||||||
|
body(route_match.to_json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Usine given agency, and actual route, get the predictions
|
||||||
|
get '/icanhaz/:agency/route/:route' do |agency, route|
|
||||||
|
# will match /icanhaz/sf-muni/route/F?lat=37.804016399999995&lon=-122.40376609999998
|
||||||
|
# Optionally takes &match_id=123
|
||||||
|
lat = to_f_nil(params[:lat])
|
||||||
|
lon = to_f_nil(params[:lon])
|
||||||
|
if agency.nil? or route.nil? or lat.nil? or lon.nil?
|
||||||
|
body("Error: Post convert lat: #{lat} lon: #{lon} route: #{params[:route]}")
|
||||||
|
status 404
|
||||||
|
else
|
||||||
|
status 200
|
||||||
|
|
||||||
|
# Get Id of databse tracked route match and indicate that it was accepted for predictions
|
||||||
|
if not params[:match_id].nil?
|
||||||
|
RouteMatch.update(params[:match_id], :accepted => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
body(get_nearest_predictions(agency, route, lat, lon).to_json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/route_matches' do
|
||||||
|
status 200
|
||||||
|
body(RouteMatch.all.to_json)
|
||||||
|
end
|
24
tags
Normal file
24
tags
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
|
||||||
|
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
|
||||||
|
!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
|
||||||
|
!_TAG_PROGRAM_NAME Exuberant Ctags //
|
||||||
|
!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
|
||||||
|
!_TAG_PROGRAM_VERSION 5.8 //
|
||||||
|
AgencyBound models/agency_bounds.rb /^class AgencyBound < ActiveRecord::Base$/;" c
|
||||||
|
InitialDb db/migrate/20140118012229_initial_db.rb /^class InitialDb < ActiveRecord::Migration$/;" c
|
||||||
|
NextBus lib/nextbus.rb /^class NextBus$/;" c
|
||||||
|
RouteMatch models/route_matches.rb /^class RouteMatch < ActiveRecord::Base$/;" c
|
||||||
|
agency_list lib/nextbus.rb /^ def self.agency_list$/;" F class:NextBus
|
||||||
|
build_direction_map canhazmuni.rb /^def build_direction_map(route_config)$/;" f
|
||||||
|
down db/migrate/20140118012229_initial_db.rb /^ def down$/;" f class:InitialDb
|
||||||
|
find_best_routes canhazmuni.rb /^def find_best_routes(agency, user_route)$/;" f
|
||||||
|
get_command lib/nextbus.rb /^ def self.get_command(command, query={})$/;" F class:NextBus
|
||||||
|
get_contained_agencies canhazmuni.rb /^def get_contained_agencies(lat, lon)$/;" f
|
||||||
|
get_nearest_predictions canhazmuni.rb /^def get_nearest_predictions(agency, route, lat, lon)$/;" f
|
||||||
|
max_val lib/utils.rb /^def max_val(v1, v2)$/;" f
|
||||||
|
min_val lib/utils.rb /^def min_val(v1, v2)$/;" f
|
||||||
|
prediction lib/nextbus.rb /^ def self.prediction(agency, route, stop, short_titles=true)$/;" F class:NextBus
|
||||||
|
prediction_multiple lib/nextbus.rb /^ def self.prediction_multiple(agency, route_stops=[])$/;" F class:NextBus
|
||||||
|
route_config lib/nextbus.rb /^ def self.route_config(agency, route)$/;" F class:NextBus
|
||||||
|
routes lib/nextbus.rb /^ def self.routes(agency)$/;" F class:NextBus
|
||||||
|
up db/migrate/20140118012229_initial_db.rb /^ def up$/;" f class:InitialDb
|
Loading…
Reference in New Issue
Block a user