From eb37770898528c1958b59f473ed37932110feadf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Wasiuty=C5=84ski?= Date: Mon, 25 Jul 2011 09:23:41 +0000 Subject: [PATCH] + rake task for creating nodes and its relationships in Neo4j --- Gemfile | 4 ++- Gemfile.lock | 66 ++++++++++++++++++++++++++++++++----- Rakefile | 71 ++++++++++++++++++++++++++++++++++------ app/main.rb | 2 +- app/models/connection.rb | 39 +++++++++++----------- app/models/hub.rb | 40 +++++++++++----------- app/models/stop.rb | 10 ++++++ app/models/transfer.rb | 4 +++ config/environment.rb | 8 +++-- 9 files changed, 180 insertions(+), 64 deletions(-) create mode 100644 app/models/stop.rb create mode 100644 app/models/transfer.rb diff --git a/Gemfile b/Gemfile index 476a0cc..86ab15a 100755 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,9 @@ source :rubygems group :app do gem 'thin' gem 'sinatra' - gem 'yajl-ruby' + gem 'neo4j', '1.1.2', :platforms => :jruby + gem 'unicode' + gem 'jruby-openssl' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 8a7faac..f8c23d9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,27 +1,69 @@ GEM remote: http://rubygems.org/ specs: - builder (3.0.0) - cucumber (1.0.0) + abstract (1.0.0) + actionpack (3.0.9) + activemodel (= 3.0.9) + activesupport (= 3.0.9) + builder (~> 2.1.2) + erubis (~> 2.6.6) + i18n (~> 0.5.0) + rack (~> 1.2.1) + rack-mount (~> 0.6.14) + rack-test (~> 0.5.7) + tzinfo (~> 0.3.23) + activemodel (3.0.9) + activesupport (= 3.0.9) + builder (~> 2.1.2) + i18n (~> 0.5.0) + activesupport (3.0.9) + bouncy-castle-java (1.5.0146.1) + builder (2.1.2) + cucumber (1.0.2) builder (>= 2.1.2) diff-lcs (>= 1.1.2) - gherkin (~> 2.4.1) + gherkin (~> 2.4.5) json (>= 1.4.6) term-ansicolor (>= 1.0.5) daemons (1.1.4) diff-lcs (1.1.2) + erubis (2.6.6) + abstract (>= 1.0.0) eventmachine (0.12.10) + eventmachine (0.12.10-java) fuubar (0.0.5) rspec (~> 2.0) rspec-instafail (~> 0.1.4) ruby-progressbar (~> 0.0.10) - gherkin (2.4.1) + gherkin (2.4.5) json (>= 1.4.6) + gherkin (2.4.5-java) + json (>= 1.4.6) + i18n (0.5.0) + jruby-openssl (0.7.4) + bouncy-castle-java json (1.5.3) + json (1.5.3-java) mime-types (1.16) - rack (1.3.0) - rack-test (0.6.0) + neo4j (1.1.2-java) + activemodel (>= 3.0.0) + orm_adapter (>= 0.0.3) + railties (>= 3.0.0) + will_paginate (~> 3.0.pre) + orm_adapter (0.0.5) + rack (1.2.3) + rack-mount (0.6.14) + rack (>= 1.0.0) + rack-test (0.5.7) rack (>= 1.0) + railties (3.0.9) + actionpack (= 3.0.9) + activesupport (= 3.0.9) + rake (>= 0.8.7) + rdoc (~> 3.4) + thor (~> 0.14.4) + rake (0.9.2) + rdoc (3.8) rest-client (1.6.3) mime-types (>= 1.16) rspec (2.6.0) @@ -36,24 +78,30 @@ GEM ruby-progressbar (0.0.10) sinatra (1.2.6) rack (~> 1.1) - tilt (< 2.0, >= 1.2.2) + tilt (>= 1.2.2, < 2.0) term-ansicolor (1.0.5) thin (1.2.11) daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) + thor (0.14.6) tilt (1.3.2) - yajl-ruby (0.8.2) + tzinfo (0.3.29) + unicode (0.4.0) + will_paginate (3.0.pre2) PLATFORMS + java ruby DEPENDENCIES cucumber fuubar + jruby-openssl + neo4j (= 1.1.2) rack-test rest-client rspec sinatra thin - yajl-ruby + unicode diff --git a/Rakefile b/Rakefile index de439b6..25ddc36 100755 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,9 @@ +#!/usr/bin/env jruby +# encoding: utf-8 + require 'rake' require 'restclient' +require 'unicode' #require 'spec/rake/spectask' require File.join(File.dirname(__FILE__), 'config', 'environment') @@ -18,7 +22,60 @@ require File.join(File.dirname(__FILE__), 'config', 'environment') namespace :neo4j do desc "Populate graph" - task :import do + task :import, [:db] do |t, args| + + require 'neo4j' + db = CONFIG['couch'] +'/'+ args[:db] + stops = {} + + Neo4j::Transaction.run do + # Get all stops + JSON.parse(RestClient.get db+'/_design/Stops/_view/by_name?group_level=1')['rows']. + each do |row| + location = row['value']['location'] + name = Unicode::upcase(row['key'].first) + stops[name] = Neo4j::Node.new(location) + end + + # Destination hub name + destination = nil + # Node from which we're adding connection + from = nil + + # Get all timetables + JSON.parse(RestClient.get db+'/_design/Timetables/_view/by_source?limit=200&descending=true')['rows']. + map{|row| row['value']}. + each do |timetable| + # timetable's destination + dest = Unicode::upcase((timetable['destination'] || timetable['route'].split('-').last).strip) + timetable['stop'] = Unicode.upcase(timetable['stop']) + + to = destination === dest ? from : stops[dest] + from = stops[timetable['stop']] # timetables['stop_id'] + + puts "#{timetable['line']}\n" if destination != dest + destination = dest + + if to == from + plus_one = timetable['stop'] +"+1" + unless from = stops[plus_one] + # create new node + from = stops[plus_one] = Neo4j::Node.new({}) # todo location + end + end + + puts "<- #{timetable['stop']} (#{from})" + puts "ERROR: #{dest}" unless to + + # Get or create relationship between from and to stops + unless connection = from.rels(:connections).outgoing.to_other(to).first + connection = Neo4j::Relationship.new(:connections, from, to) + connection['cost'] = 0 + end + # add departures from current timetable + connection['departures'] = "?" + end + end end end @@ -28,16 +85,10 @@ namespace :couchdb do task :views, [:db] do |t, args| couch = CONFIG['couch'] +'/'+ args[:db] designs = File.read './views/designs.json' - RestClient.post couch +'/_bulk_docs', designs, :content_type => :json, :accept => :json do |resp| + RestClient.post couch +'/_bulk_docs', designs, :content_type => :json, :accept => :json do |resp, req| JSON.parse(resp).each do |status| - if(status['error'] == 'conflict') - old = JSON.parse(RestClient.get(couch +'/'+ status['id']).gsub(/\n/,'')) - # Replace new lines with \n within strings then parse as JSON - new = JSON.parse(designs.gsub(/(\"[^\"]+\")/){|str| str.gsub(/\n/, '\n') })['docs']. - find{ |doc| doc['_id'] == status['id'] } # find conflicted document - RestClient.post couch, old.merge(new).to_json, :content_type => :json, :accept => :json do |resp| - p resp - end + if(status['error'] === 'conflict') + p status end end end diff --git a/app/main.rb b/app/main.rb index af064ce..c5af5d6 100755 --- a/app/main.rb +++ b/app/main.rb @@ -15,7 +15,7 @@ class Bagatela < Sinatra::Base # Welcome message get '/' do - {message: 'Welcome aboard!', version: VERSION}.to_json + {:message => 'Welcome aboard!', :version => VERSION}.to_json end # Search for connections diff --git a/app/models/connection.rb b/app/models/connection.rb index abb503f..a3155a9 100755 --- a/app/models/connection.rb +++ b/app/models/connection.rb @@ -3,30 +3,29 @@ class Connection include Neo4j::RelationshipMixin - property :cost # length in meters - property :timetables # every departure in direction - property :line # used to determine if a passenger have to make a transfer + property :cost # distance in meters + property :departures # hash of departures where key is a time and value is pair: line and arrival time # Returns nil if there are no more runs # Otherwise return cost (that is waiting time + trip time), departure time and trip time - def by_time(time) - dep, dur = next_run(time) || next - [dep-time+dur, dep, dur] - end + #def by_time(time) + #dep, dur = next_run(time) || next + #[dep-time+dur, dep, dur] + #end - # Returns nil if there are no more runs - def by_dist(time) - dep, dur = next_run(time) || next - [cost || start_node.by_dist(end_node), dep, dur] - end + ## Returns nil if there are no more runs + #def by_dist(time) + #dep, dur = next_run(time) || next + #[cost || start_node.by_dist(end_node), dep, dur] + #end - private + #private - # Finds next run and returns its departure time and waiting time - def next_run(time) - # TODO: do not use json! - JSON.parse(timetables).sort. - map{|dep, _| [dep.to_i, _]}. - find{|dep, _| dep.to_i >= time} - end + ## Finds next run and returns its departure time and waiting time + #def next_run(time) + ## TODO: do not use json! + #JSON.parse(timetables).sort. + #map{|dep, _| [dep.to_i, _]}. + #find{|dep, _| dep.to_i >= time} + #end end diff --git a/app/models/hub.rb b/app/models/hub.rb index a441baf..a17350a 100755 --- a/app/models/hub.rb +++ b/app/models/hub.rb @@ -3,32 +3,32 @@ class Hub include Neo4j::NodeMixin - property :name, :lat, :lng + property :name, :lat, :lon has_n(:connections).to(Hub).relationship(Connection) - R = 6371 * 1000 # Earth radius in meters - RAD = Math::PI / 180 # Converts degrees do radians + #R = 6371 * 1000 # Earth radius in meters + #RAD = Math::PI / 180 # Converts degrees do radians - def to_s - "Hub #{self.name}" - end + #def to_s + #"Hub #{self.name}" + #end - # Resturns distance in meters to given hub - def by_dist(hub) - a, b = [self.lat, self.lng], [hub.lat, hub.lng] - dLat = (b[0] - a[0]) * RAD - dLng = (b[1] - a[1]) * RAD + ## Resturns distance in meters to given hub + #def by_dist(hub) + #a, b = [self.lat, self.lng], [hub.lat, hub.lng] + #dLat = (b[0] - a[0]) * RAD + #dLng = (b[1] - a[1]) * RAD - d = Math.sin(dLat / 2) ** 2 + - Math.cos(a[0] * RAD) * Math.cos(b[0] * RAD) * - Math.sin(dLng / 2) ** 2 + #d = Math.sin(dLat / 2) ** 2 + + #Math.cos(a[0] * RAD) * Math.cos(b[0] * RAD) * + #Math.sin(dLng / 2) ** 2 - R * 2 * Math.atan2(Math.sqrt(d), Math.sqrt(1-d)) - end + #R * 2 * Math.atan2(Math.sqrt(d), Math.sqrt(1-d)) + #end - def by_time(hub) - # estimated time to hub in minutes given average speed of 20km/h - by_dist(hub)/1000/20 *60 - end + #def by_time(hub) + ## estimated time to hub in minutes given average speed of 20km/h + #by_dist(hub)/1000/20 *60 + #end end diff --git a/app/models/stop.rb b/app/models/stop.rb new file mode 100644 index 0000000..9dffd8a --- /dev/null +++ b/app/models/stop.rb @@ -0,0 +1,10 @@ +class Stop + include Neo4j::NodeMixin + + property :id, :lat, :lon + index :id + + has_n(:connections).to(Stop).relationship(Connection) + has_n(:transfers).to(Stop).relationship(Transfer) + +end diff --git a/app/models/transfer.rb b/app/models/transfer.rb new file mode 100644 index 0000000..93dced5 --- /dev/null +++ b/app/models/transfer.rb @@ -0,0 +1,4 @@ +class Transfer + include Neo4j::RelationshipMixin + property :cost # distance in meters +end diff --git a/config/environment.rb b/config/environment.rb index 22e10ab..55706aa 100755 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,11 +1,13 @@ require 'rubygems' -#require 'neo4j' +require 'neo4j' require 'yaml' require 'json' require File.join(File.dirname(__FILE__), 'exceptions') -#$: << File.join(File.dirname(__FILE__), '..', 'app', 'models') -#require 'connection' +$: << File.join(File.dirname(__FILE__), '..', 'app', 'models') +require 'connection' +require 'transfer' +require 'stop' #require 'hub' #require 'a_star'