trellodon 0.2.1 → 0.3.0
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +3 -1
- data/lib/{trellodon/api_executor.rb → .rbnext/2.7/trellodon/executor.rb} +6 -9
- data/lib/.rbnext/3.0/trellodon/client.rb +149 -0
- data/lib/.rbnext/3.1/trellodon/schedulers/concurrent.rb +1 -1
- data/lib/trellodon/cli.rb +9 -11
- data/lib/trellodon/{api_client.rb → client.rb} +16 -2
- data/lib/{.rbnext/2.7/trellodon/api_executor.rb → trellodon/executor.rb} +6 -9
- data/lib/trellodon/schedulers/concurrent.rb +1 -1
- data/lib/trellodon/version.rb +1 -1
- data/lib/trellodon.rb +2 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a61911e92509a77f13aa90f9f56c14d038dbe443b4e1063d28f66b6745b04432
|
4
|
+
data.tar.gz: '079609a773107e786a7d795fe7d23564ded2aa66e8d587a6fd22b4e288e15478'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 617119b48c1f2385c87d57ffd2683933097ed1aae11a51577f65e16012ab1f58b65187f57884123ed04ba522797925826e3cf202e7d4dad9d1fd63a3d736bcef
|
7
|
+
data.tar.gz: 30c1f1bcb2df2c69a3c8d0cebc93a597f16d2207d33bef979670134d306bd0173cc97948f38f739cf1d6b5eef0a7a602112fb0de40021d078d0188280ca4d039
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
## [master]
|
4
4
|
|
5
|
+
## [0.3.0] - 2022-04-12 🚀🚀🚀
|
6
|
+
|
7
|
+
- Handle missing members. ([@palkan][])
|
8
|
+
|
9
|
+
- Use inline scheduler by default. ([@palkan][])
|
10
|
+
|
11
|
+
- Replace `--sync` with `--concurrency=0`. ([@palkan][])
|
12
|
+
|
5
13
|
## [0.2.1] - 2022-04-12 🚀
|
6
14
|
|
7
15
|
- Misc changes.
|
data/README.md
CHANGED
@@ -31,10 +31,12 @@ To generate them, go to the [trello.com/app-key](https://trello.com/app-key) pag
|
|
31
31
|
## Usage
|
32
32
|
|
33
33
|
```sh
|
34
|
-
Usage: trellodon dump
|
34
|
+
Usage: trellodon dump [options]
|
35
35
|
Options:
|
36
36
|
--board VALUE Board URL or ID
|
37
37
|
--out VALUE Destination folder path
|
38
|
+
--concurrency VALUE Amount of processing threads (default: 4). Set to 0 to execute API requests in-line
|
39
|
+
--clear-auth Remove saved api credentials
|
38
40
|
-v, --version Print current version
|
39
41
|
-h, --help Print help
|
40
42
|
```
|
@@ -1,10 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "trellodon/api_client"
|
4
|
-
require "trellodon/config"
|
5
|
-
|
6
3
|
module Trellodon
|
7
|
-
class
|
4
|
+
class Executor
|
8
5
|
BOARD_ID_REGEX = /\/b\/([^\/]+)\//
|
9
6
|
private_constant :BOARD_ID_REGEX
|
10
7
|
|
@@ -16,7 +13,7 @@ module Trellodon
|
|
16
13
|
@scheduler = scheduler
|
17
14
|
check_credentials!
|
18
15
|
|
19
|
-
@
|
16
|
+
@client = Client.new(api_key: @api_key, api_token: @api_token)
|
20
17
|
end
|
21
18
|
|
22
19
|
def download(board_pointer)
|
@@ -25,13 +22,13 @@ module Trellodon
|
|
25
22
|
startup_time = Time.now
|
26
23
|
logger.debug "Fetching board 🚀️️"
|
27
24
|
board = scheduler.post do
|
28
|
-
|
25
|
+
client.fetch_board(board_id).tap { |_1| formatter.board_added(_1) }
|
29
26
|
end.value
|
30
27
|
|
31
|
-
logger.debug "Fetching cards
|
28
|
+
logger.debug "Fetching #{board.card_ids.size} cards from the board with comments and attachments 🐢"
|
32
29
|
board.card_ids.map do |card_id|
|
33
30
|
scheduler.post do
|
34
|
-
|
31
|
+
client.fetch_card(card_id).tap { |_1| formatter.card_added(_1) }
|
35
32
|
end
|
36
33
|
end.map(&:value)
|
37
34
|
|
@@ -41,7 +38,7 @@ module Trellodon
|
|
41
38
|
|
42
39
|
private
|
43
40
|
|
44
|
-
attr_reader :
|
41
|
+
attr_reader :client, :board_id, :logger, :scheduler, :formatter
|
45
42
|
|
46
43
|
def check_credentials!
|
47
44
|
return if @api_key.to_s.size.positive? && @api_token.to_s.size.positive?
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "trello"
|
4
|
+
require "trellodon/entities"
|
5
|
+
|
6
|
+
module Trellodon
|
7
|
+
class Client
|
8
|
+
class DeletedMember < Struct.new(:id)
|
9
|
+
def full_name ; "[DELETED]"; end
|
10
|
+
|
11
|
+
def username ; "__deleted__"; end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(api_key:, api_token:, logger: Config.logger)
|
15
|
+
@logger = logger
|
16
|
+
@client = Trello::Client.new(
|
17
|
+
developer_public_key: api_key,
|
18
|
+
member_token: api_token
|
19
|
+
)
|
20
|
+
@members = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch_board(board_id)
|
24
|
+
retrying do
|
25
|
+
board = @client.find(:boards, board_id)
|
26
|
+
|
27
|
+
Board.new(
|
28
|
+
id: board.id,
|
29
|
+
short_id: board.short_url.chars.last(8).join,
|
30
|
+
name: board.name,
|
31
|
+
lists: lists_from(board),
|
32
|
+
card_ids: @client.find_many(Trello::Card, "/boards/#{board_id}/cards", fields: "id").map(&:id),
|
33
|
+
last_activity_date: board.last_activity_date
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch_card(card_id)
|
39
|
+
retrying do
|
40
|
+
card = @client.find(:cards, card_id)
|
41
|
+
|
42
|
+
Card.new(
|
43
|
+
id: card.id,
|
44
|
+
short_id: card.short_url.chars.last(8).join,
|
45
|
+
name: card.name,
|
46
|
+
desc: card.desc,
|
47
|
+
labels: card.labels.map(&:name),
|
48
|
+
list_id: card.list_id,
|
49
|
+
comments: comments_from(card),
|
50
|
+
attachments: attachments_from(card),
|
51
|
+
checklists: checklists_from(card),
|
52
|
+
last_activity_date: card.last_activity_date
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
attr_reader :logger
|
60
|
+
|
61
|
+
def retrying(&block)
|
62
|
+
attempt = 0
|
63
|
+
begin
|
64
|
+
block.call
|
65
|
+
rescue Trello::Error => err
|
66
|
+
raise unless err.status_code == 429
|
67
|
+
attempt += 1
|
68
|
+
cooldown = 2**attempt + rand(2**attempt) - 2**(attempt - 1)
|
69
|
+
logger.warn "API limit exceeded, cool down for #{cooldown}s"
|
70
|
+
sleep cooldown
|
71
|
+
retry
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def comments_from(card)
|
76
|
+
card.comments.map do |comment|
|
77
|
+
Comment.new(
|
78
|
+
text: comment.data["text"],
|
79
|
+
date: comment.date,
|
80
|
+
creator: member_from(comment)
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def attachments_from(card)
|
86
|
+
card.attachments.map do |attach|
|
87
|
+
Attachment.new(
|
88
|
+
file_name: attach.file_name,
|
89
|
+
mime_type: attach.mime_type,
|
90
|
+
bytes: attach.bytes,
|
91
|
+
date: attach.date,
|
92
|
+
name: attach.name,
|
93
|
+
is_upload: attach.is_upload,
|
94
|
+
headers: attach.is_upload ? {"Authorization" => "OAuth oauth_consumer_key=\"#{@client.configuration.developer_public_key}\", oauth_token=\"#{@client.configuration.member_token}\""} : {},
|
95
|
+
url: attach.url
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def lists_from(board)
|
101
|
+
board.lists.map do |list|
|
102
|
+
List.new(
|
103
|
+
id: list.id,
|
104
|
+
name: list.name,
|
105
|
+
short_id: board.short_url.chars.last(8).join
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def checklists_from(card)
|
111
|
+
card.checklists.map do |checklist|
|
112
|
+
Checklist.new(
|
113
|
+
id: checklist.id,
|
114
|
+
name: checklist.name,
|
115
|
+
items: checklist_items_from(checklist)
|
116
|
+
)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def checklist_items_from(checklist)
|
121
|
+
checklist.items.map do |item|
|
122
|
+
ChecklistItem.new(
|
123
|
+
id: item.id,
|
124
|
+
name: item.name,
|
125
|
+
state: item.state
|
126
|
+
)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def member_from(comment)
|
131
|
+
return @members[comment.creator_id] if @members.has_key? comment.creator_id
|
132
|
+
|
133
|
+
member =
|
134
|
+
begin
|
135
|
+
@client.find(:members, comment.creator_id)
|
136
|
+
rescue Trello::Error => err
|
137
|
+
raise unless err.status_code == 404
|
138
|
+
|
139
|
+
DeletedMember.new(comment.creator_id)
|
140
|
+
end
|
141
|
+
|
142
|
+
@members[comment.creator_id] = Member.new(
|
143
|
+
id: member.id,
|
144
|
+
full_name: member.full_name,
|
145
|
+
username: member.username
|
146
|
+
)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/trellodon/cli.rb
CHANGED
@@ -46,8 +46,7 @@ module Trellodon
|
|
46
46
|
opts.banner = "Usage: trellodon dump \e[34m[options]\e[0m\n"\
|
47
47
|
"Options:\n"
|
48
48
|
|
49
|
-
opts.on("--
|
50
|
-
opts.on("--concurrency VALUE", "Amount of processing threads (default: 50)")
|
49
|
+
opts.on("--concurrency VALUE", "Amount of processing threads (default: 4)")
|
51
50
|
opts.on("--clear-auth", "Remove saved api credentials") do
|
52
51
|
config.reload(api_token: nil, api_key: nil)
|
53
52
|
end
|
@@ -103,7 +102,7 @@ module Trellodon
|
|
103
102
|
|
104
103
|
def initialize
|
105
104
|
@config = Config.new
|
106
|
-
@scheduler = Schedulers::
|
105
|
+
@scheduler = Schedulers::Inline.new
|
107
106
|
end
|
108
107
|
|
109
108
|
def run
|
@@ -111,15 +110,16 @@ module Trellodon
|
|
111
110
|
|
112
111
|
need_to_fill_config = config.to_h[:api_key].nil?
|
113
112
|
|
114
|
-
executor = Trellodon::
|
113
|
+
executor = Trellodon::Executor.new(
|
115
114
|
api_key: config.api_key,
|
116
115
|
api_token: config.api_token,
|
117
116
|
formatter: Trellodon::Formatters::Markdown.new(output_dir: config.out),
|
118
117
|
scheduler: scheduler
|
119
118
|
)
|
120
|
-
executor.download(config.board)
|
121
119
|
|
122
120
|
ask_save_credentials if need_to_fill_config
|
121
|
+
|
122
|
+
executor.download(config.board)
|
123
123
|
end
|
124
124
|
|
125
125
|
private
|
@@ -152,14 +152,12 @@ module Trellodon
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def change_scheduler(params)
|
155
|
-
if params.key?(:sync)
|
156
|
-
@scheduler = Schedulers::Inline.new
|
157
|
-
return
|
158
|
-
end
|
159
|
-
|
160
155
|
return unless params[:concurrency]
|
161
156
|
|
162
|
-
threads = params[:concurrency]
|
157
|
+
threads = Integer(params[:concurrency])
|
158
|
+
|
159
|
+
return @scheduler = Schedulers::Inline.new if threads.zero?
|
160
|
+
|
163
161
|
raise "Value of concurrency option must be integer and greater than zero" unless threads.positive?
|
164
162
|
|
165
163
|
@scheduler = Schedulers::Concurrent.new(threads)
|
@@ -4,7 +4,13 @@ require "trello"
|
|
4
4
|
require "trellodon/entities"
|
5
5
|
|
6
6
|
module Trellodon
|
7
|
-
class
|
7
|
+
class Client
|
8
|
+
class DeletedMember < Struct.new(:id)
|
9
|
+
def full_name = "[DELETED]"
|
10
|
+
|
11
|
+
def username = "__deleted__"
|
12
|
+
end
|
13
|
+
|
8
14
|
def initialize(api_key:, api_token:, logger: Config.logger)
|
9
15
|
@logger = logger
|
10
16
|
@client = Trello::Client.new(
|
@@ -124,7 +130,15 @@ module Trellodon
|
|
124
130
|
def member_from(comment)
|
125
131
|
return @members[comment.creator_id] if @members.has_key? comment.creator_id
|
126
132
|
|
127
|
-
member =
|
133
|
+
member =
|
134
|
+
begin
|
135
|
+
@client.find(:members, comment.creator_id)
|
136
|
+
rescue Trello::Error => err
|
137
|
+
raise unless err.status_code == 404
|
138
|
+
|
139
|
+
DeletedMember.new(comment.creator_id)
|
140
|
+
end
|
141
|
+
|
128
142
|
@members[comment.creator_id] = Member.new(
|
129
143
|
id: member.id,
|
130
144
|
full_name: member.full_name,
|
@@ -1,10 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "trellodon/api_client"
|
4
|
-
require "trellodon/config"
|
5
|
-
|
6
3
|
module Trellodon
|
7
|
-
class
|
4
|
+
class Executor
|
8
5
|
BOARD_ID_REGEX = /\/b\/([^\/]+)\//
|
9
6
|
private_constant :BOARD_ID_REGEX
|
10
7
|
|
@@ -16,7 +13,7 @@ module Trellodon
|
|
16
13
|
@scheduler = scheduler
|
17
14
|
check_credentials!
|
18
15
|
|
19
|
-
@
|
16
|
+
@client = Client.new(api_key: @api_key, api_token: @api_token)
|
20
17
|
end
|
21
18
|
|
22
19
|
def download(board_pointer)
|
@@ -25,13 +22,13 @@ module Trellodon
|
|
25
22
|
startup_time = Time.now
|
26
23
|
logger.debug "Fetching board 🚀️️"
|
27
24
|
board = scheduler.post do
|
28
|
-
|
25
|
+
client.fetch_board(board_id).tap { formatter.board_added(_1) }
|
29
26
|
end.value
|
30
27
|
|
31
|
-
logger.debug "Fetching cards
|
28
|
+
logger.debug "Fetching #{board.card_ids.size} cards from the board with comments and attachments 🐢"
|
32
29
|
board.card_ids.map do |card_id|
|
33
30
|
scheduler.post do
|
34
|
-
|
31
|
+
client.fetch_card(card_id).tap { formatter.card_added(_1) }
|
35
32
|
end
|
36
33
|
end.map(&:value)
|
37
34
|
|
@@ -41,7 +38,7 @@ module Trellodon
|
|
41
38
|
|
42
39
|
private
|
43
40
|
|
44
|
-
attr_reader :
|
41
|
+
attr_reader :client, :board_id, :logger, :scheduler, :formatter
|
45
42
|
|
46
43
|
def check_credentials!
|
47
44
|
return if @api_key.to_s.size.positive? && @api_token.to_s.size.positive?
|
data/lib/trellodon/version.rb
CHANGED
data/lib/trellodon.rb
CHANGED
@@ -7,7 +7,8 @@ RubyNext::Language.setup_gem_load_path(transpile: true)
|
|
7
7
|
|
8
8
|
require "trellodon/version"
|
9
9
|
require "trellodon/config"
|
10
|
-
require "trellodon/
|
10
|
+
require "trellodon/client"
|
11
|
+
require "trellodon/executor"
|
11
12
|
|
12
13
|
require "trellodon/entities"
|
13
14
|
require "trellodon/schedulers"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trellodon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fargelus
|
@@ -100,12 +100,12 @@ files:
|
|
100
100
|
- bin/console
|
101
101
|
- bin/setup
|
102
102
|
- bin/trellodon
|
103
|
-
- lib/.rbnext/2.7/trellodon/
|
103
|
+
- lib/.rbnext/2.7/trellodon/executor.rb
|
104
|
+
- lib/.rbnext/3.0/trellodon/client.rb
|
104
105
|
- lib/.rbnext/3.1/trellodon/schedulers/concurrent.rb
|
105
106
|
- lib/trellodon.rb
|
106
|
-
- lib/trellodon/api_client.rb
|
107
|
-
- lib/trellodon/api_executor.rb
|
108
107
|
- lib/trellodon/cli.rb
|
108
|
+
- lib/trellodon/client.rb
|
109
109
|
- lib/trellodon/config.rb
|
110
110
|
- lib/trellodon/entities.rb
|
111
111
|
- lib/trellodon/entities/attachment.rb
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/trellodon/entities/comment.rb
|
117
117
|
- lib/trellodon/entities/list.rb
|
118
118
|
- lib/trellodon/entities/member.rb
|
119
|
+
- lib/trellodon/executor.rb
|
119
120
|
- lib/trellodon/formatters.rb
|
120
121
|
- lib/trellodon/formatters/base.rb
|
121
122
|
- lib/trellodon/formatters/markdown.rb
|