transcribable 0.0.1

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.
Files changed (55) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/Rakefile +32 -0
  3. data/lib/generators/templates/assets/stylesheets/simple_frame.css +284 -0
  4. data/lib/generators/templates/config/documentcloud.yml +3 -0
  5. data/lib/generators/templates/controller.rb +48 -0
  6. data/lib/generators/templates/migration.rb +15 -0
  7. data/lib/generators/templates/migration_add_columns.rb +13 -0
  8. data/lib/generators/templates/model.rb +10 -0
  9. data/lib/generators/templates/views/_form.html.erb +32 -0
  10. data/lib/generators/templates/views/layouts/simple_frame.html.erb +50 -0
  11. data/lib/generators/templates/views/new.html.erb +28 -0
  12. data/lib/generators/transcribable_generator.rb +59 -0
  13. data/lib/tasks/harvester.rake +23 -0
  14. data/lib/transcribable.rb +156 -0
  15. data/lib/transcribable/railtie.rb +11 -0
  16. data/lib/transcribable/version.rb +3 -0
  17. data/test/dummy/README.rdoc +28 -0
  18. data/test/dummy/Rakefile +6 -0
  19. data/test/dummy/app/assets/javascripts/application.js +13 -0
  20. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  21. data/test/dummy/app/controllers/application_controller.rb +5 -0
  22. data/test/dummy/app/helpers/application_helper.rb +2 -0
  23. data/test/dummy/app/models/filing.rb +6 -0
  24. data/test/dummy/app/models/transcription.rb +10 -0
  25. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  26. data/test/dummy/bin/bundle +3 -0
  27. data/test/dummy/bin/rails +4 -0
  28. data/test/dummy/bin/rake +4 -0
  29. data/test/dummy/config.ru +4 -0
  30. data/test/dummy/config/application.rb +23 -0
  31. data/test/dummy/config/boot.rb +5 -0
  32. data/test/dummy/config/database.yml +25 -0
  33. data/test/dummy/config/environment.rb +5 -0
  34. data/test/dummy/config/environments/development.rb +29 -0
  35. data/test/dummy/config/environments/production.rb +80 -0
  36. data/test/dummy/config/environments/test.rb +36 -0
  37. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  38. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  39. data/test/dummy/config/initializers/inflections.rb +16 -0
  40. data/test/dummy/config/initializers/mime_types.rb +5 -0
  41. data/test/dummy/config/initializers/secret_token.rb +12 -0
  42. data/test/dummy/config/initializers/session_store.rb +3 -0
  43. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  44. data/test/dummy/config/locales/en.yml +23 -0
  45. data/test/dummy/config/routes.rb +56 -0
  46. data/test/dummy/db/schema.rb +19 -0
  47. data/test/dummy/db/test.sqlite3 +0 -0
  48. data/test/dummy/log/test.log +979 -0
  49. data/test/dummy/public/404.html +58 -0
  50. data/test/dummy/public/422.html +58 -0
  51. data/test/dummy/public/500.html +57 -0
  52. data/test/dummy/public/favicon.ico +0 -0
  53. data/test/test_helper.rb +15 -0
  54. data/test/transcribable_test.rb +72 -0
  55. metadata +202 -0
@@ -0,0 +1,50 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ <meta name="viewport" content="width=device-width">
6
+ <%%= stylesheet_link_tag :simple_frame %>
7
+ <%%= yield :css %>
8
+ <!-- switchy -->
9
+ <meta name="viewport" content="width=device-width, maximum-scale=1.6">
10
+ <!-- /switchy -->
11
+ <script src="//s3.amazonaws.com/s3.documentcloud.org/viewer/loader.js"></script>
12
+ <%%= yield :javascripts %>
13
+ <%%= yield :header %>
14
+ <title><%%= yield :title %></title>
15
+ </head>
16
+ <body class="app" id="simple_wrapper">
17
+ <div id="banner">
18
+ <div class="wrapper">
19
+ <div id="banner-identity">
20
+ <p id="app-tagline">My Transcribable App</p>
21
+ </div>
22
+ </div>
23
+ </div>
24
+
25
+ <div id="banner-nav">
26
+ <div class="wrapper">
27
+ <div style="clear:both"></div>
28
+ </div>
29
+ </div>
30
+
31
+ <div id="content">
32
+ <div class="wrapper">
33
+ <div id="modal-screen-coverer"></div>
34
+ <div id="simple_frame">
35
+ <%%= yield %>
36
+ </div>
37
+ </div>
38
+ </div>
39
+
40
+ <div id="fb-root"></div>
41
+ <div id="footer">
42
+ <div class="wrapper">
43
+ <div id="footer-logo">
44
+ <p id="footer-logo-image">My Transcribable App</p>
45
+ </div>
46
+ <div style="clear:both;"></div>
47
+ </div>
48
+ </div>
49
+ </body>
50
+ </html>
@@ -0,0 +1,28 @@
1
+ <%% if flash[:alert] %>
2
+ <div class="warn"><%%= flash[:alert] %></div>
3
+ <%% end %>
4
+ <%% if flash[:notice] %>
5
+ <div class="notice"><%%= flash[:notice] %></div>
6
+ <%% end %>
7
+
8
+ <h1><%%= "Tell Us About This Document" %></h1>
9
+ <div id="left-dc-embed">
10
+ <div id="doc">
11
+ </div>
12
+ </div>
13
+ <div id="right-column">
14
+ <%%= render 'form' %>
15
+ </div>
16
+
17
+ <script>
18
+ var CURRENT_DOCUMENT = DV.load("https://documentcloud.org/documents/<%%= @<%= @table.singularize %>.dc_slug %>.js", {
19
+ container : '#doc',
20
+ width : 660,
21
+ height : 900,
22
+ zoom : 'auto',
23
+ showSidebar : false,
24
+ embedded : true,
25
+ showAnnotations : false
26
+ //afterLoad : setupViewer
27
+ });
28
+ </script>
@@ -0,0 +1,59 @@
1
+ require "rails/generators"
2
+ require "rails/generators/active_record"
3
+ require "transcribable"
4
+
5
+
6
+ class TranscribableGenerator < ActiveRecord::Generators::Base
7
+ include Transcribable
8
+
9
+ desc "Generates transcriptions table"
10
+ # to get around AR Generators requiring a NAME param
11
+ argument :name, type: :string, default: 'random_name'
12
+
13
+ source_root File.expand_path('../templates', __FILE__)
14
+
15
+ def table
16
+ @table = Transcribable.table
17
+ end
18
+
19
+ def transcribable_attrs
20
+ Transcribable.transcribable_attrs
21
+ end
22
+
23
+ def new_columns
24
+ Transcribable.new_columns
25
+ end
26
+
27
+ def copy_files
28
+ # Copies the migration template to db/migrate.
29
+ if Transcribable.new_columns.length > 0
30
+ @migration_name = "add_#{Transcribable.new_columns.keys.join("_and_")}_to_transcriptions"
31
+ migration_template 'migration_add_columns.rb', "db/migrate/#{@migration_name}.rb"
32
+ elsif !ActiveRecord::Base.connection.tables.include?("transcriptions")
33
+ migration_template 'migration.rb', 'db/migrate/create_transcriptions_table.rb'
34
+ end
35
+
36
+ # controller
37
+ template 'controller.rb', 'app/controllers/transcriptions_controller.rb'
38
+
39
+ # model
40
+ template 'model.rb', 'app/models/transcription.rb'
41
+
42
+ # views
43
+ template 'views/layouts/simple_frame.html.erb', 'app/views/layouts/simple_frame.html.erb'
44
+ template 'views/_form.html.erb', 'app/views/transcriptions/_form.html.erb'
45
+ template 'views/edit.html.erb', 'app/views/transcriptions/edit.html.erb'
46
+ template 'views/new.html.erb', 'app/views/transcriptions/new.html.erb'
47
+
48
+ # assets
49
+ template 'assets/stylesheets/simple_frame.css', 'app/assets/stylesheets/simple_frame.css'
50
+
51
+ # config
52
+ template 'config/documentcloud.yml', 'config/documentcloud.yml'
53
+
54
+ # routes
55
+ route "resources :transcriptions, :only => [:new, :create]"
56
+ route "resources :#{@table.to_sym}, :only => [:index, :show]"
57
+ route "root :to => \"#{@table}#index\""
58
+ end
59
+ end
@@ -0,0 +1,23 @@
1
+ namespace :transcribable do
2
+ desc "Harvest documents to transcribe from DocumentCloud"
3
+ task :harvest => :environment do
4
+ require 'rest-client'
5
+ klass = Kernel.const_get(Transcribable.table.classify)
6
+ dc = YAML.load(File.read("#{Rails.root.to_s}/config/documentcloud.yml"))
7
+ dc_project = JSON.parse(RestClient.get("https://#{CGI::escape(dc['email'])}:#{CGI::escape(dc['password'])}@www.documentcloud.org/api/projects.json"))
8
+ dc_project = dc_project['projects'].select {|q| q['id'] == dc['project'].scan(/^\d+/)[0].to_i }[0]
9
+ dc_project['document_ids'].each do |doc_id|
10
+ begin
11
+ dc_doc = JSON.parse(RestClient.get("https://www.documentcloud.org/api/documents/#{doc_id}.json"))['document']
12
+ # this will skip non-public documents
13
+ rescue RestClient::ResourceNotFound
14
+ next
15
+ end
16
+ obj = klass.find_or_initialize_by_url("https://www.documentcloud.org/documents/#{dc_doc['id']}")
17
+ # don't plow over verified docs if rerunning the script
18
+ obj.verified = false if obj.new_record?
19
+ obj.save
20
+ puts "== added #{obj.url}"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,156 @@
1
+ # adds to Filing
2
+ require 'uuid'
3
+
4
+ module Transcribable
5
+ require 'transcribable/railtie'
6
+ extend ActiveSupport::Concern
7
+
8
+ mattr_accessor :table
9
+ mattr_accessor :transcribable_attrs
10
+
11
+ included do
12
+ set_verification_threshhold
13
+ end
14
+
15
+ def self.table
16
+ # transcribable_attrs() "discovers" the columns
17
+ # and table. run it if we don't have a table yet.
18
+ transcribable_attrs if @@table.nil?
19
+ @@table
20
+ end
21
+
22
+ # creates a hash like...
23
+ # {'buyer' => :string, 'amount' => :integer}
24
+ def self.transcribable_attrs
25
+ return @@transcribable_attrs if @@transcribable_attrs
26
+
27
+ @@transcribable_attrs = {}
28
+ ActiveRecord::Base.connection.tables.reject {|t| t == "schema_migrations" }.each do |table|
29
+ klass = Kernel.const_get(table.classify)
30
+ klass.column_names.each do |col|
31
+ if klass.transcribable?(col)
32
+ @@table = table
33
+ @@transcribable_attrs[col] = Filing.columns_hash[col].type
34
+ end
35
+ end
36
+ end
37
+
38
+ @@transcribable_attrs
39
+ end
40
+
41
+ # If we've migrated our master table,
42
+ # and need transcriptions to catch up.
43
+ # Returns a hash like transcribable_attrs
44
+ def self.new_columns
45
+ cols = Transcribable.transcribable_attrs.keys - Transcription.columns_hash.keys
46
+ cols.reduce(Hash.new(0)) do |memo, it|
47
+ memo[it] = Kernel.const_get(@@table.classify).columns_hash[it].type
48
+ memo
49
+ end
50
+ end
51
+
52
+ module ClassMethods
53
+ def transcribable(*args)
54
+ args.each do |k|
55
+ self.columns_hash[k.to_s].instance_variable_set("@transcribable", true)
56
+ end
57
+ include Transcribable::LocalInstanceMethods
58
+ end
59
+
60
+ def transcribable?(_attr)
61
+ self.columns_hash[_attr].instance_variable_get("@transcribable")
62
+ end
63
+
64
+ def skip_verification(*args)
65
+ @@skip_verification = args
66
+ end
67
+
68
+ def skipped_attrs
69
+ @@skip_verification
70
+ end
71
+
72
+ # The number over which people must agree
73
+ # on every attribute to verify a transcription
74
+ def set_verification_threshhold(lvl = 1)
75
+ @@verification_threshhold = lvl
76
+ end
77
+
78
+ def verification_threshhold
79
+ @@verification_threshhold
80
+ end
81
+
82
+ # Attributes that are potential reasons
83
+ # to skip a transcription. If enough people
84
+ # agree to skip, the filing will be marked transcribed.
85
+ def skip_transcription(*args)
86
+ @@skippable = args
87
+ end
88
+
89
+ # Override this to write your own assigner
90
+ # By default, it picks a random filing that
91
+ # a user has not transcribed. If there's nothing left
92
+ # for that user to do, it returns nil. This will get slower
93
+ # the more transcriptions you have, so it'll be a good idea
94
+ # to index the filing_id (or whatever master table foreign key)
95
+ # column in your transcriptions table.
96
+ def assign!(user_id)
97
+ user_transcribed_filings = Transcription.where(:user_id => user_id).map {|q| q["#{self.table_name.downcase.singularize}_id".to_sym] }.uniq
98
+ filings = self.where(:verified => [nil, false])
99
+ if user_transcribed_filings.length > 0
100
+ filings = filings.where("id NOT IN (?)", user_transcribed_filings)
101
+ end
102
+ pick = rand(filings.length - 1)
103
+ filings.length > 0 ? filings[pick] : nil
104
+ end
105
+ end
106
+
107
+ module LocalInstanceMethods
108
+ # Override this to create your own verifier.
109
+ # By default, all "transcribable" attributes
110
+ # need to be agreed on by @@verification_threshhold people.
111
+ def verify!
112
+ chosen = {}
113
+
114
+ attributes = Transcribable.transcribable_attrs.keys.reject do |k|
115
+ self.class.skipped_attrs.include?(k.to_sym)
116
+ end
117
+
118
+ Rails.logger.info("== Verifying #{attributes.join(", ")}")
119
+
120
+ aggregate = transcriptions.reduce({}) do |memo, it|
121
+ attributes.each do |attribute|
122
+ memo[attribute] = memo[attribute] ? memo[attribute] : {}
123
+ memo[attribute][it.instance_values['attributes'][attribute].to_s.upcase] = memo[attribute][it.instance_values['attributes'][attribute].to_s.upcase] ?
124
+ memo[attribute][it.instance_values['attributes'][attribute].to_s.upcase] + 1 : 1
125
+ end
126
+
127
+ memo
128
+ end
129
+
130
+ aggregate.each do |attribute, answers|
131
+ answers.each do |answer, answer_ct|
132
+ if answer_ct > self.class.verification_threshhold
133
+ chosen[attribute] = answers.each.max_by {|k,v| v}.first
134
+ end
135
+ end
136
+ end
137
+
138
+ if chosen.keys.length == attributes.length
139
+ attributes.each do |a|
140
+ self[a] = chosen[a]
141
+ end
142
+ self.verified = true
143
+ self.save
144
+ Rails.logger.info("== Verified #{self.url}")
145
+ else
146
+ Rails.logger.info("== Not verified: #{self.url}")
147
+ end
148
+ end
149
+
150
+ def dc_slug
151
+ url.split(/\//)[-1]
152
+ end
153
+ end
154
+ end
155
+
156
+ ActiveRecord::Base.send :include, Transcribable
@@ -0,0 +1,11 @@
1
+ require 'transcribable'
2
+ require 'rails'
3
+ module Transcribable
4
+ class Railtie < Rails::Railtie
5
+ railtie_name :transcribable
6
+
7
+ rake_tasks do
8
+ load "tasks/harvester.rake"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Transcribable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Dummy::Application.load_tasks
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ # Prevent CSRF attacks by raising an exception.
3
+ # For APIs, you may want to use :null_session instead.
4
+ protect_from_forgery with: :exception
5
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,6 @@
1
+ class Filing < ActiveRecord::Base
2
+ transcribable :buyer, :amount, :notes
3
+ skip_verification :notes
4
+ has_many :transcriptions
5
+ set_verification_threshhold 4
6
+ end
@@ -0,0 +1,10 @@
1
+ class Transcription < ActiveRecord::Base
2
+ belongs_to :user
3
+ belongs_to :filing
4
+
5
+ Transcribable.transcribable_attrs.select {|k,v| [:integer, :float, :decimal].include?(v) }.keys.each do |k|
6
+ define_method("#{k}=") do |val|
7
+ write_attribute k.to_sym, val.to_s.gsub(/[^0-9\.]/,"").to_f
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
6
+ <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>