e621_export_downloader 0.0.5 → 0.0.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21a24836744a301b7a69e15bbd3b55ffce5dae10d651182f7243539f8c84fa51
4
- data.tar.gz: ddaad209b463b39855e05761f368454be981940d4092f5c759be90c237012fff
3
+ metadata.gz: f2a75639fe81247f91a36b903b70b6845ea89a1d5e12bab6d4fed6b55a3d22f4
4
+ data.tar.gz: 211d90dffba5ffcf495babdc9d0382c54143a64cfca483637d4d70c283b9934e
5
5
  SHA512:
6
- metadata.gz: fe1b7227c38ac328ebbd51b32f6ae24b4631d4ffadb5e83c3e3ec3768bcea5ece054ed9e497c2952b2e4d4cbb802727aafc48553bfba5d58795db85eddc33832
7
- data.tar.gz: b12433e7f2403687773793db0a0c61abf4665b20b1909576c15828ae6c57d94447abdca44b4a1f2aa86c96ff66a5c33b1eaa9c37633d4de8dc376409d5cd09a7
6
+ metadata.gz: f254e3bfaac75803d596b6668c1466610111be7d3ed716c4678263883ca0c174d837f395305adf9a72a6eb2be6f5ffcd9a57ca8d8208268c8229c753b16c0111
7
+ data.tar.gz: e4ec86ff5d0adb542907aaf7c750342b16743f2e171278e3eab02a842dfb36f46afaab0687d00d57df81a0c0581065e6d9a2553b459e0ef85dccbf61d8bc24b1
data/README.md CHANGED
@@ -134,6 +134,66 @@ e621-export-downloader --cache # enable caching
134
134
  e621-export-downloader --no-cache # disable caching (default)
135
135
  ```
136
136
 
137
+ ## Rails Integration
138
+
139
+ Run the install generator to copy the migration and create the initializer:
140
+
141
+ ```bash
142
+ bin/rails g e621_export_downloader:install
143
+ bin/rails db:migrate
144
+ ```
145
+
146
+ Two options are available:
147
+
148
+ | Option | Default | Description |
149
+ |---|---|---|
150
+ | `--schema NAME` | `e621` | Schema or prefix name for the tables |
151
+ | `--table-format FORMAT` | `schema` | `schema` — tables in a PostgreSQL schema (`e621.artists`); `prefix` — tables in the default schema with a name prefix (`e621_artists`) |
152
+
153
+ ```bash
154
+ # custom schema name
155
+ bin/rails g e621_export_downloader:install --schema my_e621
156
+
157
+ # prefix style instead of a dedicated schema
158
+ bin/rails g e621_export_downloader:install --table-format prefix
159
+
160
+ # combine both
161
+ bin/rails g e621_export_downloader:install --schema my_e621 --table-format prefix
162
+ ```
163
+
164
+ This generates:
165
+ - `db/migrate/<timestamp>_create_e621_tables.rb` — creates all e621 tables (and the schema when using `--table-format schema`)
166
+ - `config/initializers/e621_models.rb` — requires the ActiveRecord models
167
+
168
+ The following ActiveRecord models are then available:
169
+
170
+ | Model | Table |
171
+ |---------------------------|-----------------------------|
172
+ | `E621::Artist` | `e621.artists` |
173
+ | `E621::BulkUpdateRequest` | `e621.bulk_update_requests` |
174
+ | `E621::Pool` | `e621.pools` |
175
+ | `E621::Post` | `e621.posts` |
176
+ | `E621::PostReplacement` | `e621.post_replacements` |
177
+ | `E621::PostVersion` | `e621.post_versions` |
178
+ | `E621::Tag` | `e621.tags` |
179
+ | `E621::TagAlias` | `e621.tag_aliases` |
180
+ | `E621::TagImplication` | `e621.tag_implications` |
181
+ | `E621::WikiPage` | `e621.wiki_pages` |
182
+
183
+ Each model provides `upsert_from_export` and `upsert_all_from_export` for persisting parsed export records:
184
+
185
+ ```ruby
186
+ client = E621ExportDownloader::Client.new
187
+
188
+ client.get("posts").read do |post|
189
+ E621::Post.upsert_from_export(post)
190
+ end
191
+
192
+ # or in batch
193
+ posts = client.get("posts").read_all
194
+ E621::Post.upsert_all_from_export(posts)
195
+ ```
196
+
137
197
  ## ActiveJob Integration
138
198
 
139
199
  `E621ExportDownloader::Serializers::ActiveJob` allows `E621ExportDownloader::Types` values to be passed as ActiveJob arguments.
data/Rakefile CHANGED
@@ -1,8 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require("bundler/gem_tasks")
4
+ require("rake/testtask")
4
5
  require("rubocop/rake_task")
5
6
 
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.pattern = "test/**/*_test.rb"
9
+ end
10
+
6
11
  RuboCop::RakeTask.new
7
12
 
8
13
  task(default: :rubocop)
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateE621Tables < ActiveRecord::Migration[7.1]
4
+ def change
5
+ reversible do |r|
6
+ r.up { execute("CREATE SCHEMA e621") }
7
+ r.down { execute("DROP SCHEMA e621 CASCADE") }
8
+ end
9
+
10
+ create_table(:e621_artists, id: :bigint, default: nil, force: :cascade) do |t|
11
+ t.datetime(:created_at, null: false)
12
+ t.bigint(:creator_id, null: false)
13
+ t.string(:group_name)
14
+ t.boolean(:is_active, null: false)
15
+ t.boolean(:is_locked, null: false)
16
+ t.bigint(:linked_user_id)
17
+ t.string(:name, null: false)
18
+ t.text(:other_names, array: true, null: false, default: [])
19
+ t.datetime(:updated_at, null: false)
20
+ t.text(:urls, array: true, null: false, default: [])
21
+ end
22
+
23
+ add_index(:e621_artists, :name, unique: true)
24
+ add_index(:e621_artists, :creator_id)
25
+
26
+ create_table(:e621_bulk_update_requests, id: :bigint, default: nil, force: :cascade) do |t|
27
+ t.bigint(:approver_id)
28
+ t.datetime(:created_at, null: false)
29
+ t.bigint(:forum_topic_id)
30
+ t.text(:script, null: false)
31
+ t.string(:status, null: false)
32
+ t.string(:title)
33
+ t.datetime(:updated_at, null: false)
34
+ t.bigint(:user_id, null: false)
35
+ end
36
+
37
+ add_index(:e621_bulk_update_requests, :user_id)
38
+ add_index(:e621_bulk_update_requests, :status)
39
+
40
+ create_table(:e621_pools, id: :bigint, default: nil, force: :cascade) do |t|
41
+ t.string(:category, null: false)
42
+ t.datetime(:created_at, null: false)
43
+ t.bigint(:creator_id, null: false)
44
+ t.text(:description, null: false)
45
+ t.boolean(:is_active, null: false)
46
+ t.string(:name, null: false)
47
+ t.bigint(:post_ids, array: true, null: false, default: [])
48
+ t.datetime(:updated_at)
49
+ end
50
+
51
+ add_index(:e621_pools, :creator_id)
52
+ add_index(:e621_pools, :name)
53
+
54
+ create_table(:e621_posts, id: :bigint, default: nil, force: :cascade) do |t|
55
+ t.bigint(:approver_id)
56
+ t.bigint(:change_seq, null: false)
57
+ t.integer(:comment_count, null: false)
58
+ t.datetime(:created_at, null: false)
59
+ t.text(:description, null: false)
60
+ t.integer(:down_score, null: false)
61
+ t.float(:duration)
62
+ t.integer(:fav_count, null: false)
63
+ t.string(:file_ext, null: false)
64
+ t.bigint(:file_size, null: false)
65
+ t.integer(:image_height, null: false)
66
+ t.integer(:image_width, null: false)
67
+ t.boolean(:is_deleted, null: false)
68
+ t.boolean(:is_flagged, null: false)
69
+ t.boolean(:is_note_locked, null: false)
70
+ t.boolean(:is_pending, null: false)
71
+ t.boolean(:is_rating_locked, null: false)
72
+ t.boolean(:is_status_locked, null: false)
73
+ t.text(:locked_tags, null: false)
74
+ t.string(:md5)
75
+ t.bigint(:parent_id)
76
+ t.string(:rating, null: false)
77
+ t.integer(:score, null: false)
78
+ t.text(:sources, array: true, null: false, default: [])
79
+ t.text(:tags, array: true, null: false, default: [])
80
+ t.integer(:up_score, null: false)
81
+ t.datetime(:updated_at)
82
+ t.bigint(:uploader_id)
83
+ end
84
+
85
+ add_index(:e621_posts, :md5, unique: true, where: "md5 IS NOT NULL")
86
+ add_index(:e621_posts, :uploader_id)
87
+ add_index(:e621_posts, :rating)
88
+ add_index(:e621_posts, :is_deleted)
89
+
90
+ create_table(:e621_post_replacements, id: :bigint, default: nil, force: :cascade) do |t|
91
+ t.bigint(:approver_id)
92
+ t.datetime(:created_at, null: false)
93
+ t.bigint(:creator_id, null: false)
94
+ t.string(:file_ext, null: false)
95
+ t.string(:file_name, null: false)
96
+ t.bigint(:file_size, null: false)
97
+ t.integer(:image_height, null: false)
98
+ t.integer(:image_width, null: false)
99
+ t.string(:md5, null: false)
100
+ t.bigint(:post_id, null: false)
101
+ t.text(:reason, null: false)
102
+ t.text(:source)
103
+ t.string(:status, null: false)
104
+ t.datetime(:updated_at, null: false)
105
+ end
106
+
107
+ add_index(:e621_post_replacements, :post_id)
108
+ add_index(:e621_post_replacements, :creator_id)
109
+ add_index(:e621_post_replacements, :status)
110
+
111
+ create_table(:e621_post_versions, id: :bigint, default: nil, force: :cascade) do |t|
112
+ t.text(:added_locked_tags, array: true, null: false, default: [])
113
+ t.text(:added_tags, array: true, null: false, default: [])
114
+ t.text(:description)
115
+ t.boolean(:description_changed, null: false)
116
+ t.text(:locked_tags)
117
+ t.boolean(:parent_changed, null: false)
118
+ t.bigint(:parent_id)
119
+ t.string(:rating)
120
+ t.boolean(:rating_changed, null: false)
121
+ t.text(:reason)
122
+ t.text(:removed_locked_tags, array: true, null: false, default: [])
123
+ t.text(:removed_tags, array: true, null: false, default: [])
124
+ t.text(:source)
125
+ t.boolean(:source_changed, null: false)
126
+ t.text(:tags)
127
+ t.datetime(:updated_at, null: false)
128
+ t.bigint(:updater_id, null: false)
129
+ t.integer(:version, null: false)
130
+ end
131
+
132
+ add_index(:e621_post_versions, :updater_id)
133
+ add_index(:e621_post_versions, :updated_at)
134
+
135
+ create_table(:e621_tag_aliases, id: :bigint, default: nil, force: :cascade) do |t|
136
+ t.string(:antecedent_name, null: false)
137
+ t.string(:consequent_name, null: false)
138
+ t.datetime(:created_at)
139
+ t.string(:status, null: false)
140
+ end
141
+
142
+ add_index(:e621_tag_aliases, :antecedent_name)
143
+ add_index(:e621_tag_aliases, :consequent_name)
144
+ add_index(:e621_tag_aliases, :status)
145
+
146
+ create_table(:e621_tag_implications, id: :bigint, default: nil, force: :cascade) do |t|
147
+ t.string(:antecedent_name, null: false)
148
+ t.string(:consequent_name, null: false)
149
+ t.datetime(:created_at)
150
+ t.string(:status, null: false)
151
+ end
152
+
153
+ add_index(:e621_tag_implications, :antecedent_name)
154
+ add_index(:e621_tag_implications, :consequent_name)
155
+ add_index(:e621_tag_implications, :status)
156
+
157
+ create_table(:e621_tags, id: :bigint, default: nil, force: :cascade) do |t|
158
+ t.string(:category, null: false)
159
+ t.string(:name, null: false)
160
+ t.integer(:post_count, null: false)
161
+ end
162
+
163
+ add_index(:e621_tags, :name, unique: true)
164
+ add_index(:e621_tags, :category)
165
+
166
+ create_table(:e621_wiki_pages, id: :bigint, default: nil, force: :cascade) do |t|
167
+ t.text(:body, null: false)
168
+ t.datetime(:created_at, null: false)
169
+ t.bigint(:creator_id)
170
+ t.boolean(:is_locked, null: false)
171
+ t.string(:title, null: false)
172
+ t.datetime(:updated_at)
173
+ t.bigint(:uploader_id)
174
+ end
175
+
176
+ add_index(:e621_wiki_pages, :title, unique: true)
177
+ add_index(:e621_wiki_pages, :creator_id)
178
+ end
179
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class Artist < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.artists"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::Artist).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::Artist]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ created_at: record.created_at,
27
+ creator_id: record.creator_id,
28
+ group_name: record.group_name,
29
+ is_active: record.is_active,
30
+ is_locked: record.is_locked,
31
+ linked_user_id: record.linked_user_id,
32
+ name: record.name,
33
+ other_names: record.other_names,
34
+ updated_at: record.updated_at,
35
+ urls: record.urls,
36
+ }
37
+ end)
38
+ end
39
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class BulkUpdateRequest < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.bulk_update_requests"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::BulkUpdateRequest).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::BulkUpdateRequest]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ approver_id: record.approver_id,
27
+ created_at: record.created_at,
28
+ forum_topic_id: record.forum_topic_id,
29
+ script: record.script,
30
+ status: record.status,
31
+ title: record.title,
32
+ updated_at: record.updated_at,
33
+ user_id: record.user_id,
34
+ }
35
+ end)
36
+ end
37
+ end
data/lib/e621/pool.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class Pool < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.pools"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::Pool).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::Pool]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ category: record.category,
27
+ created_at: record.created_at,
28
+ creator_id: record.creator_id,
29
+ description: record.description,
30
+ is_active: record.is_active,
31
+ name: record.name,
32
+ post_ids: record.post_ids,
33
+ updated_at: record.updated_at,
34
+ }
35
+ end)
36
+ end
37
+ end
data/lib/e621/post.rb ADDED
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class Post < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.posts"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::Post).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::Post]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ approver_id: record.approver_id,
27
+ change_seq: record.change_seq,
28
+ comment_count: record.comment_count,
29
+ created_at: record.created_at,
30
+ description: record.description,
31
+ down_score: record.down_score,
32
+ duration: record.duration,
33
+ fav_count: record.fav_count,
34
+ file_ext: record.file_ext,
35
+ file_size: record.file_size,
36
+ image_height: record.image_height,
37
+ image_width: record.image_width,
38
+ is_deleted: record.is_deleted,
39
+ is_flagged: record.is_flagged,
40
+ is_note_locked: record.is_note_locked,
41
+ is_pending: record.is_pending,
42
+ is_rating_locked: record.is_rating_locked,
43
+ is_status_locked: record.is_status_locked,
44
+ locked_tags: record.locked_tags,
45
+ md5: record.md5,
46
+ parent_id: record.parent_id,
47
+ rating: record.rating,
48
+ score: record.score,
49
+ sources: record.sources,
50
+ tags: record.tags,
51
+ up_score: record.up_score,
52
+ updated_at: record.updated_at,
53
+ uploader_id: record.uploader_id,
54
+ }
55
+ end)
56
+ end
57
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class PostReplacement < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.post_replacements"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::PostReplacement).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::PostReplacement]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ approver_id: record.approver_id,
27
+ created_at: record.created_at,
28
+ creator_id: record.creator_id,
29
+ file_ext: record.file_ext,
30
+ file_name: record.file_name,
31
+ file_size: record.file_size,
32
+ image_height: record.image_height,
33
+ image_width: record.image_width,
34
+ md5: record.md5,
35
+ post_id: record.post_id,
36
+ reason: record.reason,
37
+ source: record.source,
38
+ status: record.status,
39
+ updated_at: record.updated_at,
40
+ }
41
+ end)
42
+ end
43
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class PostVersion < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.post_versions"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::PostVersion).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::PostVersion]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ added_locked_tags: record.added_locked_tags,
27
+ added_tags: record.added_tags,
28
+ description: record.description,
29
+ description_changed: record.description_changed,
30
+ locked_tags: record.locked_tags,
31
+ parent_changed: record.parent_changed,
32
+ parent_id: record.parent_id,
33
+ rating: record.rating,
34
+ rating_changed: record.rating_changed,
35
+ reason: record.reason,
36
+ removed_locked_tags: record.removed_locked_tags,
37
+ removed_tags: record.removed_tags,
38
+ source: record.source,
39
+ source_changed: record.source_changed,
40
+ tags: record.tags,
41
+ updated_at: record.updated_at,
42
+ updater_id: record.updater_id,
43
+ version: record.version,
44
+ }
45
+ end)
46
+ end
47
+ end
data/lib/e621/tag.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class Tag < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.tags"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::Tag).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::Tag]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ category: record.category,
27
+ name: record.name,
28
+ post_count: record.post_count,
29
+ }
30
+ end)
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class TagAlias < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.tag_aliases"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::TagAlias).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::TagAlias]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ antecedent_name: record.antecedent_name,
27
+ consequent_name: record.consequent_name,
28
+ created_at: record.created_at,
29
+ status: record.status,
30
+ }
31
+ end)
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class TagImplication < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.tag_implications"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::TagImplication).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::TagImplication]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ antecedent_name: record.antecedent_name,
27
+ consequent_name: record.consequent_name,
28
+ created_at: record.created_at,
29
+ status: record.status,
30
+ }
31
+ end)
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require("active_record")
5
+
6
+ module E621
7
+ class WikiPage < ActiveRecord::Base
8
+ extend(T::Sig)
9
+
10
+ self.table_name = "e621.wiki_pages"
11
+ self.record_timestamps = false
12
+
13
+ sig { params(record: E621ExportDownloader::Models::WikiPage).returns(T.untyped) }
14
+ def self.upsert_from_export(record)
15
+ upsert(attributes_from_export(record))
16
+ end
17
+
18
+ sig { params(records: T::Array[E621ExportDownloader::Models::WikiPage]).returns(T.untyped) }
19
+ def self.upsert_all_from_export(records)
20
+ upsert_all(records.map { |r| attributes_from_export(r) })
21
+ end
22
+
23
+ private_class_method(def self.attributes_from_export(record)
24
+ {
25
+ id: record.id,
26
+ body: record.body,
27
+ created_at: record.created_at,
28
+ creator_id: record.creator_id,
29
+ is_locked: record.is_locked,
30
+ title: record.title,
31
+ updated_at: record.updated_at,
32
+ uploader_id: record.uploader_id,
33
+ }
34
+ end)
35
+ end
36
+ end
@@ -114,7 +114,7 @@ module E621ExportDownloader
114
114
  @is_note_locked = T.let(record["is_note_locked"] == "t", T::Boolean)
115
115
  @is_pending = T.let(record["is_pending"] == "t", T::Boolean)
116
116
  @is_rating_locked = T.let(record["is_rating_locked"] == "t", T::Boolean)
117
- @is_status_locked = T.let(record["is_rating_locked"] == "t", T::Boolean)
117
+ @is_status_locked = T.let(record["is_status_locked"] == "t", T::Boolean)
118
118
  @locked_tags = T.let(T.must(record["locked_tags"]), String)
119
119
  @md5 = T.let(T.must(record["md5"]).empty? ? nil : record["md5"], T.nilable(String))
120
120
  @parent_id = T.let(T.must(record["parent_id"]).empty? ? nil : record["parent_id"].to_i, T.nilable(Integer))
@@ -28,7 +28,7 @@ module E621ExportDownloader
28
28
  attr_reader(:updated_at)
29
29
 
30
30
  sig { returns(T.nilable(Integer)) }
31
- attr_reader(:uploader_id)
31
+ attr_reader(:updater_id)
32
32
 
33
33
  sig { params(record: T::Hash[String, String]).void }
34
34
  def initialize(record)
@@ -40,20 +40,20 @@ module E621ExportDownloader
40
40
  @is_locked = T.let(record["is_locked"] == "t", T::Boolean)
41
41
  @title = T.let(T.must(record["title"]), String)
42
42
  @updated_at = T.let(T.must(record["updated_at"]).empty? ? nil : DateTime.parse(record["updated_at"]), T.nilable(DateTime))
43
- @uploader_id = T.let(T.must(record["uploader_id"]).empty? ? nil : record["uploader_id"].to_i, T.nilable(Integer))
43
+ @updater_id = T.let(T.must(record["updater_id"]).empty? ? nil : record["updater_id"].to_i, T.nilable(Integer))
44
44
  end
45
45
 
46
46
  sig { params(_args: T.untyped).returns(String) }
47
47
  def to_json(*_args)
48
48
  {
49
- body: @body,
50
- created_at: @created_at,
51
- creator_id: @creator_id,
52
- id: @id,
53
- is_locked: @is_locked,
54
- title: @title,
55
- updated_at: @updated_at,
56
- uploader_id: @uploader_id,
49
+ body: @body,
50
+ created_at: @created_at,
51
+ creator_id: @creator_id,
52
+ id: @id,
53
+ is_locked: @is_locked,
54
+ title: @title,
55
+ updated_at: @updated_at,
56
+ updater_id: @updater_id,
57
57
  }.to_json
58
58
  end
59
59
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../e621/artist")
4
+ require_relative("../e621/bulk_update_request")
5
+ require_relative("../e621/pool")
6
+ require_relative("../e621/post")
7
+ require_relative("../e621/post_replacement")
8
+ require_relative("../e621/post_version")
9
+ require_relative("../e621/tag")
10
+ require_relative("../e621/tag_alias")
11
+ require_relative("../e621/tag_implication")
12
+ require_relative("../e621/wiki_page")
@@ -2,6 +2,10 @@
2
2
 
3
3
  module E621ExportDownloader
4
4
  class Railtie < Rails::Railtie
5
+ rake_tasks do
6
+ load(File.expand_path("../tasks/e621_export_downloader.rake", __dir__))
7
+ end
8
+
5
9
  initializer("e621_export_downloader.register_active_job_serializer") do
6
10
  ActiveSupport.on_load(:active_job) do
7
11
  require("e621_export_downloader/serializers/active_job")
@@ -4,7 +4,7 @@
4
4
  # loaded by bundler
5
5
  module E621ExportDownloader
6
6
  module Constants
7
- VERSION = "0.0.5"
7
+ VERSION = "0.0.6"
8
8
  WEBSITE = "https://github.com/DonovanDMC/E621ExportDownloader.rb"
9
9
  end
10
10
  end
@@ -8,6 +8,9 @@ loader.inflector.inflect({ "api_export_data" => "APIExportData" })
8
8
  loader.ignore("#{__dir__}/e621_export_downloader/version.rb")
9
9
  loader.ignore("#{__dir__}/e621_export_downloader/railtie.rb")
10
10
  loader.ignore("#{__dir__}/e621_export_downloader/serializers/active_job.rb")
11
+ loader.ignore("#{__dir__}/e621_export_downloader/models.rb")
12
+ loader.ignore("#{__dir__}/e621")
13
+ loader.ignore("#{__dir__}/generators")
11
14
  loader.setup
12
15
 
13
16
  require("e621_export_downloader/railtie") if defined?(Rails::Railtie)
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require("rails/generators")
4
+ require("rails/generators/active_record")
5
+
6
+ module E621ExportDownloader
7
+ #
8
+ # Rails generator used for setting up E621ExportDownloader tables in a Rails application.
9
+ # Run it with +bin/rails g e621_export_downloader:install+ in your console.
10
+ #
11
+ class InstallGenerator < Rails::Generators::Base
12
+ include(ActiveRecord::Generators::Migration)
13
+
14
+ TEMPLATES = File.join(File.dirname(__FILE__), "templates")
15
+ source_paths << TEMPLATES
16
+
17
+ class_option(:schema, type: :string, default: "e621",
18
+ desc: "Schema or prefix name for the e621 tables")
19
+ class_option(:table_format, type: :string, default: "schema",
20
+ desc: "Table reference format: 'schema' uses a PostgreSQL schema (e621.artists), 'prefix' uses a name prefix (e621_artists)")
21
+
22
+ def validate_options
23
+ return if %w[schema prefix].include?(options[:table_format])
24
+ abort("--table-format must be 'schema' or 'prefix', got '#{options[:table_format]}'")
25
+ end
26
+
27
+ def create_migration_file
28
+ migration_template("migrations/create_e621_tables.rb.erb", File.join(db_migrate_path, "create_e621_tables.rb"))
29
+ end
30
+
31
+ def create_initializer
32
+ template("initializers/e621_models.rb.erb", "config/initializers/e621_models.rb")
33
+ end
34
+
35
+ private
36
+
37
+ def schema
38
+ options[:schema]
39
+ end
40
+
41
+ def use_schema?
42
+ options[:table_format] == "schema"
43
+ end
44
+
45
+ def table_ref(name)
46
+ use_schema? ? "\"#{schema}.#{name}\"" : ":#{schema}_#{name}"
47
+ end
48
+
49
+ def table_name_for(name)
50
+ use_schema? ? "#{schema}.#{name}" : "#{schema}_#{name}"
51
+ end
52
+
53
+ def migration_version
54
+ "[#{ActiveRecord::VERSION::STRING.to_f}]"
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # E621::Artist - <%= table_name_for("artists") %>
4
+ # E621::BulkUpdateRequest - <%= table_name_for("bulk_update_requests") %>
5
+ # E621::Pool - <%= table_name_for("pools") %>
6
+ # E621::Post - <%= table_name_for("posts") %>
7
+ # E621::PostReplacement - <%= table_name_for("post_replacements") %>
8
+ # E621::PostVersion - <%= table_name_for("post_versions") %>
9
+ # E621::Tag - <%= table_name_for("tags") %>
10
+ # E621::TagAlias - <%= table_name_for("tag_aliases") %>
11
+ # E621::TagImplication - <%= table_name_for("tag_implications") %>
12
+ # E621::WikiPage - <%= table_name_for("wiki_pages") %>
13
+ require("e621_export_downloader/models")
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateE621Tables < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ <% if use_schema? -%>
6
+ reversible do |r|
7
+ r.up { execute("CREATE SCHEMA <%= schema %>") }
8
+ r.down { execute("DROP SCHEMA <%= schema %> CASCADE") }
9
+ end
10
+
11
+ <% end -%>
12
+ create_table(<%= table_ref("artists") %>, id: :bigint, default: nil, force: :cascade) do |t|
13
+ t.datetime(:created_at, null: false)
14
+ t.bigint(:creator_id, null: false, index: true)
15
+ t.string(:group_name)
16
+ t.boolean(:is_active, null: false)
17
+ t.boolean(:is_locked, null: false)
18
+ t.bigint(:linked_user_id)
19
+ t.string(:name, null: false, index: { unique: true })
20
+ t.text(:other_names, array: true, null: false, default: [])
21
+ t.datetime(:updated_at, null: false)
22
+ t.text(:urls, array: true, null: false, default: [])
23
+ end
24
+
25
+ create_table(<%= table_ref("bulk_update_requests") %>, id: :bigint, default: nil, force: :cascade) do |t|
26
+ t.bigint(:approver_id)
27
+ t.datetime(:created_at, null: false)
28
+ t.bigint(:forum_topic_id)
29
+ t.text(:script, null: false)
30
+ t.string(:status, null: false, index: true)
31
+ t.string(:title)
32
+ t.datetime(:updated_at, null: false)
33
+ t.bigint(:user_id, null: false, index: true)
34
+ end
35
+
36
+ create_table(<%= table_ref("pools") %>, id: :bigint, default: nil, force: :cascade) do |t|
37
+ t.string(:category, null: false)
38
+ t.datetime(:created_at, null: false)
39
+ t.bigint(:creator_id, null: false, index: true)
40
+ t.text(:description, null: false)
41
+ t.boolean(:is_active, null: false)
42
+ t.string(:name, null: false, index: true)
43
+ t.bigint(:post_ids, array: true, null: false, default: [])
44
+ t.datetime(:updated_at)
45
+ end
46
+
47
+ create_table(<%= table_ref("posts") %>, id: :bigint, default: nil, force: :cascade) do |t|
48
+ t.bigint(:approver_id)
49
+ t.bigint(:change_seq, null: false)
50
+ t.integer(:comment_count, null: false)
51
+ t.datetime(:created_at, null: false)
52
+ t.text(:description, null: false)
53
+ t.integer(:down_score, null: false)
54
+ t.float(:duration)
55
+ t.integer(:fav_count, null: false)
56
+ t.string(:file_ext, null: false)
57
+ t.bigint(:file_size, null: false)
58
+ t.integer(:image_height, null: false)
59
+ t.integer(:image_width, null: false)
60
+ t.boolean(:is_deleted, null: false, index: true)
61
+ t.boolean(:is_flagged, null: false)
62
+ t.boolean(:is_note_locked, null: false)
63
+ t.boolean(:is_pending, null: false)
64
+ t.boolean(:is_rating_locked, null: false)
65
+ t.boolean(:is_status_locked, null: false)
66
+ t.text(:locked_tags, null: false)
67
+ t.string(:md5, index: { unique: true }, null: false)
68
+ t.bigint(:parent_id)
69
+ t.string(:rating, null: false, index: true)
70
+ t.integer(:score, null: false)
71
+ t.text(:sources, array: true, null: false, default: [])
72
+ t.text(:tags, array: true, null: false, default: [])
73
+ t.integer(:up_score, null: false)
74
+ t.datetime(:updated_at)
75
+ t.bigint(:uploader_id, index: true)
76
+ end
77
+
78
+ create_table(<%= table_ref("post_replacements") %>, id: :bigint, default: nil, force: :cascade) do |t|
79
+ t.bigint(:approver_id)
80
+ t.datetime(:created_at, null: false)
81
+ t.bigint(:creator_id, null: false, index: true)
82
+ t.string(:file_ext, null: false)
83
+ t.string(:file_name, null: false)
84
+ t.bigint(:file_size, null: false)
85
+ t.integer(:image_height, null: false)
86
+ t.integer(:image_width, null: false)
87
+ t.string(:md5, null: false)
88
+ t.bigint(:post_id, null: false, index: true)
89
+ t.text(:reason, null: false)
90
+ t.text(:source)
91
+ t.string(:status, null: false, index: true)
92
+ t.datetime(:updated_at, null: false)
93
+ end
94
+
95
+ create_table(<%= table_ref("post_versions") %>, id: :bigint, default: nil, force: :cascade) do |t|
96
+ t.text(:added_locked_tags, array: true, null: false, default: [])
97
+ t.text(:added_tags, array: true, null: false, default: [])
98
+ t.text(:description)
99
+ t.boolean(:description_changed, null: false)
100
+ t.text(:locked_tags)
101
+ t.boolean(:parent_changed, null: false)
102
+ t.bigint(:parent_id)
103
+ t.string(:rating)
104
+ t.boolean(:rating_changed, null: false)
105
+ t.text(:reason)
106
+ t.text(:removed_locked_tags, array: true, null: false, default: [])
107
+ t.text(:removed_tags, array: true, null: false, default: [])
108
+ t.text(:source)
109
+ t.boolean(:source_changed, null: false)
110
+ t.text(:tags)
111
+ t.datetime(:updated_at, null: false, index: true)
112
+ t.bigint(:updater_id, null: false, index: true)
113
+ t.integer(:version, null: false)
114
+ end
115
+
116
+ create_table(<%= table_ref("tag_aliases") %>, id: :bigint, default: nil, force: :cascade) do |t|
117
+ t.string(:antecedent_name, null: false, index: true)
118
+ t.string(:consequent_name, null: false, index: true)
119
+ t.datetime(:created_at)
120
+ t.string(:status, null: false, index: true)
121
+ end
122
+
123
+ create_table(<%= table_ref("tag_implications") %>, id: :bigint, default: nil, force: :cascade) do |t|
124
+ t.string(:antecedent_name, null: false, index: true)
125
+ t.string(:consequent_name, null: false, index: true)
126
+ t.datetime(:created_at)
127
+ t.string(:status, null: false, index: true)
128
+ end
129
+
130
+ create_table(<%= table_ref("tags") %>, id: :bigint, default: nil, force: :cascade) do |t|
131
+ t.string(:category, null: false, index: true)
132
+ t.string(:name, null: false, index: { unique: true })
133
+ t.integer(:post_count, null: false)
134
+ end
135
+
136
+ create_table(<%= table_ref("wiki_pages") %>, id: :bigint, default: nil, force: :cascade) do |t|
137
+ t.text(:body, null: false)
138
+ t.datetime(:created_at, null: false)
139
+ t.bigint(:creator_id, index: true)
140
+ t.boolean(:is_locked, null: false)
141
+ t.string(:title, null: false, index: { unique: true })
142
+ t.datetime(:updated_at)
143
+ t.bigint(:uploader_id)
144
+ end
145
+ end
146
+ end
data/sorbet/config CHANGED
@@ -1,5 +1,9 @@
1
1
  --dir
2
2
  .
3
+ --ignore=db/
4
+ --ignore=lib/generators/
5
+ --ignore=lib/tasks/
6
+ --ignore=test/
3
7
  --ignore=tmp/
4
8
  --ignore=vendor/
5
9
  --parser=prism
@@ -0,0 +1,17 @@
1
+ # typed: true
2
+
3
+ module ActiveRecord
4
+ class Base
5
+ sig { params(value: T.untyped).void }
6
+ def self.table_name=(value); end
7
+
8
+ sig { params(value: T.untyped).void }
9
+ def self.record_timestamps=(value); end
10
+
11
+ sig { params(attributes: T::Hash[Symbol, T.untyped], kwargs: T.untyped).returns(T.untyped) }
12
+ def self.upsert(attributes, **kwargs); end
13
+
14
+ sig { params(attributes: T::Array[T::Hash[Symbol, T.untyped]], kwargs: T.untyped).returns(T.untyped) }
15
+ def self.upsert_all(attributes, **kwargs); end
16
+ end
17
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require("minitest/autorun")
4
+ require("rails/generators/testing/behavior")
5
+ require("rails/generators/testing/assertions")
6
+ require("generators/e621_export_downloader/install_generator")
7
+
8
+ class InstallGeneratorTest < Rails::Generators::TestCase
9
+ tests(E621ExportDownloader::InstallGenerator)
10
+ destination(File.expand_path("../tmp", __dir__))
11
+ setup(:prepare_destination)
12
+
13
+ test("creates migration with schema format by default") do
14
+ run_generator
15
+ assert_migration("db/migrate/create_e621_tables.rb") do |content|
16
+ assert_match(/CREATE SCHEMA e621/, content)
17
+ assert_match(/create_table\("e621\.artists"/, content)
18
+ end
19
+ end
20
+
21
+ test("creates initializer") do
22
+ run_generator
23
+ assert_file("config/initializers/e621_models.rb") do |content|
24
+ assert_match(%r{require\("e621_export_downloader/models"\)}, content)
25
+ assert_match(/E621::Artist\s+-\s+e621\.artists/, content)
26
+ end
27
+ end
28
+
29
+ test("--schema changes the schema name") do
30
+ run_generator(%w[--schema mydb])
31
+ assert_migration("db/migrate/create_e621_tables.rb") do |content|
32
+ assert_match(/CREATE SCHEMA mydb/, content)
33
+ assert_match(/create_table\("mydb\.artists"/, content)
34
+ end
35
+ assert_file("config/initializers/e621_models.rb") do |content|
36
+ assert_match(/E621::Artist\s+-\s+mydb\.artists/, content)
37
+ end
38
+ end
39
+
40
+ test("--table-format prefix omits schema creation and uses prefixed table names") do
41
+ run_generator(%w[--table-format prefix])
42
+ assert_migration("db/migrate/create_e621_tables.rb") do |content|
43
+ assert_no_match(/CREATE SCHEMA/, content)
44
+ assert_match(/create_table\(:e621_artists/, content)
45
+ end
46
+ assert_file("config/initializers/e621_models.rb") do |content|
47
+ assert_match(/E621::Artist\s+-\s+e621_artists/, content)
48
+ end
49
+ end
50
+
51
+ test("--schema and --table-format prefix combine") do
52
+ run_generator(%w[--schema mydb --table-format prefix])
53
+ assert_migration("db/migrate/create_e621_tables.rb") do |content|
54
+ assert_no_match(/CREATE SCHEMA/, content)
55
+ assert_match(/create_table\(:mydb_artists/, content)
56
+ end
57
+ assert_file("config/initializers/e621_models.rb") do |content|
58
+ assert_match(/E621::Artist\s+-\s+mydb_artists/, content)
59
+ end
60
+ end
61
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: e621_export_downloader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Donovan_DMC
@@ -78,7 +78,18 @@ files:
78
78
  - LICENSE
79
79
  - README.md
80
80
  - Rakefile
81
+ - db/migrate/create_e621_tables.rb
81
82
  - exe/e621-export-downloader
83
+ - lib/e621/artist.rb
84
+ - lib/e621/bulk_update_request.rb
85
+ - lib/e621/pool.rb
86
+ - lib/e621/post.rb
87
+ - lib/e621/post_replacement.rb
88
+ - lib/e621/post_version.rb
89
+ - lib/e621/tag.rb
90
+ - lib/e621/tag_alias.rb
91
+ - lib/e621/tag_implication.rb
92
+ - lib/e621/wiki_page.rb
82
93
  - lib/e621_export_downloader.rb
83
94
  - lib/e621_export_downloader/api_export_data.rb
84
95
  - lib/e621_export_downloader/client.rb
@@ -88,6 +99,7 @@ files:
88
99
  - lib/e621_export_downloader/constants.rb
89
100
  - lib/e621_export_downloader/export.rb
90
101
  - lib/e621_export_downloader/export_helper.rb
102
+ - lib/e621_export_downloader/models.rb
91
103
  - lib/e621_export_downloader/models/artist.rb
92
104
  - lib/e621_export_downloader/models/bulk_update_request.rb
93
105
  - lib/e621_export_downloader/models/pool.rb
@@ -102,6 +114,9 @@ files:
102
114
  - lib/e621_export_downloader/serializers/active_job.rb
103
115
  - lib/e621_export_downloader/types.rb
104
116
  - lib/e621_export_downloader/version.rb
117
+ - lib/generators/e621_export_downloader/install_generator.rb
118
+ - lib/generators/e621_export_downloader/templates/initializers/e621_models.rb.erb
119
+ - lib/generators/e621_export_downloader/templates/migrations/create_e621_tables.rb.erb
105
120
  - sorbet/config
106
121
  - sorbet/rbi/annotations/.gitattributes
107
122
  - sorbet/rbi/annotations/faraday.rbi
@@ -200,10 +215,12 @@ files:
200
215
  - sorbet/rbi/gems/websocket-driver@0.8.0.rbi
201
216
  - sorbet/rbi/gems/websocket-extensions@0.1.5.rbi
202
217
  - sorbet/rbi/gems/zeitwerk@2.7.5.rbi
218
+ - sorbet/rbi/shims/active_record.rbi
203
219
  - sorbet/rbi/shims/faraday.rbi
204
220
  - sorbet/rbi/todo.rbi
205
221
  - sorbet/tapioca/config.yml
206
222
  - sorbet/tapioca/require.rb
223
+ - test/generators/install_generator_test.rb
207
224
  homepage: https://github.com/DonovanDMC/E621ExportDownloader.rb
208
225
  licenses:
209
226
  - MIT