renshuu 0.2.0 → 1.0.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/.rubocop.yml +1 -1
- data/CHANGELOG.md +7 -0
- data/README.md +85 -0
- data/lib/renshuu/models/grammar.rb +10 -3
- data/lib/renshuu/models/kanji.rb +5 -51
- data/lib/renshuu/models/list.rb +138 -0
- data/lib/renshuu/models/mixins/listable.rb +69 -0
- data/lib/renshuu/models/mixins/schedulable.rb +69 -0
- data/lib/renshuu/models/presence.rb +66 -0
- data/lib/renshuu/models/schedule.rb +208 -0
- data/lib/renshuu/models/sentence.rb +2 -2
- data/lib/renshuu/models/types.rb +5 -6
- data/lib/renshuu/models/user_data.rb +1 -1
- data/lib/renshuu/models/word.rb +10 -3
- data/lib/renshuu/models.rb +8 -0
- data/lib/renshuu/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df6e1af568b7bb56df71d9bd55a92b9a64e0319e4a0daa6df5afec0a4c2d905e
|
|
4
|
+
data.tar.gz: 06fe422633574facfb49f0c9fbc53b52969026fe9df4fd8f50cf7904768ab379
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 73dd997e5c0ef3fcd15de076192d660ee36158f7ac8bc54a54d3ce98281358595ae5b9523a8918fd66d2e5a9ebedd04d24c345137fe54e3b2f851ae5733f6813
|
|
7
|
+
data.tar.gz: 2c639cbe92edd74939c8b9ca2785c64a68b0d827c61f50d638e26605dff70169c1354893fc2a05ff976e4d6a40e5530145889520691555d18719caf714a1050d
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Ruby wrapper for [Renshuu], the Japanese learning platform.
|
|
4
4
|
|
|
5
|
+
[![Pipeline status badge][pipeline]][pipelines]
|
|
6
|
+
[![Coverage badge][coverage]][pipelines]
|
|
7
|
+
[![Latest release badge][latest-release]][releases]
|
|
8
|
+
|
|
5
9
|
[Renshuu]: https://renshuu.org
|
|
10
|
+
[pipeline]: https://gitlab.com/Richard-Degenne/renshuu/badges/dev/pipeline.svg?ignore_skipped=true
|
|
11
|
+
[pipelines]: https://gitlab.com/Richard-Degenne/renshuu/-/pipelines/
|
|
12
|
+
[coverage]: https://gitlab.com/Richard-Degenne/renshuu/badges/dev/coverage.svg
|
|
13
|
+
[latest-release]: https://gitlab.com/Richard-Degenne/renshuu/-/badges/release.svg
|
|
14
|
+
[releases]: https://gitlab.com/Richard-Degenne/renshuu/-/releases/
|
|
6
15
|
|
|
7
16
|
## Installation
|
|
8
17
|
|
|
@@ -99,6 +108,82 @@ word = Renshuu::Word.get(370)
|
|
|
99
108
|
word.sentences
|
|
100
109
|
```
|
|
101
110
|
|
|
111
|
+
### Study lists
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
# Lists the user's study lists
|
|
115
|
+
Renshuu::Schedule.list
|
|
116
|
+
|
|
117
|
+
# Retrieves a specific list
|
|
118
|
+
Renshuu::Schedule.get(36555)
|
|
119
|
+
|
|
120
|
+
# Retrieves the contents of a list
|
|
121
|
+
list = Renshuu::Schedule.get(36555)
|
|
122
|
+
list.contents
|
|
123
|
+
list.contents(page: 2, group: :review_today)
|
|
124
|
+
|
|
125
|
+
# Manage list content
|
|
126
|
+
kanji = Renshuu::Kanji.get('食')
|
|
127
|
+
kanji.add_to_list(list)
|
|
128
|
+
kanji.remove_from_list(list)
|
|
129
|
+
# Or
|
|
130
|
+
list.add(kanji)
|
|
131
|
+
list.remove(kanji)
|
|
132
|
+
|
|
133
|
+
word = Renshuu::Word.get(370)
|
|
134
|
+
word.add_to_list(list)
|
|
135
|
+
word.remove_from_list(list)
|
|
136
|
+
# Or
|
|
137
|
+
list.add(word)
|
|
138
|
+
list.remove(word)
|
|
139
|
+
|
|
140
|
+
grammar = Renshuu::Grammar.get(927)
|
|
141
|
+
grammar.add_to_list(list)
|
|
142
|
+
grammar.remove_from_list(list)
|
|
143
|
+
# Or
|
|
144
|
+
list.add(grammar)
|
|
145
|
+
list.remove(grammar)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Lists
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
# Lists the user's lists
|
|
152
|
+
Renshuu::List.list
|
|
153
|
+
|
|
154
|
+
# Retrieves the contents of a list
|
|
155
|
+
list = Renshuu::List.list.first.lists.first # Browsing the list groups
|
|
156
|
+
list.contents
|
|
157
|
+
list.contents(page: 2)
|
|
158
|
+
|
|
159
|
+
# Manage list content
|
|
160
|
+
kanji = Renshuu::Kanji.get('食')
|
|
161
|
+
kanji.add_to_list(list)
|
|
162
|
+
kanji.remove_from_list(list)
|
|
163
|
+
# Or
|
|
164
|
+
list.add(kanji)
|
|
165
|
+
list.remove(kanji)
|
|
166
|
+
|
|
167
|
+
word = Renshuu::Word.get(370)
|
|
168
|
+
word.add_to_list(list)
|
|
169
|
+
word.remove_from_list(list)
|
|
170
|
+
# Or
|
|
171
|
+
list.add(word)
|
|
172
|
+
list.remove(word)
|
|
173
|
+
|
|
174
|
+
grammar = Renshuu::Grammar.get(927)
|
|
175
|
+
grammar.add_to_list(list)
|
|
176
|
+
grammar.remove_from_list(list)
|
|
177
|
+
# Or
|
|
178
|
+
list.add(grammar)
|
|
179
|
+
list.remove(grammar)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Documentation
|
|
183
|
+
|
|
184
|
+
A comprehensive documenation is available at
|
|
185
|
+
<https://renshuu.richarddegenne.fr>.
|
|
186
|
+
|
|
102
187
|
## Development
|
|
103
188
|
|
|
104
189
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
@@ -4,6 +4,9 @@ module Renshuu
|
|
|
4
4
|
##
|
|
5
5
|
# Model class for grammar entries.
|
|
6
6
|
class Grammar < Model
|
|
7
|
+
include Schedulable
|
|
8
|
+
include Listable
|
|
9
|
+
|
|
7
10
|
##
|
|
8
11
|
# Searches the grammar dictionary.
|
|
9
12
|
#
|
|
@@ -65,11 +68,15 @@ module Renshuu
|
|
|
65
68
|
##
|
|
66
69
|
# @!attribute [r] models
|
|
67
70
|
# @return [Array<Model>]
|
|
68
|
-
attribute
|
|
71
|
+
attribute(:models, Types::Array.of(Model).default { [] })
|
|
69
72
|
|
|
70
73
|
##
|
|
71
74
|
# @!attribute [r] user_data
|
|
72
|
-
# @return [UserData]
|
|
73
|
-
attribute :user_data, UserData
|
|
75
|
+
# @return [UserData, nil]
|
|
76
|
+
attribute? :user_data, UserData
|
|
77
|
+
##
|
|
78
|
+
# @!attribute [r] presence
|
|
79
|
+
# @return [Presence, nil]
|
|
80
|
+
attribute? :presence, Presence
|
|
74
81
|
end
|
|
75
82
|
end
|
data/lib/renshuu/models/kanji.rb
CHANGED
|
@@ -6,6 +6,9 @@ module Renshuu
|
|
|
6
6
|
#
|
|
7
7
|
# @see https://api.renshuu.org/docs/#/Kanji
|
|
8
8
|
class Kanji < Model
|
|
9
|
+
include Schedulable
|
|
10
|
+
include Listable
|
|
11
|
+
|
|
9
12
|
##
|
|
10
13
|
# Searches the Renshuu kanji dictionary.
|
|
11
14
|
#
|
|
@@ -102,58 +105,9 @@ module Renshuu
|
|
|
102
105
|
# @!attribute [r] user_data
|
|
103
106
|
# @return [UserData, nil]
|
|
104
107
|
attribute? :user_data, UserData
|
|
105
|
-
|
|
106
108
|
##
|
|
107
|
-
#
|
|
108
|
-
|
|
109
|
-
##
|
|
110
|
-
# Model class for kanji schedule presence.
|
|
111
|
-
class Schedule < Model
|
|
112
|
-
##
|
|
113
|
-
# @!attribute [r] sched_id
|
|
114
|
-
# @return [String]
|
|
115
|
-
attribute :sched_id, Types::String
|
|
116
|
-
alias schedule_id sched_id
|
|
117
|
-
##
|
|
118
|
-
# @!attribute [r] name
|
|
119
|
-
# @return [String]
|
|
120
|
-
attribute :name, Types::String
|
|
121
|
-
##
|
|
122
|
-
# @!attribute [r] has_word
|
|
123
|
-
# @return [Boolean]
|
|
124
|
-
attribute :has_word, Types::Params::Bool
|
|
125
|
-
end
|
|
126
|
-
##
|
|
127
|
-
# @!attribute [r] scheds
|
|
128
|
-
# @return [Array<Schedule>]
|
|
129
|
-
attribute :scheds, Types::Array.of(Schedule)
|
|
130
|
-
alias schedules scheds
|
|
131
|
-
|
|
132
|
-
##
|
|
133
|
-
# Model class for kanji list presence.
|
|
134
|
-
class List < Model
|
|
135
|
-
##
|
|
136
|
-
# @!attribute [r] list_id
|
|
137
|
-
# @return [String]
|
|
138
|
-
attribute :list_id, Types::String
|
|
139
|
-
##
|
|
140
|
-
# @!attribute [r] name
|
|
141
|
-
# @return [String]
|
|
142
|
-
attribute :name, Types::String
|
|
143
|
-
##
|
|
144
|
-
# @!attribute [r] set_name
|
|
145
|
-
# @return [String, nil]
|
|
146
|
-
attribute? :set_name, Types::String
|
|
147
|
-
##
|
|
148
|
-
# @!attribute [r] has_word
|
|
149
|
-
# @return [Boolean]
|
|
150
|
-
attribute :has_word, Types::Params::Bool
|
|
151
|
-
end
|
|
152
|
-
##
|
|
153
|
-
# @!attribute [r] lists
|
|
154
|
-
# @return [Array<List>]
|
|
155
|
-
attribute :lists, Types::Array.of(List)
|
|
156
|
-
end
|
|
109
|
+
# @!attribute [r] presence
|
|
110
|
+
# @return [Presence, nil]
|
|
157
111
|
attribute? :presence, Presence
|
|
158
112
|
end
|
|
159
113
|
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Renshuu
|
|
4
|
+
##
|
|
5
|
+
# Model class for lists.
|
|
6
|
+
#
|
|
7
|
+
# @see https://api.renshuu.org/docs/#/User
|
|
8
|
+
class List < Model
|
|
9
|
+
##
|
|
10
|
+
# Mapping between list termtypes and model classes.
|
|
11
|
+
#
|
|
12
|
+
# @see #term_class
|
|
13
|
+
TERMTYPES = {
|
|
14
|
+
'kanji' => Kanji, 'vocab' => Word, 'grammar' => Grammar,
|
|
15
|
+
'sent' => Sentence
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Retrives list groups from the API.
|
|
20
|
+
#
|
|
21
|
+
# @return [Array<Group>]
|
|
22
|
+
def self.list
|
|
23
|
+
body = Renshuu.client.query(:get, 'v1/lists')
|
|
24
|
+
|
|
25
|
+
body.fetch(:termtype_groups).flat_map do |termtype_group|
|
|
26
|
+
termtype = termtype_group.fetch(:termtype)
|
|
27
|
+
termtype_group.fetch(:groups).map { Group.new(termtype:, **_1) }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Model class for list groups, _a.k.a._ sets.
|
|
33
|
+
class Group < Model
|
|
34
|
+
##
|
|
35
|
+
# @!attribute [r] group_title
|
|
36
|
+
# @return [String]
|
|
37
|
+
attribute :group_title, Types::String
|
|
38
|
+
alias title group_title
|
|
39
|
+
##
|
|
40
|
+
# @!attribute [r] termtype
|
|
41
|
+
# @return [String]
|
|
42
|
+
attribute :termtype, Types::String
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
# @!attribute [r] lists
|
|
46
|
+
# @return [Array<List>]
|
|
47
|
+
attribute :lists, Types::Array.of(List)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# @!attribute [r] list_id
|
|
52
|
+
# @return [String]
|
|
53
|
+
attribute :list_id, Types::String
|
|
54
|
+
alias id list_id
|
|
55
|
+
##
|
|
56
|
+
# @!attribute [r] termtype
|
|
57
|
+
# @return [String]
|
|
58
|
+
attribute :termtype, Types::String
|
|
59
|
+
##
|
|
60
|
+
# @!attribute [r] title
|
|
61
|
+
# @return [String]
|
|
62
|
+
attribute :title, Types::String
|
|
63
|
+
##
|
|
64
|
+
# @!attribute [r] description
|
|
65
|
+
# @return [String]
|
|
66
|
+
attribute :description, Types::String
|
|
67
|
+
##
|
|
68
|
+
# @!attribute [r] num_terms
|
|
69
|
+
# @return [Integer]
|
|
70
|
+
attribute :num_terms, Types::Integer
|
|
71
|
+
##
|
|
72
|
+
# @!attribute [r] privacy
|
|
73
|
+
# @return [String]
|
|
74
|
+
attribute :privacy, Types::String
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# Retrieves terms contained in the list.
|
|
78
|
+
#
|
|
79
|
+
# @param [Integer, nil] page
|
|
80
|
+
#
|
|
81
|
+
# @return [Array<Kanji,Word,Grammar,Sentence>] Depending on the type of list
|
|
82
|
+
def contents(page: nil)
|
|
83
|
+
params = { pg: page }.compact
|
|
84
|
+
body = Renshuu.client.query(:get, "v1/list/#{id}", params:)
|
|
85
|
+
|
|
86
|
+
body.dig(:contents, :terms).map { term_class.new(_1) }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
##
|
|
90
|
+
# Adds the given item to the list.
|
|
91
|
+
#
|
|
92
|
+
# @param [Listable] item
|
|
93
|
+
#
|
|
94
|
+
# @return [Void]
|
|
95
|
+
# @raise [ArgumentError] If the type of the item does not match the
|
|
96
|
+
# list's type
|
|
97
|
+
#
|
|
98
|
+
# @see Listable#add_to_list
|
|
99
|
+
def add(item)
|
|
100
|
+
unless item.is_a?(term_class)
|
|
101
|
+
raise ArgumentError, "Expected a `#{term_class}`, got a `#{item.class}`"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
item.add_to_list(self)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# Removes the given item from the list.
|
|
109
|
+
#
|
|
110
|
+
# @param [Listable] item
|
|
111
|
+
#
|
|
112
|
+
# @return [Void]
|
|
113
|
+
# @raise [ArgumentError] If the type of the item does not match the
|
|
114
|
+
# list's type
|
|
115
|
+
#
|
|
116
|
+
# @see Listable#remove_from_list
|
|
117
|
+
def remove(item)
|
|
118
|
+
unless item.is_a?(term_class)
|
|
119
|
+
raise ArgumentError, "Expected a `#{term_class}`, got a `#{item.class}`"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
item.remove_from_list(self)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# Model class to use for this list's terms.
|
|
127
|
+
#
|
|
128
|
+
# @return [Class]
|
|
129
|
+
# @raise [KeyError]
|
|
130
|
+
#
|
|
131
|
+
# @see ::TERMTYPES
|
|
132
|
+
def term_class
|
|
133
|
+
@term_class ||= TERMTYPES.fetch(termtype) do |key|
|
|
134
|
+
raise KeyError, "Unkown list type `#{key}`"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Renshuu
|
|
4
|
+
##
|
|
5
|
+
# Mixin for items that can be added to study {List}s.
|
|
6
|
+
#
|
|
7
|
+
# = Interface
|
|
8
|
+
#
|
|
9
|
+
# This mixin rely on the following interface:
|
|
10
|
+
#
|
|
11
|
+
# * +#id+: Identifier of the listable item;
|
|
12
|
+
# * +.base_route+: Path part used in URL construction.
|
|
13
|
+
module Listable
|
|
14
|
+
def self.included(base) # :nodoc:
|
|
15
|
+
super
|
|
16
|
+
|
|
17
|
+
base.extend(ClassMethods)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# Adds the item to the given list.
|
|
22
|
+
#
|
|
23
|
+
# @param [List, String] list
|
|
24
|
+
#
|
|
25
|
+
# @return [Void]
|
|
26
|
+
def add_to_list(list)
|
|
27
|
+
list_id = case list
|
|
28
|
+
when List then list.id
|
|
29
|
+
else list
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Renshuu.client.query(
|
|
33
|
+
:put, "v1/#{self.class.base_route}/#{id}", params: { list_id: }
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Removes the item from the given list.
|
|
39
|
+
#
|
|
40
|
+
# @param [List, String] list
|
|
41
|
+
#
|
|
42
|
+
# @return [Void]
|
|
43
|
+
def remove_from_list(list)
|
|
44
|
+
list_id = case list
|
|
45
|
+
when List then list.id
|
|
46
|
+
else list
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Renshuu.client.query(
|
|
50
|
+
:delete, "v1/#{self.class.base_route}/#{id}", params: { list_id: }
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Module for class methods provided with the mixin.
|
|
56
|
+
module ClassMethods
|
|
57
|
+
##
|
|
58
|
+
# Path part used in URL construction.
|
|
59
|
+
#
|
|
60
|
+
# Defaults to the class name, demodulized and underscored. Override to
|
|
61
|
+
# change behavior.
|
|
62
|
+
#
|
|
63
|
+
# @return [String]
|
|
64
|
+
def base_route
|
|
65
|
+
name.demodulize.underscore
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Renshuu
|
|
4
|
+
##
|
|
5
|
+
# Mixin for items that can be added to study {Schedule}s.
|
|
6
|
+
#
|
|
7
|
+
# = Interface
|
|
8
|
+
#
|
|
9
|
+
# This mixin rely on the following interface:
|
|
10
|
+
#
|
|
11
|
+
# * +#id+: Identifier of the schedulable item;
|
|
12
|
+
# * +.base_route+: Path part used in URL construction.
|
|
13
|
+
module Schedulable
|
|
14
|
+
def self.included(base)
|
|
15
|
+
super
|
|
16
|
+
|
|
17
|
+
base.extend(ClassMethods)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# Adds the item to the given schedule.
|
|
22
|
+
#
|
|
23
|
+
# @param [Schedule, String] schedule
|
|
24
|
+
#
|
|
25
|
+
# @return [Void]
|
|
26
|
+
def add_to_schedule(schedule)
|
|
27
|
+
sched_id = case schedule
|
|
28
|
+
when Schedule then schedule.id
|
|
29
|
+
else schedule
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Renshuu.client.query(
|
|
33
|
+
:put, "v1/#{self.class.base_route}/#{id}", params: { sched_id: }
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Removes the item from the given schedule.
|
|
39
|
+
#
|
|
40
|
+
# @param [Schedule, String] schedule
|
|
41
|
+
#
|
|
42
|
+
# @return [Void]
|
|
43
|
+
def remove_from_schedule(schedule)
|
|
44
|
+
sched_id = case schedule
|
|
45
|
+
when Schedule then schedule.id
|
|
46
|
+
else schedule
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Renshuu.client.query(
|
|
50
|
+
:delete, "v1/#{self.class.base_route}/#{id}", params: { sched_id: }
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Module for class methods provided with the mixin.
|
|
56
|
+
module ClassMethods
|
|
57
|
+
##
|
|
58
|
+
# Path part used in URL construction.
|
|
59
|
+
#
|
|
60
|
+
# Defaults to the class name, demodulized and underscored. Override to
|
|
61
|
+
# change behavior.
|
|
62
|
+
#
|
|
63
|
+
# @return [String]
|
|
64
|
+
def base_route
|
|
65
|
+
name.demodulize.underscore
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Renshuu
|
|
4
|
+
##
|
|
5
|
+
# Model class for term presence.
|
|
6
|
+
class Presence < Model
|
|
7
|
+
##
|
|
8
|
+
# Model class for schedule presence.
|
|
9
|
+
class Schedule < Model
|
|
10
|
+
##
|
|
11
|
+
# @!attribute [r] sched_id
|
|
12
|
+
# @return [String]
|
|
13
|
+
attribute :sched_id, Types::String
|
|
14
|
+
alias schedule_id sched_id
|
|
15
|
+
##
|
|
16
|
+
# @!attribute [r] name
|
|
17
|
+
# @return [String]
|
|
18
|
+
attribute :name, Types::String
|
|
19
|
+
##
|
|
20
|
+
# @!attribute [r] has_word
|
|
21
|
+
# @return [Boolean]
|
|
22
|
+
attribute :has_word, Types::Params::Bool
|
|
23
|
+
alias has_word? has_word
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# Retrieves the schedule associated with this presence item.
|
|
27
|
+
#
|
|
28
|
+
# @return [Renshuu::Schedule]
|
|
29
|
+
#
|
|
30
|
+
# @see Renshuu::Schedule.get
|
|
31
|
+
def schedule
|
|
32
|
+
Renshuu::Schedule.get(schedule_id)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
##
|
|
36
|
+
# @!attribute [r] scheds
|
|
37
|
+
# @return [Array<Schedule>]
|
|
38
|
+
attribute(:scheds, Types::Array.of(Schedule).default { [] })
|
|
39
|
+
alias schedules scheds
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# Model class for list presence.
|
|
43
|
+
class List < Model
|
|
44
|
+
##
|
|
45
|
+
# @!attribute [r] list_id
|
|
46
|
+
# @return [String]
|
|
47
|
+
attribute :list_id, Types::String
|
|
48
|
+
##
|
|
49
|
+
# @!attribute [r] name
|
|
50
|
+
# @return [String]
|
|
51
|
+
attribute :name, Types::String
|
|
52
|
+
##
|
|
53
|
+
# @!attribute [r] set_name
|
|
54
|
+
# @return [String, nil]
|
|
55
|
+
attribute? :set_name, Types::String
|
|
56
|
+
##
|
|
57
|
+
# @!attribute [r] has_word
|
|
58
|
+
# @return [Boolean]
|
|
59
|
+
attribute :has_word, Types::Params::Bool
|
|
60
|
+
end
|
|
61
|
+
##
|
|
62
|
+
# @!attribute [r] lists
|
|
63
|
+
# @return [Array<List>]
|
|
64
|
+
attribute(:lists, Types::Array.of(List).default { [] })
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Renshuu
|
|
4
|
+
##
|
|
5
|
+
# Model class for schedules.
|
|
6
|
+
#
|
|
7
|
+
# @see https://api.renshuu.org/docs/#/Schedules
|
|
8
|
+
class Schedule < Model
|
|
9
|
+
##
|
|
10
|
+
# Mapping between schedule booktypes and model classes.
|
|
11
|
+
#
|
|
12
|
+
# @see #term_class
|
|
13
|
+
BOOKTYPES = {
|
|
14
|
+
'kanji' => Kanji, 'vocab' => Word, 'grammar' => Grammar,
|
|
15
|
+
'sent' => Sentence
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Lists all schedules of the current user.
|
|
20
|
+
#
|
|
21
|
+
# @return [Array<Schedule>]
|
|
22
|
+
#
|
|
23
|
+
# @see https://api.renshuu.org/docs/#/Schedules/get_schedule
|
|
24
|
+
def self.list
|
|
25
|
+
body = Renshuu.client.query(:get, 'v1/schedule')
|
|
26
|
+
|
|
27
|
+
body.fetch(:schedules).map { new(_1) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# Retrieves a specific schedule from the API.
|
|
32
|
+
#
|
|
33
|
+
# @param [String, Integer] id
|
|
34
|
+
# @return [Schedule]
|
|
35
|
+
#
|
|
36
|
+
# @see https://api.renshuu.org/docs/#/Schedules/get_schedule__id_
|
|
37
|
+
def self.get(id)
|
|
38
|
+
body = Renshuu.client.query(:get, "v1/schedule/#{id}")
|
|
39
|
+
|
|
40
|
+
body.fetch(:schedules).first.then { new(_1) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# @!attribute [r] id
|
|
45
|
+
# @return [String]
|
|
46
|
+
attribute :id, Types::String
|
|
47
|
+
##
|
|
48
|
+
# @!attribute [r] name
|
|
49
|
+
# @return [String]
|
|
50
|
+
attribute :name, Types::String
|
|
51
|
+
##
|
|
52
|
+
# @!attribute [r] booktype
|
|
53
|
+
# @return [String]
|
|
54
|
+
attribute :booktype, Types::String
|
|
55
|
+
##
|
|
56
|
+
# @!attribute [r] is_frozen
|
|
57
|
+
# @return [Boolean]
|
|
58
|
+
attribute :is_frozen, Types::Params::Bool
|
|
59
|
+
|
|
60
|
+
##
|
|
61
|
+
# Model class for schedules today counts.
|
|
62
|
+
class TodayCount < Model
|
|
63
|
+
transform_keys do |key|
|
|
64
|
+
key == :new ? :new_terms : key
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# @!attribute [r] review
|
|
69
|
+
# @return [Integer]
|
|
70
|
+
attribute :review, Types::Integer
|
|
71
|
+
##
|
|
72
|
+
# @!attribute [r] new_terms
|
|
73
|
+
# @note +new+ is a reserved keyword, so we have to circumvent that.
|
|
74
|
+
# @return [Integer]
|
|
75
|
+
attribute :new_terms, Types::Integer
|
|
76
|
+
end
|
|
77
|
+
##
|
|
78
|
+
# @!attribute [r] today
|
|
79
|
+
# rn [TodayCount]
|
|
80
|
+
attribute :today, TodayCount
|
|
81
|
+
|
|
82
|
+
##
|
|
83
|
+
# Model class for schedule upcoming counts.
|
|
84
|
+
class UpcomingCount < Model
|
|
85
|
+
##
|
|
86
|
+
# @!attribute [r] days_in_future
|
|
87
|
+
# @return [Integer]
|
|
88
|
+
attribute :days_in_future, Types::Integer
|
|
89
|
+
##
|
|
90
|
+
# @!attribute [r] terms_to_review
|
|
91
|
+
# @return [Integer]
|
|
92
|
+
attribute :terms_to_review, Types::Integer
|
|
93
|
+
end
|
|
94
|
+
##
|
|
95
|
+
# @!attribute [r] upcoming
|
|
96
|
+
# @return [Array<UpcomingCount>]
|
|
97
|
+
attribute :upcoming, Types::Array.of(UpcomingCount)
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
# Model class for schedule term counts.
|
|
101
|
+
class TermCount < Model
|
|
102
|
+
##
|
|
103
|
+
# @!attribute [r] total_count
|
|
104
|
+
# @return [Integer]
|
|
105
|
+
attribute :total_count, Types::Integer
|
|
106
|
+
##
|
|
107
|
+
# @!attribute [r] studied_count
|
|
108
|
+
# @return [Integer]
|
|
109
|
+
attribute :studied_count, Types::Integer
|
|
110
|
+
##
|
|
111
|
+
# @!attribute [r] unstudied_count
|
|
112
|
+
# @return [Integer]
|
|
113
|
+
attribute :unstudied_count, Types::Integer
|
|
114
|
+
##
|
|
115
|
+
# @!attribute [r] hidden_count
|
|
116
|
+
# @return [Integer]
|
|
117
|
+
attribute :hidden_count, Types::Integer
|
|
118
|
+
end
|
|
119
|
+
##
|
|
120
|
+
# @!attribute [r] terms
|
|
121
|
+
# @return [TermCount]
|
|
122
|
+
attribute :terms, TermCount
|
|
123
|
+
|
|
124
|
+
##
|
|
125
|
+
# Model class for schedule new term counts.
|
|
126
|
+
class NewTermCount < Model
|
|
127
|
+
##
|
|
128
|
+
# @!attribute [r] today_count
|
|
129
|
+
# @return [Integer]
|
|
130
|
+
attribute :today_count, Types::Integer
|
|
131
|
+
##
|
|
132
|
+
# @!attribute [r] rolling_week_count
|
|
133
|
+
# @return [Integer]
|
|
134
|
+
attribute :rolling_week_count, Types::Integer
|
|
135
|
+
end
|
|
136
|
+
##
|
|
137
|
+
# @!attribute [r] new_terms
|
|
138
|
+
# @return [NewTermCount]
|
|
139
|
+
attribute :new_terms, NewTermCount
|
|
140
|
+
|
|
141
|
+
##
|
|
142
|
+
# Retrieves the terms contained in the schedule.
|
|
143
|
+
#
|
|
144
|
+
# @param [Integer, nil] page
|
|
145
|
+
# @param [Symbol, nil] group One of +:all+, +:blocked+, +:studied+,
|
|
146
|
+
# +:notyetstudied+, +:cannot_study+, +:review_today+, +:mastery_1+,
|
|
147
|
+
# +:mastery_2+, +:mastery_3+, +:mastery_4+, +:mastery_5+, +:mastery_6+,
|
|
148
|
+
# +:mastery_7+, +:mastery_8+ or +:mastery_9+.
|
|
149
|
+
#
|
|
150
|
+
# @return [Array<Kanji,Word,Grammar,Sentence>] Depending on the type of
|
|
151
|
+
# schedule
|
|
152
|
+
def contents(page: nil, group: nil)
|
|
153
|
+
params = { pg: page, group: }.compact
|
|
154
|
+
body = Renshuu.client.query(:get, "v1/schedule/#{id}/list", params:)
|
|
155
|
+
|
|
156
|
+
body.dig(:contents, :terms).map { term_class.new(_1) }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
##
|
|
160
|
+
# Adds the given item to the schedule.
|
|
161
|
+
#
|
|
162
|
+
# @param [Schedulable] item
|
|
163
|
+
#
|
|
164
|
+
# @return [Void]
|
|
165
|
+
# @raise [ArgumentError] If the type of the item does not match the
|
|
166
|
+
# schedule's type
|
|
167
|
+
#
|
|
168
|
+
# @see Schedulable#add_to_schedule
|
|
169
|
+
def add(item)
|
|
170
|
+
unless item.is_a?(term_class)
|
|
171
|
+
raise ArgumentError, "Expected a `#{term_class}`, got a `#{item.class}`"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
item.add_to_schedule(self)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
##
|
|
178
|
+
# Removes the given item from the schedule.
|
|
179
|
+
#
|
|
180
|
+
# @param [Schedulable] item
|
|
181
|
+
#
|
|
182
|
+
# @return [Void]
|
|
183
|
+
# @raise [ArgumentError] If the type of the item does not match the
|
|
184
|
+
# schedule's type
|
|
185
|
+
#
|
|
186
|
+
# @see Schedulable#remove_from_schedule
|
|
187
|
+
def remove(item)
|
|
188
|
+
unless item.is_a?(term_class)
|
|
189
|
+
raise ArgumentError, "Expected a `#{term_class}`, got a `#{item.class}`"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
item.remove_from_schedule(self)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# Model class to use for this schedule's terms.
|
|
197
|
+
#
|
|
198
|
+
# @return [Class]
|
|
199
|
+
# @raise [KeyError]
|
|
200
|
+
#
|
|
201
|
+
# @see ::BOOKTYPES
|
|
202
|
+
def term_class
|
|
203
|
+
@term_class ||= BOOKTYPES.fetch(booktype) do |key|
|
|
204
|
+
raise KeyError, "Unkown schedule type `#{key}`"
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
@@ -46,8 +46,8 @@ module Renshuu
|
|
|
46
46
|
attribute :japanese, Types::String
|
|
47
47
|
##
|
|
48
48
|
# @!attribute [r] hiragana
|
|
49
|
-
# @return [String]
|
|
50
|
-
attribute :hiragana, Types::String
|
|
49
|
+
# @return [String, nil]
|
|
50
|
+
attribute? :hiragana, Types::String
|
|
51
51
|
##
|
|
52
52
|
# @!attribute [r] meaning
|
|
53
53
|
# @return [Hash{String => String}]
|
data/lib/renshuu/models/types.rb
CHANGED
|
@@ -9,12 +9,11 @@ module Renshuu
|
|
|
9
9
|
##
|
|
10
10
|
# Dry type for dates.
|
|
11
11
|
Date = Constructor(::Date) do |date|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
else
|
|
17
|
-
::Date.parse(date)
|
|
12
|
+
case date
|
|
13
|
+
when ::Date then date
|
|
14
|
+
when 'Not yet' then nil
|
|
15
|
+
when 'Now' then ::Date.today
|
|
16
|
+
else ::Date.parse(date)
|
|
18
17
|
end
|
|
19
18
|
end
|
|
20
19
|
|
data/lib/renshuu/models/word.rb
CHANGED
|
@@ -6,6 +6,9 @@ module Renshuu
|
|
|
6
6
|
#
|
|
7
7
|
# @see https://api.renshuu.org/docs/#/Vocabulary
|
|
8
8
|
class Word < Model
|
|
9
|
+
include Schedulable
|
|
10
|
+
include Listable
|
|
11
|
+
|
|
9
12
|
##
|
|
10
13
|
# Searches the Renshuu vocabulary dictionary.
|
|
11
14
|
#
|
|
@@ -44,13 +47,13 @@ module Renshuu
|
|
|
44
47
|
attribute :hiragana_full, Types::String
|
|
45
48
|
##
|
|
46
49
|
# @!attribute [r] pic
|
|
47
|
-
# @return [Array<URI
|
|
48
|
-
attribute
|
|
50
|
+
# @return [Array<URI>]
|
|
51
|
+
attribute(:pic, Types::Array.of(Types::URI).default { [] })
|
|
49
52
|
alias pictures pic
|
|
50
53
|
##
|
|
51
54
|
# @!attribute [r] markers
|
|
52
55
|
# @return [Array<String>]
|
|
53
|
-
attribute
|
|
56
|
+
attribute(:markers, Types::Array.of(Types::String).default { [] })
|
|
54
57
|
##
|
|
55
58
|
# @!attribute [r] pitch
|
|
56
59
|
# @return [Array<String>]
|
|
@@ -69,6 +72,10 @@ module Renshuu
|
|
|
69
72
|
# @!attribute [r] user_data
|
|
70
73
|
# @return [UserData, nil]
|
|
71
74
|
attribute? :user_data, UserData
|
|
75
|
+
##
|
|
76
|
+
# @!attribute [r] presence
|
|
77
|
+
# @return [Presence, nil]
|
|
78
|
+
attribute? :presence, Presence
|
|
72
79
|
|
|
73
80
|
##
|
|
74
81
|
# Retrieves sentences using this word from the dictionary.
|
data/lib/renshuu/models.rb
CHANGED
|
@@ -6,10 +6,15 @@ end
|
|
|
6
6
|
# Types
|
|
7
7
|
require_relative 'models/types'
|
|
8
8
|
|
|
9
|
+
# Mixins
|
|
10
|
+
require_relative 'models/mixins/listable'
|
|
11
|
+
require_relative 'models/mixins/schedulable'
|
|
12
|
+
|
|
9
13
|
# Base class
|
|
10
14
|
require_relative 'models/model'
|
|
11
15
|
|
|
12
16
|
# Other models (respect blank lines for dependency order)
|
|
17
|
+
require_relative 'models/presence'
|
|
13
18
|
require_relative 'models/user_data'
|
|
14
19
|
|
|
15
20
|
require_relative 'models/grammar'
|
|
@@ -17,3 +22,6 @@ require_relative 'models/kanji'
|
|
|
17
22
|
require_relative 'models/profile'
|
|
18
23
|
require_relative 'models/sentence'
|
|
19
24
|
require_relative 'models/word'
|
|
25
|
+
|
|
26
|
+
require_relative 'models/list'
|
|
27
|
+
require_relative 'models/schedule'
|
data/lib/renshuu/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: renshuu
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Richard Degenne
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-12-
|
|
11
|
+
date: 2025-12-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: dry-struct
|
|
@@ -62,8 +62,13 @@ files:
|
|
|
62
62
|
- lib/renshuu/models.rb
|
|
63
63
|
- lib/renshuu/models/grammar.rb
|
|
64
64
|
- lib/renshuu/models/kanji.rb
|
|
65
|
+
- lib/renshuu/models/list.rb
|
|
66
|
+
- lib/renshuu/models/mixins/listable.rb
|
|
67
|
+
- lib/renshuu/models/mixins/schedulable.rb
|
|
65
68
|
- lib/renshuu/models/model.rb
|
|
69
|
+
- lib/renshuu/models/presence.rb
|
|
66
70
|
- lib/renshuu/models/profile.rb
|
|
71
|
+
- lib/renshuu/models/schedule.rb
|
|
67
72
|
- lib/renshuu/models/sentence.rb
|
|
68
73
|
- lib/renshuu/models/types.rb
|
|
69
74
|
- lib/renshuu/models/user_data.rb
|