transcribable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/Rakefile +32 -0
- data/lib/generators/templates/assets/stylesheets/simple_frame.css +284 -0
- data/lib/generators/templates/config/documentcloud.yml +3 -0
- data/lib/generators/templates/controller.rb +48 -0
- data/lib/generators/templates/migration.rb +15 -0
- data/lib/generators/templates/migration_add_columns.rb +13 -0
- data/lib/generators/templates/model.rb +10 -0
- data/lib/generators/templates/views/_form.html.erb +32 -0
- data/lib/generators/templates/views/layouts/simple_frame.html.erb +50 -0
- data/lib/generators/templates/views/new.html.erb +28 -0
- data/lib/generators/transcribable_generator.rb +59 -0
- data/lib/tasks/harvester.rake +23 -0
- data/lib/transcribable.rb +156 -0
- data/lib/transcribable/railtie.rb +11 -0
- data/lib/transcribable/version.rb +3 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/filing.rb +6 -0
- data/test/dummy/app/models/transcription.rb +10 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/db/schema.rb +19 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +979 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/test_helper.rb +15 -0
- data/test/transcribable_test.rb +72 -0
- 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,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>.
|
data/test/dummy/Rakefile
ADDED
@@ -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,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>
|