coupler 0.0.7-java → 0.0.8-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/coupler.gemspec +11 -2
- data/db/migrate/024_add_error_msg_to_jobs.rb +5 -0
- data/db/migrate/025_add_notifications.rb +16 -0
- data/db/migrate/026_add_status_to_resources.rb +7 -0
- data/db/migrate/027_add_notification_id_to_jobs.rb +8 -0
- data/lib/coupler.rb +6 -2
- data/lib/coupler/base.rb +1 -0
- data/lib/coupler/extensions.rb +3 -1
- data/lib/coupler/extensions/imports.rb +11 -14
- data/lib/coupler/extensions/notifications.rb +26 -0
- data/lib/coupler/models.rb +1 -0
- data/lib/coupler/models/comparison.rb +0 -1
- data/lib/coupler/models/connection.rb +0 -1
- data/lib/coupler/models/field.rb +6 -6
- data/lib/coupler/models/import.rb +9 -3
- data/lib/coupler/models/job.rb +64 -20
- data/lib/coupler/models/matcher.rb +0 -1
- data/lib/coupler/models/notification.rb +7 -0
- data/lib/coupler/models/project.rb +0 -1
- data/lib/coupler/models/resource.rb +52 -31
- data/lib/coupler/models/result.rb +0 -1
- data/lib/coupler/models/scenario.rb +0 -1
- data/lib/coupler/models/transformation.rb +2 -3
- data/lib/coupler/models/transformer.rb +0 -1
- data/lib/coupler/scheduler.rb +8 -0
- data/tasks/db.rake +2 -2
- data/test/functional/test_imports.rb +21 -13
- data/test/functional/test_notifications.rb +38 -0
- data/test/integration/test_import.rb +25 -1
- data/test/unit/models/test_field.rb +2 -13
- data/test/unit/models/test_import.rb +4 -0
- data/test/unit/models/test_job.rb +59 -6
- data/test/unit/models/test_notification.rb +17 -0
- data/test/unit/models/test_resource.rb +114 -29
- data/test/unit/models/test_transformation.rb +4 -4
- data/test/unit/test_base.rb +1 -1
- data/test/unit/test_scheduler.rb +11 -0
- data/webroot/public/css/style.css +23 -0
- data/webroot/public/js/application.js +31 -10
- data/webroot/views/jobs/list.erb +2 -0
- data/webroot/views/layout.erb +3 -1
- data/webroot/views/notifications/index.erb +16 -0
- data/webroot/views/resources/list.erb +12 -7
- data/webroot/views/sidebar.erb +2 -0
- metadata +11 -2
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0.0. | 
| 1 | 
            +
            0.0.8
         | 
    
        data/coupler.gemspec
    CHANGED
    
    | @@ -5,12 +5,12 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            Gem::Specification.new do |s|
         | 
| 7 7 | 
             
              s.name = %q{coupler}
         | 
| 8 | 
            -
              s.version = "0.0. | 
| 8 | 
            +
              s.version = "0.0.8"
         | 
| 9 9 | 
             
              s.platform = %q{java}
         | 
| 10 10 |  | 
| 11 11 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 12 12 | 
             
              s.authors = ["Jeremy Stephens"]
         | 
| 13 | 
            -
              s.date = %q{2011- | 
| 13 | 
            +
              s.date = %q{2011-08-16}
         | 
| 14 14 | 
             
              s.default_executable = %q{coupler}
         | 
| 15 15 | 
             
              s.description = %q{Coupler is a (JRuby) desktop application designed to link datasets together}
         | 
| 16 16 | 
             
              s.email = %q{jeremy.f.stephens@vanderbilt.edu}
         | 
| @@ -59,6 +59,10 @@ Gem::Specification.new do |s| | |
| 59 59 | 
             
                "db/migrate/021_add_fields_to_connections.rb",
         | 
| 60 60 | 
             
                "db/migrate/022_remove_database_name_from_resources.rb",
         | 
| 61 61 | 
             
                "db/migrate/023_add_import_jobs.rb",
         | 
| 62 | 
            +
                "db/migrate/024_add_error_msg_to_jobs.rb",
         | 
| 63 | 
            +
                "db/migrate/025_add_notifications.rb",
         | 
| 64 | 
            +
                "db/migrate/026_add_status_to_resources.rb",
         | 
| 65 | 
            +
                "db/migrate/027_add_notification_id_to_jobs.rb",
         | 
| 62 66 | 
             
                "features/connections.feature",
         | 
| 63 67 | 
             
                "features/matchers.feature",
         | 
| 64 68 | 
             
                "features/projects.feature",
         | 
| @@ -86,6 +90,7 @@ Gem::Specification.new do |s| | |
| 86 90 | 
             
                "lib/coupler/extensions/imports.rb",
         | 
| 87 91 | 
             
                "lib/coupler/extensions/jobs.rb",
         | 
| 88 92 | 
             
                "lib/coupler/extensions/matchers.rb",
         | 
| 93 | 
            +
                "lib/coupler/extensions/notifications.rb",
         | 
| 89 94 | 
             
                "lib/coupler/extensions/projects.rb",
         | 
| 90 95 | 
             
                "lib/coupler/extensions/resources.rb",
         | 
| 91 96 | 
             
                "lib/coupler/extensions/results.rb",
         | 
| @@ -104,6 +109,7 @@ Gem::Specification.new do |s| | |
| 104 109 | 
             
                "lib/coupler/models/job.rb",
         | 
| 105 110 | 
             
                "lib/coupler/models/jobify.rb",
         | 
| 106 111 | 
             
                "lib/coupler/models/matcher.rb",
         | 
| 112 | 
            +
                "lib/coupler/models/notification.rb",
         | 
| 107 113 | 
             
                "lib/coupler/models/project.rb",
         | 
| 108 114 | 
             
                "lib/coupler/models/resource.rb",
         | 
| 109 115 | 
             
                "lib/coupler/models/result.rb",
         | 
| @@ -138,6 +144,7 @@ Gem::Specification.new do |s| | |
| 138 144 | 
             
                "test/functional/test_imports.rb",
         | 
| 139 145 | 
             
                "test/functional/test_jobs.rb",
         | 
| 140 146 | 
             
                "test/functional/test_matchers.rb",
         | 
| 147 | 
            +
                "test/functional/test_notifications.rb",
         | 
| 141 148 | 
             
                "test/functional/test_projects.rb",
         | 
| 142 149 | 
             
                "test/functional/test_resources.rb",
         | 
| 143 150 | 
             
                "test/functional/test_results.rb",
         | 
| @@ -158,6 +165,7 @@ Gem::Specification.new do |s| | |
| 158 165 | 
             
                "test/unit/models/test_import.rb",
         | 
| 159 166 | 
             
                "test/unit/models/test_job.rb",
         | 
| 160 167 | 
             
                "test/unit/models/test_matcher.rb",
         | 
| 168 | 
            +
                "test/unit/models/test_notification.rb",
         | 
| 161 169 | 
             
                "test/unit/models/test_project.rb",
         | 
| 162 170 | 
             
                "test/unit/models/test_resource.rb",
         | 
| 163 171 | 
             
                "test/unit/models/test_result.rb",
         | 
| @@ -242,6 +250,7 @@ Gem::Specification.new do |s| | |
| 242 250 | 
             
                "webroot/views/layout.erb",
         | 
| 243 251 | 
             
                "webroot/views/matchers/form.erb",
         | 
| 244 252 | 
             
                "webroot/views/matchers/list.erb",
         | 
| 253 | 
            +
                "webroot/views/notifications/index.erb",
         | 
| 245 254 | 
             
                "webroot/views/projects/form.erb",
         | 
| 246 255 | 
             
                "webroot/views/projects/index.erb",
         | 
| 247 256 | 
             
                "webroot/views/projects/show.erb",
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            Sequel.migration do
         | 
| 2 | 
            +
              up do
         | 
| 3 | 
            +
                create_table(:notifications) do
         | 
| 4 | 
            +
                  primary_key :id
         | 
| 5 | 
            +
                  String :message
         | 
| 6 | 
            +
                  String :url
         | 
| 7 | 
            +
                  TrueClass :seen
         | 
| 8 | 
            +
                  Integer :import_id
         | 
| 9 | 
            +
                  Time :created_at
         | 
| 10 | 
            +
                  Time :updated_at
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
              down do
         | 
| 14 | 
            +
                drop_table(:notifications)
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
    
        data/lib/coupler.rb
    CHANGED
    
    | @@ -21,8 +21,12 @@ require 'json' | |
| 21 21 | 
             
            require 'fastercsv'
         | 
| 22 22 | 
             
            require 'carrierwave/sequel'
         | 
| 23 23 | 
             
            require 'mongrel'
         | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 24 | 
            +
             | 
| 25 | 
            +
            =begin
         | 
| 26 | 
            +
            # Sequel will automatically include these as needed
         | 
| 27 | 
            +
            require 'jdbc/mysql'
         | 
| 28 | 
            +
            require 'jdbc/h2'
         | 
| 29 | 
            +
            =end
         | 
| 26 30 |  | 
| 27 31 | 
             
            module Coupler
         | 
| 28 32 | 
             
              def self.environment
         | 
    
        data/lib/coupler/base.rb
    CHANGED
    
    
    
        data/lib/coupler/extensions.rb
    CHANGED
    
    | @@ -3,6 +3,8 @@ module Coupler | |
| 3 3 | 
             
              end
         | 
| 4 4 | 
             
            end
         | 
| 5 5 |  | 
| 6 | 
            +
            require File.dirname(__FILE__) + "/extensions/exceptions"
         | 
| 7 | 
            +
             | 
| 6 8 | 
             
            require File.dirname(__FILE__) + "/extensions/connections"
         | 
| 7 9 | 
             
            require File.dirname(__FILE__) + "/extensions/projects"
         | 
| 8 10 | 
             
            require File.dirname(__FILE__) + "/extensions/resources"
         | 
| @@ -13,4 +15,4 @@ require File.dirname(__FILE__) + "/extensions/results" | |
| 13 15 | 
             
            require File.dirname(__FILE__) + "/extensions/jobs"
         | 
| 14 16 | 
             
            require File.dirname(__FILE__) + "/extensions/transformers"
         | 
| 15 17 | 
             
            require File.dirname(__FILE__) + "/extensions/imports"
         | 
| 16 | 
            -
            require File.dirname(__FILE__) + "/extensions/ | 
| 18 | 
            +
            require File.dirname(__FILE__) + "/extensions/notifications"
         | 
| @@ -9,19 +9,16 @@ module Coupler | |
| 9 9 |  | 
| 10 10 | 
             
                    app.post "/projects/:project_id/imports" do
         | 
| 11 11 | 
             
                      @import = Models::Import.new(params[:import].merge(:project_id => @project.id))
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                      if @import. | 
| 14 | 
            -
                        @ | 
| 15 | 
            -
                         | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
                          end
         | 
| 22 | 
            -
                        end
         | 
| 12 | 
            +
                      @resource = Models::Resource.new(:name => @import.name, :project_id => @project.id, :status => 'pending')
         | 
| 13 | 
            +
                      if @import.valid? && @resource.valid?
         | 
| 14 | 
            +
                        @import.save
         | 
| 15 | 
            +
                        @resource.import = @import
         | 
| 16 | 
            +
                        @resource.save
         | 
| 17 | 
            +
                        Scheduler.instance.schedule_import_job(@import)
         | 
| 18 | 
            +
                        redirect("/projects/#{@project.id}/resources")
         | 
| 19 | 
            +
                      else
         | 
| 20 | 
            +
                        erb(:'imports/new')
         | 
| 23 21 | 
             
                      end
         | 
| 24 | 
            -
                      erb(:'imports/new')
         | 
| 25 22 | 
             
                    end
         | 
| 26 23 |  | 
| 27 24 | 
             
                    app.get "/projects/:project_id/imports/:id/edit" do
         | 
| @@ -34,8 +31,8 @@ module Coupler | |
| 34 31 | 
             
                      @import = Models::Import[:id => params[:id], :project_id => @project.id]
         | 
| 35 32 | 
             
                      raise ImportNotFound    unless @import
         | 
| 36 33 | 
             
                      @import.repair_duplicate_keys!(params[:delete])
         | 
| 37 | 
            -
                      @resource | 
| 38 | 
            -
                      redirect("/projects/#{@project.id}/resources/#{@resource.id}")
         | 
| 34 | 
            +
                      @import.resource.activate!
         | 
| 35 | 
            +
                      redirect("/projects/#{@project.id}/resources/#{@import.resource.id}")
         | 
| 39 36 | 
             
                    end
         | 
| 40 37 | 
             
                  end
         | 
| 41 38 | 
             
                end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            module Coupler
         | 
| 2 | 
            +
              module Extensions
         | 
| 3 | 
            +
                module Notifications
         | 
| 4 | 
            +
                  include Models
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def self.registered(app)
         | 
| 7 | 
            +
                    app.before do
         | 
| 8 | 
            +
                      Notification.filter(~{:seen => true}, {:url => request.path_info}).update(:seen => true)
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    app.get "/notifications" do
         | 
| 12 | 
            +
                      @notifications = Notification.order(:created_at).all
         | 
| 13 | 
            +
                      erb :"notifications/index"
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    app.get "/notifications/unseen.json" do
         | 
| 17 | 
            +
                      content_type 'application/json'
         | 
| 18 | 
            +
                      notifications = Notification.filter(~{:seen => true}).order(:created_at).all
         | 
| 19 | 
            +
                      notifications.collect do |n|
         | 
| 20 | 
            +
                        { 'id' => n.id, 'message' => n.message, 'url' => n.url, 'created_at' => n.created_at.iso8601 }
         | 
| 21 | 
            +
                      end.to_json
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
    
        data/lib/coupler/models.rb
    CHANGED
    
    | @@ -16,6 +16,7 @@ module Coupler | |
| 16 16 | 
             
                autoload :Scenario,       File.dirname(__FILE__) + "/models/scenario"
         | 
| 17 17 | 
             
                autoload :Transformation, File.dirname(__FILE__) + "/models/transformation"
         | 
| 18 18 | 
             
                autoload :Transformer,    File.dirname(__FILE__) + "/models/transformer"
         | 
| 19 | 
            +
                autoload :Notification,   File.dirname(__FILE__) + "/models/notification"
         | 
| 19 20 | 
             
              end
         | 
| 20 21 | 
             
            end
         | 
| 21 22 |  | 
    
        data/lib/coupler/models/field.rb
    CHANGED
    
    | @@ -1,4 +1,3 @@ | |
| 1 | 
            -
            pp caller
         | 
| 2 1 | 
             
            module Coupler
         | 
| 3 2 | 
             
              module Models
         | 
| 4 3 | 
             
                class Field < Sequel::Model
         | 
| @@ -6,13 +5,14 @@ module Coupler | |
| 6 5 | 
             
                  many_to_one :resource
         | 
| 7 6 | 
             
                  one_to_many :transformations, :key => :source_field_id
         | 
| 8 7 |  | 
| 9 | 
            -
                   | 
| 10 | 
            -
                     | 
| 11 | 
            -
             | 
| 8 | 
            +
                  TYPES = {
         | 
| 9 | 
            +
                    'integer'  => {:type => Integer},
         | 
| 10 | 
            +
                    'string'   => {:type => String, :size => 255},
         | 
| 11 | 
            +
                    'datetime' => {:type => DateTime}
         | 
| 12 | 
            +
                  }
         | 
| 12 13 |  | 
| 13 14 | 
             
                  def local_column_options
         | 
| 14 | 
            -
                    { :name => name, : | 
| 15 | 
            -
                      :primary_key => is_primary_key }
         | 
| 15 | 
            +
                    { :name => name, :primary_key => is_primary_key }.merge!(TYPES[final_type])
         | 
| 16 16 | 
             
                  end
         | 
| 17 17 |  | 
| 18 18 | 
             
                  def final_type
         | 
| @@ -1,4 +1,3 @@ | |
| 1 | 
            -
            pp caller
         | 
| 2 1 | 
             
            module Coupler
         | 
| 3 2 | 
             
              module Models
         | 
| 4 3 | 
             
                class Import < Sequel::Model
         | 
| @@ -15,6 +14,7 @@ module Coupler | |
| 15 14 | 
             
                            \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2} )\z /x
         | 
| 16 15 |  | 
| 17 16 | 
             
                  many_to_one :project
         | 
| 17 | 
            +
                  one_to_one :resource
         | 
| 18 18 | 
             
                  plugin :serialization
         | 
| 19 19 | 
             
                  serialize_attributes :marshal, :field_types, :field_names
         | 
| 20 20 | 
             
                  mount_uploader :data, DataUploader
         | 
| @@ -50,7 +50,7 @@ module Coupler | |
| 50 50 | 
             
                    @preview
         | 
| 51 51 | 
             
                  end
         | 
| 52 52 |  | 
| 53 | 
            -
                  def import!
         | 
| 53 | 
            +
                  def import!(&progress)
         | 
| 54 54 | 
             
                    project.local_database do |db|
         | 
| 55 55 | 
             
                      column_info = []
         | 
| 56 56 | 
             
                      column_names = []
         | 
| @@ -79,7 +79,9 @@ module Coupler | |
| 79 79 | 
             
                      buffer = ImportBuffer.new(column_names, ds)
         | 
| 80 80 | 
             
                      skip = has_headers
         | 
| 81 81 | 
             
                      primary_key_index = field_names.index(primary_key_name)
         | 
| 82 | 
            -
                       | 
| 82 | 
            +
                      io = File.open(data.file.file, 'rb')
         | 
| 83 | 
            +
                      csv = FasterCSV.new(io)
         | 
| 84 | 
            +
                      csv.each do |row|
         | 
| 83 85 | 
             
                        if skip
         | 
| 84 86 | 
             
                          # skip header if necessary
         | 
| 85 87 | 
             
                          skip = false
         | 
| @@ -97,6 +99,10 @@ module Coupler | |
| 97 99 | 
             
                        self.has_duplicate_keys = true if num > 1
         | 
| 98 100 |  | 
| 99 101 | 
             
                        buffer.add(row)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                        if block_given?
         | 
| 104 | 
            +
                          yield io.pos
         | 
| 105 | 
            +
                        end
         | 
| 100 106 | 
             
                      end
         | 
| 101 107 | 
             
                      buffer.flush
         | 
| 102 108 |  | 
    
        data/lib/coupler/models/job.rb
    CHANGED
    
    | @@ -1,4 +1,3 @@ | |
| 1 | 
            -
            pp caller
         | 
| 2 1 | 
             
            module Coupler
         | 
| 3 2 | 
             
              module Models
         | 
| 4 3 | 
             
                class Job < Sequel::Model
         | 
| @@ -6,6 +5,8 @@ module Coupler | |
| 6 5 |  | 
| 7 6 | 
             
                  many_to_one :resource
         | 
| 8 7 | 
             
                  many_to_one :scenario
         | 
| 8 | 
            +
                  many_to_one :import
         | 
| 9 | 
            +
                  many_to_one :notification
         | 
| 9 10 |  | 
| 10 11 | 
             
                  def percent_completed
         | 
| 11 12 | 
             
                    total > 0 ? completed * 100 / total : 0
         | 
| @@ -13,31 +14,74 @@ module Coupler | |
| 13 14 |  | 
| 14 15 | 
             
                  def execute
         | 
| 15 16 | 
             
                    Logger.instance.info("Starting job #{id} (#{name})")
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    opts = {}
         | 
| 16 19 | 
             
                    case name
         | 
| 17 20 | 
             
                    when 'transform'
         | 
| 18 | 
            -
                       | 
| 19 | 
            -
             | 
| 20 | 
            -
                       | 
| 21 | 
            -
             | 
| 22 | 
            -
                        resource.transform! { |n| update(:completed => completed + n) }
         | 
| 23 | 
            -
                        new_status = 'done'
         | 
| 24 | 
            -
                      ensure
         | 
| 25 | 
            -
                        update(:status => new_status, :completed_at => Time.now)
         | 
| 26 | 
            -
                      end
         | 
| 21 | 
            +
                      opts[:total] = resource.source_dataset_count
         | 
| 22 | 
            +
                    when 'import'
         | 
| 23 | 
            +
                      opts[:total] = import.data.file.size
         | 
| 24 | 
            +
                    end
         | 
| 27 25 |  | 
| 28 | 
            -
                     | 
| 29 | 
            -
                       | 
| 26 | 
            +
                    begin
         | 
| 27 | 
            +
                      opts[:status] = 'running'
         | 
| 28 | 
            +
                      opts[:started_at] = Time.now
         | 
| 29 | 
            +
                      update(opts)
         | 
| 30 | 
            +
                      send("execute_#{name}")
         | 
| 31 | 
            +
                    rescue Exception => e
         | 
| 32 | 
            +
                      message = "%s: %s\n  %s" % [e.class.to_s, e.to_s, e.backtrace.join("\n  ")]
         | 
| 33 | 
            +
                      update({
         | 
| 34 | 
            +
                        :status => 'failed',
         | 
| 35 | 
            +
                        :completed_at => Time.now,
         | 
| 36 | 
            +
                        :error_msg => message
         | 
| 37 | 
            +
                      })
         | 
| 38 | 
            +
                      Logger.instance.error("Job #{id} (#{name}) crashed: #{message}")
         | 
| 39 | 
            +
                      raise e
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
                    self.status = 'done'
         | 
| 42 | 
            +
                    self.completed_at = Time.now
         | 
| 43 | 
            +
                    save
         | 
| 44 | 
            +
                    Logger.instance.info("Job #{id} (#{name}) finished successfully")
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  private
         | 
| 30 48 |  | 
| 31 | 
            -
             | 
| 32 | 
            -
                       | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 49 | 
            +
                    def execute_transform
         | 
| 50 | 
            +
                      resource.transform! { |n| update(:completed => completed + n) }
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    def execute_run_scenario
         | 
| 54 | 
            +
                      scenario.run!
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    def execute_import
         | 
| 58 | 
            +
                      last = Time.now # don't slam the database
         | 
| 59 | 
            +
                      result = import.import! do |pos|
         | 
| 60 | 
            +
                        now = Time.now
         | 
| 61 | 
            +
                        if now - last >= 1
         | 
| 62 | 
            +
                          last = now
         | 
| 63 | 
            +
                          update(:completed => pos)
         | 
| 64 | 
            +
                        end
         | 
| 65 | 
            +
                      end
         | 
| 66 | 
            +
                      if result
         | 
| 67 | 
            +
                        # NOTE: This is a bug waiting to happen. Import doesn't verify
         | 
| 68 | 
            +
                        # that it has a resource, but supposedly, it will always have
         | 
| 69 | 
            +
                        # one. The resource gets created at the same time in the
         | 
| 70 | 
            +
                        # controller.
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                        resource = import.resource
         | 
| 73 | 
            +
                        resource.activate!
         | 
| 74 | 
            +
                        self.notification = Notification.create({
         | 
| 75 | 
            +
                          :message => "Import finished successfully",
         | 
| 76 | 
            +
                          :url => "/projects/#{import.project_id}/resources/#{resource.id}"
         | 
| 77 | 
            +
                        })
         | 
| 78 | 
            +
                      else
         | 
| 79 | 
            +
                        self.notification = Notification.create({
         | 
| 80 | 
            +
                          :message => "Import finished, but with errors",
         | 
| 81 | 
            +
                          :url => "/projects/#{import.project_id}/imports/#{import.id}/edit"
         | 
| 82 | 
            +
                        })
         | 
| 37 83 | 
             
                      end
         | 
| 38 84 | 
             
                    end
         | 
| 39 | 
            -
                    Logger.instance.info("Job #{id} (#{name}) finished")
         | 
| 40 | 
            -
                  end
         | 
| 41 85 | 
             
                end
         | 
| 42 86 | 
             
              end
         | 
| 43 87 | 
             
            end
         | 
| @@ -1,4 +1,3 @@ | |
| 1 | 
            -
            pp caller
         | 
| 2 1 | 
             
            module Coupler
         | 
| 3 2 | 
             
              module Models
         | 
| 4 3 | 
             
                class Resource < Sequel::Model
         | 
| @@ -30,8 +29,6 @@ module Coupler | |
| 30 29 | 
             
                  def import=(*args)
         | 
| 31 30 | 
             
                    result = super
         | 
| 32 31 | 
             
                    if new?
         | 
| 33 | 
            -
                      self.project = import.project
         | 
| 34 | 
            -
                      self.name = import.name
         | 
| 35 32 | 
             
                      self.table_name = "import_#{import.id}"
         | 
| 36 33 | 
             
                    end
         | 
| 37 34 | 
             
                    result
         | 
| @@ -106,21 +103,20 @@ module Coupler | |
| 106 103 | 
             
                    end
         | 
| 107 104 | 
             
                  end
         | 
| 108 105 |  | 
| 109 | 
            -
                  def status
         | 
| 110 | 
            -
                    if transformed_with.to_s != transformation_ids.join(",") || transformations_dataset.filter("updated_at > ?", transformed_at).count > 0
         | 
| 111 | 
            -
                      "out_of_date"
         | 
| 112 | 
            -
                    else
         | 
| 113 | 
            -
                      "ok"
         | 
| 114 | 
            -
                    end
         | 
| 115 | 
            -
                  end
         | 
| 116 | 
            -
             | 
| 117 106 | 
             
                  def scenarios
         | 
| 118 107 | 
             
                    Scenario.filter(["resource_1_id = ? OR resource_2_id = ?", id, id]).all
         | 
| 119 108 | 
             
                  end
         | 
| 120 109 |  | 
| 121 | 
            -
                  def  | 
| 110 | 
            +
                  def transformations_updated!
         | 
| 111 | 
            +
                    last_updated_at = transformed_at
         | 
| 112 | 
            +
                    transformation_ids = []
         | 
| 113 | 
            +
             | 
| 122 114 | 
             
                    fields_dataset.update(:local_db_type => nil, :local_type => nil)
         | 
| 123 115 | 
             
                    transformations_dataset.order(:position).each do |transformation|
         | 
| 116 | 
            +
                      transformation_ids << transformation.id
         | 
| 117 | 
            +
                      if last_updated_at.nil? || transformation.updated_at > last_updated_at
         | 
| 118 | 
            +
                        last_updated_at = transformation.updated_at
         | 
| 119 | 
            +
                      end
         | 
| 124 120 | 
             
                      if transformation.source_field_id == transformation.result_field_id
         | 
| 125 121 | 
             
                        source_field = transformation.source_field
         | 
| 126 122 | 
             
                        changes = transformation.field_changes[source_field.id]
         | 
| @@ -130,6 +126,12 @@ module Coupler | |
| 130 126 | 
             
                        })
         | 
| 131 127 | 
             
                      end
         | 
| 132 128 | 
             
                    end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    if transformed_with.to_s != transformation_ids.join(",") || (last_updated_at && last_updated_at > transformed_at)
         | 
| 131 | 
            +
                      update(:status => "out_of_date")
         | 
| 132 | 
            +
                    else
         | 
| 133 | 
            +
                      update(:status => "ok")
         | 
| 134 | 
            +
                    end
         | 
| 133 135 | 
             
                  end
         | 
| 134 136 |  | 
| 135 137 | 
             
                  def transform!(&progress)
         | 
| @@ -137,6 +139,7 @@ module Coupler | |
| 137 139 | 
             
                    create_local_table!
         | 
| 138 140 | 
             
                    _transform(&progress)
         | 
| 139 141 | 
             
                    self.update({
         | 
| 142 | 
            +
                      :status => 'ok',
         | 
| 140 143 | 
             
                      :transformed_at => Time.now,
         | 
| 141 144 | 
             
                      :transformed_with => t_ids
         | 
| 142 145 | 
             
                    })
         | 
| @@ -161,6 +164,13 @@ module Coupler | |
| 161 164 | 
             
                    primary_key_name.to_sym
         | 
| 162 165 | 
             
                  end
         | 
| 163 166 |  | 
| 167 | 
            +
                  # Activate resource that was pending until import was completed
         | 
| 168 | 
            +
                  def activate!
         | 
| 169 | 
            +
                    set_primary_key
         | 
| 170 | 
            +
                    create_fields
         | 
| 171 | 
            +
                    update(:status => "ok")
         | 
| 172 | 
            +
                  end
         | 
| 173 | 
            +
             | 
| 164 174 | 
             
                  private
         | 
| 165 175 | 
             
                    def transformation_ids
         | 
| 166 176 | 
             
                      transformations_dataset.select(:id).order(:id).all.collect(&:id)
         | 
| @@ -170,6 +180,15 @@ module Coupler | |
| 170 180 | 
             
                      Coupler.connection_string("project_#{project.id}")
         | 
| 171 181 | 
             
                    end
         | 
| 172 182 |  | 
| 183 | 
            +
                    def set_primary_key
         | 
| 184 | 
            +
                      source_database do |db|
         | 
| 185 | 
            +
                        schema = db.schema(table_name.to_sym)
         | 
| 186 | 
            +
                        info = schema.detect { |x| x[1][:primary_key] }
         | 
| 187 | 
            +
                        self.primary_key_name = info[0].to_s
         | 
| 188 | 
            +
                        self.primary_key_type = info[1][:type].to_s
         | 
| 189 | 
            +
                      end
         | 
| 190 | 
            +
                    end
         | 
| 191 | 
            +
             | 
| 173 192 | 
             
                    def create_fields
         | 
| 174 193 | 
             
                      source_schema.each do |(name, info)|
         | 
| 175 194 | 
             
                        add_field({
         | 
| @@ -233,19 +252,21 @@ module Coupler | |
| 233 252 | 
             
                      validates_presence [:project_id, :name]
         | 
| 234 253 | 
             
                      validates_presence :slug
         | 
| 235 254 | 
             
                      validates_unique [:name, :project_id], [:slug, :project_id]
         | 
| 236 | 
            -
             | 
| 237 | 
            -
             | 
| 238 | 
            -
             | 
| 239 | 
            -
                         | 
| 240 | 
            -
                           | 
| 241 | 
            -
             | 
| 242 | 
            -
                             | 
| 243 | 
            -
             | 
| 244 | 
            -
                             | 
| 245 | 
            -
             | 
| 246 | 
            -
                               | 
| 247 | 
            -
             | 
| 248 | 
            -
                               | 
| 255 | 
            +
             | 
| 256 | 
            +
                      if status != 'pending'
         | 
| 257 | 
            +
                        validates_presence [:table_name]
         | 
| 258 | 
            +
                        if errors.on(:table_name).nil?
         | 
| 259 | 
            +
                          source_database do |db|
         | 
| 260 | 
            +
                            sym = self.table_name.to_sym
         | 
| 261 | 
            +
                            if !db.tables.include?(sym)
         | 
| 262 | 
            +
                              errors.add(:table_name, "is invalid")
         | 
| 263 | 
            +
                            else
         | 
| 264 | 
            +
                              keys = db.schema(sym).select { |info| info[1][:primary_key] }
         | 
| 265 | 
            +
                              if keys.empty?
         | 
| 266 | 
            +
                                errors.add(:table_name, "doesn't have a primary key")
         | 
| 267 | 
            +
                              elsif keys.length > 1
         | 
| 268 | 
            +
                                errors.add(:table_name, "has too many primary keys")
         | 
| 269 | 
            +
                              end
         | 
| 249 270 | 
             
                            end
         | 
| 250 271 | 
             
                          end
         | 
| 251 272 | 
             
                        end
         | 
| @@ -257,11 +278,9 @@ module Coupler | |
| 257 278 | 
             
                        # NOTE: I'm doing this instead of using before_create because
         | 
| 258 279 | 
             
                        # serialization happens in before_save, which gets called before
         | 
| 259 280 | 
             
                        # the before_create hook
         | 
| 260 | 
            -
                         | 
| 261 | 
            -
                           | 
| 262 | 
            -
                           | 
| 263 | 
            -
                          self.primary_key_name = info[0].to_s
         | 
| 264 | 
            -
                          self.primary_key_type = info[1][:type].to_s
         | 
| 281 | 
            +
                        if status != "pending"
         | 
| 282 | 
            +
                          set_primary_key
         | 
| 283 | 
            +
                          self.status = "ok"
         | 
| 265 284 | 
             
                        end
         | 
| 266 285 | 
             
                      end
         | 
| 267 286 | 
             
                      super
         | 
| @@ -269,7 +288,9 @@ module Coupler | |
| 269 288 |  | 
| 270 289 | 
             
                    def after_create
         | 
| 271 290 | 
             
                      super
         | 
| 272 | 
            -
                       | 
| 291 | 
            +
                      if status != "pending"
         | 
| 292 | 
            +
                        create_fields
         | 
| 293 | 
            +
                      end
         | 
| 273 294 | 
             
                    end
         | 
| 274 295 |  | 
| 275 296 | 
             
                    def after_destroy
         |