trellodon 0.3.0 → 0.4.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 +7 -0
- data/README.md +17 -1
- data/lib/.rbnext/2.7/trellodon/executor.rb +6 -6
- data/lib/.rbnext/3.0/trellodon/client.rb +27 -24
- data/lib/trellodon/cli.rb +19 -10
- data/lib/trellodon/client.rb +27 -24
- data/lib/trellodon/config.rb +1 -1
- data/lib/trellodon/entities/attachment.rb +1 -1
- data/lib/trellodon/entities/board.rb +1 -1
- data/lib/trellodon/entities/card.rb +1 -1
- data/lib/trellodon/entities/card_event.rb +6 -0
- data/lib/trellodon/entities/checklist_item.rb +1 -1
- data/lib/trellodon/entities/list.rb +1 -1
- data/lib/trellodon/executor.rb +6 -6
- data/lib/trellodon/formatters/markdown.rb +72 -7
- data/lib/trellodon/journal.rb +20 -0
- data/lib/trellodon/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7210dc1a4a18474871607069363ab1d1acdfeb283588ef52403a38699e24bebb
|
4
|
+
data.tar.gz: 465aa0681c0c9e2a3744a94ad06b17e8eaf35bfdc90204e36d6aa35359765134
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3895577372463c4d917c0c91f557789d298d5183263b72475bf213a44bb4d17deefac8e1ce5eb13b65b93540ae8442dfb43a4123d255abbe9a403700307353dc
|
7
|
+
data.tar.gz: d7e099a0bdb52d541152522f317e3b66ed89aa0128639be8731bb0c99c677e26b815bf1d6d1b61754bfa72998df7b5649967b8a28c23c05d5d561abd775b841c
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
## [master]
|
4
4
|
|
5
|
+
## [0.4.0] - 2022-04-27 🗂️
|
6
|
+
|
7
|
+
- Resolve folder name collisions in markdown formatter ([@rinasergeeva][])
|
8
|
+
- Add user info to attachments and checklists ([@rinasergeeva][])
|
9
|
+
- Verbose option in cli. ([@fargelus][])
|
10
|
+
|
5
11
|
## [0.3.0] - 2022-04-12 🚀🚀🚀
|
6
12
|
|
7
13
|
- Handle missing members. ([@palkan][])
|
@@ -29,3 +35,4 @@
|
|
29
35
|
|
30
36
|
[@fargelus]: https://github.com/fargelus
|
31
37
|
[@rinasergeeva]: https://github.com/rinasergeeva
|
38
|
+
[@palkan]: https://github.com/palkan
|
data/README.md
CHANGED
@@ -9,6 +9,7 @@ A Ruby tool to export a Trello board and convert it into a set of folders and ma
|
|
9
9
|
* Description
|
10
10
|
* Labels
|
11
11
|
* Comments
|
12
|
+
* Checklists
|
12
13
|
* Attachments
|
13
14
|
|
14
15
|
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
@@ -30,6 +31,14 @@ To generate them, go to the [trello.com/app-key](https://trello.com/app-key) pag
|
|
30
31
|
|
31
32
|
## Usage
|
32
33
|
|
34
|
+
Copy the board URL and run the following command:
|
35
|
+
|
36
|
+
```sh
|
37
|
+
trellodon dump --board=<url>
|
38
|
+
```
|
39
|
+
|
40
|
+
Other supported options:
|
41
|
+
|
33
42
|
```sh
|
34
43
|
Usage: trellodon dump [options]
|
35
44
|
Options:
|
@@ -37,7 +46,8 @@ Options:
|
|
37
46
|
--out VALUE Destination folder path
|
38
47
|
--concurrency VALUE Amount of processing threads (default: 4). Set to 0 to execute API requests in-line
|
39
48
|
--clear-auth Remove saved api credentials
|
40
|
-
-v, --
|
49
|
+
-v, --verbose Print all processes output
|
50
|
+
-V, --version Print current version
|
41
51
|
-h, --help Print help
|
42
52
|
```
|
43
53
|
|
@@ -90,3 +100,9 @@ Some card description
|
|
90
100
|
**date**: 2022-03-16
|
91
101
|
**url**: https://trello.com/1/cards/#{card_id}/attachments/#{attachment.id}/download/image.png
|
92
102
|
```
|
103
|
+
|
104
|
+
### Trello API Limits 🚧
|
105
|
+
|
106
|
+
Trello API limits the total amount of requests for each API Key & token.
|
107
|
+
By default, 300 requests are available per 10 seconds for each API key and no more than 100 requests per 10 second interval for each token.
|
108
|
+
If a request exceeds the limit, Trello will return a 429 error. Additional information see [here](https://help.trello.com/article/838-api-rate-limits).
|
@@ -5,12 +5,12 @@ module Trellodon
|
|
5
5
|
BOARD_ID_REGEX = /\/b\/([^\/]+)\//
|
6
6
|
private_constant :BOARD_ID_REGEX
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@api_key = api_key
|
10
|
-
@api_token = api_token
|
11
|
-
@formatter = formatter
|
12
|
-
@logger = logger
|
13
|
-
@scheduler = scheduler
|
8
|
+
def initialize(opts)
|
9
|
+
@api_key = opts.fetch(:api_key)
|
10
|
+
@api_token = opts.fetch(:api_token)
|
11
|
+
@formatter = opts.fetch(:formatter)
|
12
|
+
@logger = opts.fetch(:logger) { Config.logger }
|
13
|
+
@scheduler = opts.fetch(:scheduler)
|
14
14
|
check_credentials!
|
15
15
|
|
16
16
|
@client = Client.new(api_key: @api_key, api_token: @api_token)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "uri"
|
3
4
|
require "trello"
|
4
5
|
require "trellodon/entities"
|
5
6
|
|
@@ -17,7 +18,7 @@ module Trellodon
|
|
17
18
|
developer_public_key: api_key,
|
18
19
|
member_token: api_token
|
19
20
|
)
|
20
|
-
@members =
|
21
|
+
@members = Concurrent::Map.new
|
21
22
|
end
|
22
23
|
|
23
24
|
def fetch_board(board_id)
|
@@ -26,7 +27,6 @@ module Trellodon
|
|
26
27
|
|
27
28
|
Board.new(
|
28
29
|
id: board.id,
|
29
|
-
short_id: board.short_url.chars.last(8).join,
|
30
30
|
name: board.name,
|
31
31
|
lists: lists_from(board),
|
32
32
|
card_ids: @client.find_many(Trello::Card, "/boards/#{board_id}/cards", fields: "id").map(&:id),
|
@@ -41,7 +41,6 @@ module Trellodon
|
|
41
41
|
|
42
42
|
Card.new(
|
43
43
|
id: card.id,
|
44
|
-
short_id: card.short_url.chars.last(8).join,
|
45
44
|
name: card.name,
|
46
45
|
desc: card.desc,
|
47
46
|
labels: card.labels.map(&:name),
|
@@ -77,7 +76,7 @@ module Trellodon
|
|
77
76
|
Comment.new(
|
78
77
|
text: comment.data["text"],
|
79
78
|
date: comment.date,
|
80
|
-
creator: member_from(comment)
|
79
|
+
creator: member_from(comment.creator_id)
|
81
80
|
)
|
82
81
|
end
|
83
82
|
end
|
@@ -85,12 +84,13 @@ module Trellodon
|
|
85
84
|
def attachments_from(card)
|
86
85
|
card.attachments.map do |attach|
|
87
86
|
Attachment.new(
|
88
|
-
file_name: attach.file_name,
|
87
|
+
file_name: attach.is_upload ? URI.decode_www_form_component(attach.file_name) : attach.file_name,
|
89
88
|
mime_type: attach.mime_type,
|
90
89
|
bytes: attach.bytes,
|
91
90
|
date: attach.date,
|
92
91
|
name: attach.name,
|
93
92
|
is_upload: attach.is_upload,
|
93
|
+
added_by: member_from(attach.member_id),
|
94
94
|
headers: attach.is_upload ? {"Authorization" => "OAuth oauth_consumer_key=\"#{@client.configuration.developer_public_key}\", oauth_token=\"#{@client.configuration.member_token}\""} : {},
|
95
95
|
url: attach.url
|
96
96
|
)
|
@@ -102,7 +102,7 @@ module Trellodon
|
|
102
102
|
List.new(
|
103
103
|
id: list.id,
|
104
104
|
name: list.name,
|
105
|
-
|
105
|
+
board_id: list.board_id
|
106
106
|
)
|
107
107
|
end
|
108
108
|
end
|
@@ -122,28 +122,31 @@ module Trellodon
|
|
122
122
|
ChecklistItem.new(
|
123
123
|
id: item.id,
|
124
124
|
name: item.name,
|
125
|
-
state: item.state
|
125
|
+
state: item.state,
|
126
|
+
member: member_from(item.member_id),
|
127
|
+
due_date: item.due_date
|
126
128
|
)
|
127
129
|
end
|
128
130
|
end
|
129
131
|
|
130
|
-
def member_from(
|
131
|
-
return
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
132
|
+
def member_from(id)
|
133
|
+
return nil unless id
|
134
|
+
|
135
|
+
@members.compute_if_absent(id) do
|
136
|
+
member =
|
137
|
+
begin
|
138
|
+
@client.find(:members, id)
|
139
|
+
rescue Trello::Error => err
|
140
|
+
raise unless err.status_code == 404
|
141
|
+
|
142
|
+
DeletedMember.new(id)
|
143
|
+
end
|
144
|
+
Member.new(
|
145
|
+
id: member.id,
|
146
|
+
full_name: member.full_name,
|
147
|
+
username: member.username
|
148
|
+
)
|
149
|
+
end
|
147
150
|
end
|
148
151
|
end
|
149
152
|
end
|
data/lib/trellodon/cli.rb
CHANGED
@@ -50,8 +50,9 @@ module Trellodon
|
|
50
50
|
opts.on("--clear-auth", "Remove saved api credentials") do
|
51
51
|
config.reload(api_token: nil, api_key: nil)
|
52
52
|
end
|
53
|
+
opts.on("-v", "--verbose", "Print all processes output")
|
53
54
|
|
54
|
-
opts.on_tail("-
|
55
|
+
opts.on_tail("-V", "--version", "Print current version") do
|
55
56
|
puts Trellodon::VERSION
|
56
57
|
exit
|
57
58
|
end
|
@@ -103,28 +104,25 @@ module Trellodon
|
|
103
104
|
def initialize
|
104
105
|
@config = Config.new
|
105
106
|
@scheduler = Schedulers::Inline.new
|
107
|
+
@logger = Config.logger
|
106
108
|
end
|
107
109
|
|
108
110
|
def run
|
109
111
|
check_command!
|
110
112
|
|
111
113
|
need_to_fill_config = config.to_h[:api_key].nil?
|
112
|
-
|
113
|
-
executor = Trellodon::Executor.new(
|
114
|
-
api_key: config.api_key,
|
115
|
-
api_token: config.api_token,
|
116
|
-
formatter: Trellodon::Formatters::Markdown.new(output_dir: config.out),
|
117
|
-
scheduler: scheduler
|
118
|
-
)
|
114
|
+
executor = Trellodon::Executor.new(executor_options)
|
119
115
|
|
120
116
|
ask_save_credentials if need_to_fill_config
|
121
117
|
|
122
118
|
executor.download(config.board)
|
123
119
|
end
|
124
120
|
|
121
|
+
attr_reader :scheduler, :logger
|
122
|
+
|
125
123
|
private
|
126
124
|
|
127
|
-
attr_reader :config
|
125
|
+
attr_reader :config
|
128
126
|
|
129
127
|
def prompt
|
130
128
|
Prompt.instance
|
@@ -139,13 +137,24 @@ module Trellodon
|
|
139
137
|
end
|
140
138
|
|
141
139
|
if command == "dump"
|
142
|
-
change_scheduler(params)
|
140
|
+
change_scheduler(params)
|
141
|
+
@logger.level = 0 if params.key?(:verbose)
|
143
142
|
return
|
144
143
|
end
|
145
144
|
|
146
145
|
raise "Unknown command: #{command}. Available commands: dump"
|
147
146
|
end
|
148
147
|
|
148
|
+
def executor_options
|
149
|
+
{
|
150
|
+
api_key: config.api_key,
|
151
|
+
api_token: config.api_token,
|
152
|
+
formatter: Trellodon::Formatters::Markdown.new(output_dir: config.out, logger: logger),
|
153
|
+
scheduler: scheduler,
|
154
|
+
logger: logger
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
149
158
|
def ask_save_credentials
|
150
159
|
answer = prompt.yes?("Would you like to save api creds so next time trellodon won't asking it?", default: true)
|
151
160
|
config.update_config_file! if answer
|
data/lib/trellodon/client.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "uri"
|
3
4
|
require "trello"
|
4
5
|
require "trellodon/entities"
|
5
6
|
|
@@ -17,7 +18,7 @@ module Trellodon
|
|
17
18
|
developer_public_key: api_key,
|
18
19
|
member_token: api_token
|
19
20
|
)
|
20
|
-
@members =
|
21
|
+
@members = Concurrent::Map.new
|
21
22
|
end
|
22
23
|
|
23
24
|
def fetch_board(board_id)
|
@@ -26,7 +27,6 @@ module Trellodon
|
|
26
27
|
|
27
28
|
Board.new(
|
28
29
|
id: board.id,
|
29
|
-
short_id: board.short_url.chars.last(8).join,
|
30
30
|
name: board.name,
|
31
31
|
lists: lists_from(board),
|
32
32
|
card_ids: @client.find_many(Trello::Card, "/boards/#{board_id}/cards", fields: "id").map(&:id),
|
@@ -41,7 +41,6 @@ module Trellodon
|
|
41
41
|
|
42
42
|
Card.new(
|
43
43
|
id: card.id,
|
44
|
-
short_id: card.short_url.chars.last(8).join,
|
45
44
|
name: card.name,
|
46
45
|
desc: card.desc,
|
47
46
|
labels: card.labels.map(&:name),
|
@@ -77,7 +76,7 @@ module Trellodon
|
|
77
76
|
Comment.new(
|
78
77
|
text: comment.data["text"],
|
79
78
|
date: comment.date,
|
80
|
-
creator: member_from(comment)
|
79
|
+
creator: member_from(comment.creator_id)
|
81
80
|
)
|
82
81
|
end
|
83
82
|
end
|
@@ -85,12 +84,13 @@ module Trellodon
|
|
85
84
|
def attachments_from(card)
|
86
85
|
card.attachments.map do |attach|
|
87
86
|
Attachment.new(
|
88
|
-
file_name: attach.file_name,
|
87
|
+
file_name: attach.is_upload ? URI.decode_www_form_component(attach.file_name) : attach.file_name,
|
89
88
|
mime_type: attach.mime_type,
|
90
89
|
bytes: attach.bytes,
|
91
90
|
date: attach.date,
|
92
91
|
name: attach.name,
|
93
92
|
is_upload: attach.is_upload,
|
93
|
+
added_by: member_from(attach.member_id),
|
94
94
|
headers: attach.is_upload ? {"Authorization" => "OAuth oauth_consumer_key=\"#{@client.configuration.developer_public_key}\", oauth_token=\"#{@client.configuration.member_token}\""} : {},
|
95
95
|
url: attach.url
|
96
96
|
)
|
@@ -102,7 +102,7 @@ module Trellodon
|
|
102
102
|
List.new(
|
103
103
|
id: list.id,
|
104
104
|
name: list.name,
|
105
|
-
|
105
|
+
board_id: list.board_id
|
106
106
|
)
|
107
107
|
end
|
108
108
|
end
|
@@ -122,28 +122,31 @@ module Trellodon
|
|
122
122
|
ChecklistItem.new(
|
123
123
|
id: item.id,
|
124
124
|
name: item.name,
|
125
|
-
state: item.state
|
125
|
+
state: item.state,
|
126
|
+
member: member_from(item.member_id),
|
127
|
+
due_date: item.due_date
|
126
128
|
)
|
127
129
|
end
|
128
130
|
end
|
129
131
|
|
130
|
-
def member_from(
|
131
|
-
return
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
132
|
+
def member_from(id)
|
133
|
+
return nil unless id
|
134
|
+
|
135
|
+
@members.compute_if_absent(id) do
|
136
|
+
member =
|
137
|
+
begin
|
138
|
+
@client.find(:members, id)
|
139
|
+
rescue Trello::Error => err
|
140
|
+
raise unless err.status_code == 404
|
141
|
+
|
142
|
+
DeletedMember.new(id)
|
143
|
+
end
|
144
|
+
Member.new(
|
145
|
+
id: member.id,
|
146
|
+
full_name: member.full_name,
|
147
|
+
username: member.username
|
148
|
+
)
|
149
|
+
end
|
147
150
|
end
|
148
151
|
end
|
149
152
|
end
|
data/lib/trellodon/config.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Trellodon
|
4
|
-
Attachment = Struct.new("Attachment", :file_name, :mime_type, :bytes, :date, :name, :is_upload, :headers, :url, keyword_init: true)
|
4
|
+
Attachment = Struct.new("Attachment", :file_name, :mime_type, :bytes, :date, :name, :is_upload, :headers, :url, :added_by, keyword_init: true)
|
5
5
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Trellodon
|
4
|
-
Board = Struct.new("Board", :id, :
|
4
|
+
Board = Struct.new("Board", :id, :name, :card_ids, :lists, :last_activity_date, keyword_init: true) do
|
5
5
|
def get_list(list_id)
|
6
6
|
return nil if lists.nil?
|
7
7
|
lists.find { |list| list.id == list_id }
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Trellodon
|
4
|
-
Card = Struct.new("Card", :id, :
|
4
|
+
Card = Struct.new("Card", :id, :name, :desc, :list_id, :labels, :comments, :attachments, :checklists, :last_activity_date, keyword_init: true)
|
5
5
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Trellodon
|
4
|
-
ChecklistItem = Struct.new("ChecklistItem", :id, :name, :state, keyword_init: true) do
|
4
|
+
ChecklistItem = Struct.new("ChecklistItem", :id, :name, :state, :member, :due_date, keyword_init: true) do
|
5
5
|
def checked?
|
6
6
|
state == "complete"
|
7
7
|
end
|
data/lib/trellodon/executor.rb
CHANGED
@@ -5,12 +5,12 @@ module Trellodon
|
|
5
5
|
BOARD_ID_REGEX = /\/b\/([^\/]+)\//
|
6
6
|
private_constant :BOARD_ID_REGEX
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@api_key = api_key
|
10
|
-
@api_token = api_token
|
11
|
-
@formatter = formatter
|
12
|
-
@logger = logger
|
13
|
-
@scheduler = scheduler
|
8
|
+
def initialize(opts)
|
9
|
+
@api_key = opts.fetch(:api_key)
|
10
|
+
@api_token = opts.fetch(:api_token)
|
11
|
+
@formatter = opts.fetch(:formatter)
|
12
|
+
@logger = opts.fetch(:logger) { Config.logger }
|
13
|
+
@scheduler = opts.fetch(:scheduler)
|
14
14
|
check_credentials!
|
15
15
|
|
16
16
|
@client = Client.new(api_key: @api_key, api_token: @api_token)
|
@@ -11,10 +11,16 @@ module Trellodon
|
|
11
11
|
|
12
12
|
MD_FILENAME = "README.md"
|
13
13
|
ATTACHMENTS_DIRNAME = "attachments"
|
14
|
+
DIRNAME_MAXLENGTH = 50
|
14
15
|
|
15
16
|
def initialize(output_dir:, **opts)
|
16
17
|
super(**opts)
|
17
18
|
@output_dir = output_dir
|
19
|
+
@board_dirname = Concurrent::Map.new
|
20
|
+
@board_subdirs = Concurrent::Map.new
|
21
|
+
@list_dirname = Concurrent::Map.new
|
22
|
+
@list_subdirs = Concurrent::Map.new
|
23
|
+
@card_dirname = Concurrent::Map.new
|
18
24
|
end
|
19
25
|
|
20
26
|
def card_added(card)
|
@@ -37,10 +43,7 @@ module Trellodon
|
|
37
43
|
list = @board.get_list(card.list_id)
|
38
44
|
raise "List #{card.list_id} is not found" if list.nil?
|
39
45
|
|
40
|
-
card_dir = File.join(@output_dir,
|
41
|
-
"#{board.name} [#{board.short_id}]",
|
42
|
-
"#{list.name} [#{list.short_id}]",
|
43
|
-
"#{card.name} [#{card.short_id}]")
|
46
|
+
card_dir = File.join(@output_dir, board_dirname(board), list_dirname(list), card_dirname(card))
|
44
47
|
FileUtils.mkdir_p(card_dir) unless File.directory?(card_dir)
|
45
48
|
card_dir
|
46
49
|
end
|
@@ -51,11 +54,57 @@ module Trellodon
|
|
51
54
|
attachments_dir
|
52
55
|
end
|
53
56
|
|
57
|
+
def board_dirname(board)
|
58
|
+
@board_dirname.compute_if_absent(board.id) { sanitize(board.name) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def list_dirname(list)
|
62
|
+
@list_dirname.compute_if_absent(list.id) do
|
63
|
+
uniq_dirname(
|
64
|
+
dir: sanitize(list.name),
|
65
|
+
entity: list,
|
66
|
+
dirlist: @board_subdirs,
|
67
|
+
parent_id: list.board_id
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def card_dirname(card)
|
73
|
+
@card_dirname.compute_if_absent(card.id) do
|
74
|
+
uniq_dirname(
|
75
|
+
dir: sanitize(card.name),
|
76
|
+
entity: card,
|
77
|
+
dirlist: @list_subdirs,
|
78
|
+
parent_id: card.list_id
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def uniq_dirname(dir:, entity:, dirlist:, parent_id:)
|
84
|
+
return dir unless [List, Card].include?(entity.class)
|
85
|
+
|
86
|
+
dirlist.put_if_absent(parent_id, Concurrent::Map.new)
|
87
|
+
dirlist[parent_id].put_if_absent(dir, Concurrent::Array.new([entity.id]))
|
88
|
+
dirlist[parent_id][dir].push(entity.id) unless dirlist[parent_id][dir].include?(entity.id)
|
89
|
+
idx = dirlist[parent_id][dir].index(entity.id)
|
90
|
+
idx > 0 ? dir + "(#{idx})" : dir
|
91
|
+
end
|
92
|
+
|
93
|
+
def sanitize(name, separator: "")
|
94
|
+
name.gsub(/[\/\\"*?<>|:]+/, separator).slice(0, DIRNAME_MAXLENGTH)
|
95
|
+
end
|
96
|
+
|
54
97
|
def download_attachments(card)
|
55
98
|
return if card.attachments.nil? || card.attachments.empty?
|
99
|
+
|
56
100
|
attachments_path = card_attachments_path(card)
|
57
101
|
card.attachments.select(&:is_upload).each do |att|
|
58
|
-
|
102
|
+
att_file_path = File.join(attachments_path, att.file_name)
|
103
|
+
if File.file?(att_file_path)
|
104
|
+
logger.warn "Attachment file already exists: #{att_file_path}"
|
105
|
+
else
|
106
|
+
IO.copy_stream(URI.parse(att.url).open(att.headers), att_file_path)
|
107
|
+
end
|
59
108
|
end
|
60
109
|
end
|
61
110
|
|
@@ -73,6 +122,7 @@ module Trellodon
|
|
73
122
|
---
|
74
123
|
title: #{card.name}
|
75
124
|
last_updated_at: #{card.last_activity_date}
|
125
|
+
dumped_at: #{Time.now}
|
76
126
|
labels: #{create_card_labels(card)}
|
77
127
|
---
|
78
128
|
EOS
|
@@ -96,11 +146,13 @@ module Trellodon
|
|
96
146
|
|
97
147
|
def create_card_labels(card)
|
98
148
|
return "" if card.labels.nil? || card.labels.empty?
|
149
|
+
|
99
150
|
card.labels.map { |label| "\n - " + label }.join
|
100
151
|
end
|
101
152
|
|
102
153
|
def create_card_checklists(card)
|
103
154
|
return "" if card.checklists.nil? || card.checklists.empty?
|
155
|
+
|
104
156
|
<<~EOS
|
105
157
|
|
106
158
|
## Checklists
|
@@ -117,11 +169,15 @@ module Trellodon
|
|
117
169
|
end
|
118
170
|
|
119
171
|
def create_card_checklist_item(item)
|
120
|
-
"\n- [#{item.checked? ? "x" : " "}] #{item.name}"
|
172
|
+
formatted_item = "\n- [#{item.checked? ? "x" : " "}] #{item.name}"
|
173
|
+
formatted_item += " | *assigned_to: #{create_member(item.member)}*" if item.member
|
174
|
+
formatted_item += " | *due: #{item.due_date}*" if item.due_date
|
175
|
+
formatted_item
|
121
176
|
end
|
122
177
|
|
123
178
|
def create_card_comments(card)
|
124
179
|
return "" if card.comments.nil? || card.comments.empty?
|
180
|
+
|
125
181
|
<<~EOS
|
126
182
|
|
127
183
|
## Comments
|
@@ -132,13 +188,15 @@ module Trellodon
|
|
132
188
|
def create_card_comment(comment)
|
133
189
|
<<~EOS
|
134
190
|
|
135
|
-
|
191
|
+
### #{create_member(comment.creator)} at #{comment.date}
|
192
|
+
|
136
193
|
#{comment.text}
|
137
194
|
EOS
|
138
195
|
end
|
139
196
|
|
140
197
|
def create_card_attachments(card)
|
141
198
|
return "" if card.attachments.nil? || card.attachments.empty?
|
199
|
+
|
142
200
|
<<~EOS
|
143
201
|
|
144
202
|
## Attachments
|
@@ -152,6 +210,7 @@ module Trellodon
|
|
152
210
|
### #{attachment.name}
|
153
211
|
|
154
212
|
**date**: #{attachment.date}
|
213
|
+
**added_by**: #{create_member(attachment.added_by)}
|
155
214
|
**url**: #{attachment.url}
|
156
215
|
**local copy**: #{create_attachment_local_link(attachment)}
|
157
216
|
EOS
|
@@ -159,8 +218,14 @@ module Trellodon
|
|
159
218
|
|
160
219
|
def create_attachment_local_link(attachment)
|
161
220
|
return "-" unless attachment.is_upload
|
221
|
+
|
162
222
|
"[#{attachment.file_name}](#{File.join("./", ATTACHMENTS_DIRNAME, attachment.file_name)})"
|
163
223
|
end
|
224
|
+
|
225
|
+
def create_member(member)
|
226
|
+
return "" unless member
|
227
|
+
"#{member.full_name} (@#{member.username})"
|
228
|
+
end
|
164
229
|
end
|
165
230
|
end
|
166
231
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module Trellodon
|
6
|
+
class Journal
|
7
|
+
FILENAME = "journal.yml"
|
8
|
+
|
9
|
+
def initialize(dir)
|
10
|
+
@location = File.join(dir, FILENAME)
|
11
|
+
File.write(location, "") unless File.exist?(location)
|
12
|
+
end
|
13
|
+
|
14
|
+
def <<(record)
|
15
|
+
File.write(location, record.to_yaml, mode: "a+")
|
16
|
+
end
|
17
|
+
|
18
|
+
private attr_reader :location
|
19
|
+
end
|
20
|
+
end
|
data/lib/trellodon/version.rb
CHANGED
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.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fargelus
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-04-
|
13
|
+
date: 2022-04-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: anyway_config
|
@@ -69,7 +69,7 @@ dependencies:
|
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '0'
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
|
-
name: ruby-next
|
72
|
+
name: ruby-next
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
75
|
- - ">="
|
@@ -111,6 +111,7 @@ files:
|
|
111
111
|
- lib/trellodon/entities/attachment.rb
|
112
112
|
- lib/trellodon/entities/board.rb
|
113
113
|
- lib/trellodon/entities/card.rb
|
114
|
+
- lib/trellodon/entities/card_event.rb
|
114
115
|
- lib/trellodon/entities/checklist.rb
|
115
116
|
- lib/trellodon/entities/checklist_item.rb
|
116
117
|
- lib/trellodon/entities/comment.rb
|
@@ -120,6 +121,7 @@ files:
|
|
120
121
|
- lib/trellodon/formatters.rb
|
121
122
|
- lib/trellodon/formatters/base.rb
|
122
123
|
- lib/trellodon/formatters/markdown.rb
|
124
|
+
- lib/trellodon/journal.rb
|
123
125
|
- lib/trellodon/schedulers.rb
|
124
126
|
- lib/trellodon/schedulers/base.rb
|
125
127
|
- lib/trellodon/schedulers/concurrent.rb
|