shanti555890 1.1.4 → 1.1.5

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: f46caa37cfa400f1ba3b8e7b1b413480c0ea4461f8ae973c427de8e48d1fe6e1
4
- data.tar.gz: 4d9f0741be3f817093b3f36325f513d978dda403c383fe365cf68ff3c1dc0816
3
+ metadata.gz: d1558d6e55b64d53eaa5b7a1685c5be1dd961488d13958f7bc061aef9770f6a8
4
+ data.tar.gz: 57949792a2029a2f99ee05842c2cab4979d0c9063a7897cd6c738666ffb0a5ff
5
5
  SHA512:
6
- metadata.gz: db0a4fa5b05210cb613e3939cc54879204e4242196f4eb99a74fd4138447eb6f6828f6877710771734c921bb90f6c0b5353e049a16942f58df8713111af5bf76
7
- data.tar.gz: 9784bf80e8789988c4e56d86311bcd2f0eab3bdaab7555b38c95d4b94b276938e1f2e11121027afe9c971cb253176d77f3fa05d6926d7d33d36075571b96079d
6
+ metadata.gz: 2d19c00704be66e51888b05d6fc4accd23684971e9200cf2569d0ba09270f4cac07573a8fc848c8333901c1db86a51da45a2cbd071f6ea4f0e4ed2e95f386390
7
+ data.tar.gz: 611f503e8ccde5aa1b0ff10cf24944a01fdc9bd7c256570d6a31ec3bf0262e74e60f334fbca4efbdbcb432a2544a58701c57df4120cf7ee8ba95183718642f11
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Shanti555890
4
- VERSION = "1.1.4"
4
+ VERSION = "1.1.5"
5
5
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require_relative '../student/student'
2
+ require_relative './student/student'
3
3
  require 'logger'
4
4
  class AddStudentController
5
5
 
@@ -3,13 +3,13 @@ require_relative 'window'
3
3
  require_relative 'student_list'
4
4
  require_relative 'student_list_db'
5
5
  require 'win32api'
6
- require_relative '../student/student'
6
+ require_relative './student/student'
7
7
  require_relative 'student_file_adapter'
8
8
  require_relative 'add_controller'
9
- require_relative '../strategy/student_list_json'
10
- require_relative '../strategy/student_list_txt'
11
- require_relative '../strategy/student_list_yaml'
12
- require_relative '../all_data/data_list_student_short'
9
+ require_relative './strategy/student_list_json'
10
+ require_relative './strategy/student_list_txt'
11
+ require_relative './strategy/student_list_yaml'
12
+ require_relative './all_data/data_list_student_short'
13
13
  require_relative 'update_controller'
14
14
  require 'fox16'
15
15
  include Fox
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require_relative '../student/student'
2
+ require_relative './student/student'
3
3
  require 'logger'
4
4
  class AddStudentController
5
5
 
@@ -3,13 +3,13 @@ require_relative 'window'
3
3
  require_relative 'student_list'
4
4
  require_relative 'student_list_db'
5
5
  require 'win32api'
6
- require_relative '../student/student'
6
+ require_relative './student/student'
7
7
  require_relative 'student_file_adapter'
8
8
  require_relative 'add_controller'
9
- require_relative '../strategy/student_list_json'
10
- require_relative '../strategy/student_list_txt'
11
- require_relative '../strategy/student_list_yaml'
12
- require_relative '../all_data/data_list_student_short'
9
+ require_relative './strategy/student_list_json'
10
+ require_relative './strategy/student_list_txt'
11
+ require_relative './strategy/student_list_yaml'
12
+ require_relative './all_data/data_list_student_short'
13
13
  require_relative 'update_controller'
14
14
  require 'fox16'
15
15
  include Fox
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require_relative '../strategy/student_list_base'
2
+ require_relative './strategy/student_list_base'
3
3
  class StudentFileAdapter
4
4
  def initialize(type_file,file_address)
5
5
  @file = StudentListBase.new(type_file)
@@ -2,8 +2,8 @@ require 'fox16'
2
2
  include Fox
3
3
  require_relative 'controller'
4
4
  require_relative 'dialog_create_student'
5
- require_relative '../database_labs/tab_lab'
6
- require_relative '../database_labs/tab_student'
5
+ require_relative './database_labs/tab_lab'
6
+ require_relative './database_labs/tab_student'
7
7
  class Window < FXMainWindow
8
8
  def initialize(app)
9
9
  super(app, "Students", width: 1100, height: 800) # увеличиваем размер главного окна
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../all_data/data_list'
3
+ require_relative '../all_data/data_table'
4
+
5
+ class DataListStudentLab<DataList
6
+ public_class_method :new
7
+
8
+ protected
9
+ def get_fields(object)
10
+ [object.number, object.name, object.date_load]
11
+ end
12
+
13
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ class Lab
3
+ def initialize(number: nil, name: nil, date_load: nil)
4
+ raise ArgumentError, "Required fields: name, number and date!" if number.nil? || name.nil?|| date_load.nil?
5
+ @number = number
6
+ @name = name
7
+ @date_load = date_load
8
+ end
9
+
10
+ attr_reader :number, :name, :date_load
11
+
12
+ #добавить валидацию!!!!
13
+ # дата - чтобы предыдущая дата была либо нил, либо раньше
14
+ # название - все что угодно
15
+ # id - генерится программно
16
+
17
+ def to_s
18
+ res = "Lab#{number} #{name} #{date_load}"
19
+ end
20
+
21
+ def to_hash
22
+ info_hash = {}
23
+ %i[number name date_load].each do |field|
24
+ info_hash[field] = send(field)
25
+ end
26
+ info_hash
27
+ end
28
+
29
+ def self.validate_is_date(); end
30
+
31
+
32
+
33
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ class LabList
3
+ def initialize(data_adapter)
4
+ @data_adapter = data_adapter
5
+ end
6
+
7
+ def get_lab_by_id(id_lab); end
8
+ def add_lab(lab); end
9
+ def remove_lab(id_lab);end
10
+ def replace_lab(id_lab, lab); end
11
+ def get_lab_list; end #получение все лаб в базе
12
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'lab'
3
+ require_relative 'data_list_student_lab'
4
+ class StudentLab
5
+ def initialize
6
+ self.db = DBUniversity.instance
7
+ end
8
+
9
+ def get_lab_by_number(id_lab)
10
+ hash = db.execute('SELECT * FROM labs WHERE number = ?', id_lab).first
11
+ return nil if hash.nil?
12
+ Lab.new(**hash.transform_keys(&:to_sym))
13
+ end
14
+ def add_lab(lab)
15
+ db.execute('insert into labs (number, name, date_load) VALUES (?, ?, ?)', *lab_fields(lab)).first
16
+ end
17
+ def remove_lab(id_lab)
18
+ db.execute('DELETE FROM labs WHERE number = ?', id_lab)
19
+ end
20
+ def replace_lab(id_lab, lab)
21
+ db.execute('UPDATE labs SET name=?, date_load=? WHERE number=?',*lab_fields(lab), id_lab)
22
+ end
23
+ def get_lab_list(data_list=nil) #получение все лаб в базе
24
+ labs_hash = db.prepare_exec('SELECT * FROM labs')
25
+ labs = labs_hash.map{|lab| Lab.new(**lab.transform_keys(&:to_sym))}
26
+
27
+ return DataListStudentLab.new(labs) if data_list.nil?
28
+ data_list.replace_objects(labs)
29
+ data_list
30
+ end
31
+
32
+ def get_next_number
33
+ lab_count+1
34
+ end
35
+
36
+ def lab_count
37
+ db.results_as_hash=false
38
+ res=db.execute("Select COUNT(*) from labs").first[0]
39
+ db.results_as_hash=true
40
+ res
41
+ end
42
+
43
+ private
44
+ attr_accessor :db
45
+ def lab_fields(lab)
46
+ [lab.number, lab.name, lab.date_load]
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'student_lab'
3
+ require_relative 'data_list_student_lab'
4
+ class StudentLabController
5
+ def initialize(view)
6
+ @view = view
7
+ @data_list = DataListStudentLab.new([])
8
+ @data_list.add_observer(@view)
9
+ @student_lab = StudentLab.new
10
+ end
11
+
12
+ def refresh_data
13
+ begin
14
+ @data_list = @student_lab.get_lab_list(@data_list)
15
+ rescue SQLite3::SQLException
16
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
17
+ api.call(0, "No connection to DB", "Error", 0)
18
+ exit(false)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StudentLabDbAdapter
4
+ def initialize
5
+ self.client = DBUniversity.instance
6
+ end
7
+
8
+ def get_lab_by_id(id_lab); end
9
+ def add_lab(lab); end
10
+ def remove_lab(id_lab);end
11
+ def replace_lab(id_lab, lab); end
12
+ def get_lab_list; end #получение все лаб в базе
13
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+ require 'fox16'
3
+ include Fox
4
+ require_relative 'student_lab_controller'
5
+ require_relative 'tab_student'
6
+ class TabLab<FXVerticalFrame
7
+ def initialize(parent, *args, &blk)
8
+ super
9
+ @controller= StudentLabController.new(self)
10
+ add_table
11
+ end
12
+
13
+ def add_table
14
+ table_frame = FXVerticalFrame.new(self, :padLeft=>20)
15
+ # page_change_buttons(table_frame)
16
+ # Создаем таблицу
17
+ @table = FXTable.new(table_frame, :opts => TABLE_READONLY|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|TABLE_COL_SIZABLE|TABLE_ROW_RENUMBER, :width=>600, :height=>350)
18
+ @table.setTableSize(16, 3)
19
+
20
+ @table.setColumnText(0, "Номер")
21
+ @table.setColumnText(1, "Наименование")
22
+ @table.setColumnText(2, "Дата выдачи")
23
+
24
+ # Масштабируем таблицу
25
+ @table.setRowHeaderWidth(30)
26
+ # @table.setColumnWidth(0, 150)
27
+ @table.setColumnWidth(1, 300)
28
+ @table.setColumnWidth(2, 150)
29
+ add_crud(self)
30
+
31
+ end
32
+
33
+ def add_crud(parent)
34
+ #добавление кнопок
35
+ btn_list = FXHorizontalFrame.new(parent)
36
+ btn_add = FXButton.new(btn_list, "Добавить", :opts=>BUTTON_NORMAL)
37
+ btn_update = FXButton.new(btn_list, "Обновить", :opts=>BUTTON_NORMAL)
38
+ btn_change = FXButton.new(btn_list, "Изменить", :opts=>BUTTON_NORMAL)
39
+ btn_delete = FXButton.new(btn_list, "Удалить", :opts=>BUTTON_NORMAL)
40
+
41
+ btn_change.disable
42
+ btn_delete.disable
43
+
44
+ # Устанавливаем обработчик события SEL_CHANGED для таблицы
45
+ @table.connect(SEL_CHANGED) do
46
+ num_selected_rows = 0
47
+ (0...@table.getNumRows()).each { |row_index| num_selected_rows+=1 if @table.rowSelected?(row_index)}
48
+
49
+ # Если выделена только одна строка, кнопка должна быть неактивной
50
+ if num_selected_rows == 1
51
+ btn_change.enable
52
+ btn_delete.enable
53
+ # Если выделено несколько строк, кнопка должна быть активной
54
+ elsif num_selected_rows >1
55
+ btn_change.disable
56
+ btn_delete.enable
57
+ end
58
+ end
59
+
60
+ @table.getRowHeader.connect(SEL_RIGHTBUTTONPRESS) do
61
+ @table.killSelection(true)
62
+ btn_change.disable
63
+ btn_delete.disable
64
+ end
65
+
66
+
67
+ btn_update.connect(SEL_COMMAND) do
68
+ refresh
69
+ end
70
+ end
71
+ def refresh
72
+ @controller.refresh_data
73
+ end
74
+ def on_datalist_changed(table)
75
+ TabStudent.update_data_table(@table, table)
76
+ end
77
+
78
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+ require 'fox16'
3
+ include Fox
4
+ require_relative '../database/controller'
5
+ class TabStudent<FXHorizontalFrame
6
+
7
+ def initialize(p, *args, &blk)
8
+ super
9
+ @students_on_page=5
10
+ @current_page=1
11
+ @count_student=0
12
+ @controller = StudentListController.new(self)
13
+ first_tab
14
+ end
15
+
16
+ def update_count_students(count_students)
17
+ @count_student = count_students
18
+ update_page_label
19
+ end
20
+
21
+ def self.update_data_table(table_self, new_table)
22
+ row_number=0
23
+ (0...table_self.getNumRows).each do |row|
24
+ (0...table_self.getNumColumns).each do |col|
25
+ table_self.setItemText(row, col, "")
26
+ end
27
+ end
28
+ new_table.each do |row|
29
+ (1..3).each { |index_field| table_self.setItemText(row_number, index_field-1, row[index_field].to_s) }
30
+ row_number+=1
31
+ end
32
+ end
33
+
34
+ def on_datalist_changed(table)
35
+ TabStudent.update_data_table(@table, table)
36
+ end
37
+ def refresh
38
+ @controller.refresh_data(@current_page, @students_on_page)
39
+ end
40
+
41
+ def first_tab
42
+ add_filters
43
+ add_table
44
+ end
45
+
46
+ def add_filters
47
+ #filter
48
+ frame_filter = FXVerticalFrame.new(self, :padTop=>50)
49
+ frame_filter.resize(500,300)
50
+
51
+ field_filter =[[:git, 'Гит'], [:mail, 'Почта'], [:phone_number, 'Телефон'], [:telegram, 'Телеграм']]
52
+
53
+ #ФИЛЬТР ИМЕНИ
54
+ nameLabel = FXLabel.new(frame_filter, "Фамилия и инициалы")
55
+ name_text_field = FXTextField.new(frame_filter, 40)
56
+ @filter = {short_name: name_text_field}
57
+
58
+ #фильтрация для остальных полей
59
+ field_filter.each do |field|
60
+ @filter[field[0]] = create_radio_group(field, frame_filter)
61
+ end
62
+
63
+ #кнопка очистить
64
+ btn_clear = FXButton.new(frame_filter, "Очистить", :opts=>BUTTON_NORMAL)
65
+ end
66
+
67
+ def add_table
68
+ table_frame = FXVerticalFrame.new(self, :padLeft=>20)
69
+ page_change_buttons(table_frame)
70
+ # Создаем таблицу
71
+ @table = FXTable.new(table_frame, :opts => TABLE_READONLY|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|TABLE_COL_SIZABLE|TABLE_ROW_RENUMBER, :width=>580, :height=>320)
72
+ @table.setTableSize(@students_on_page, 3)
73
+
74
+ @table.setColumnText(0, "ФИО")
75
+ @table.setColumnText(1, "Git")
76
+ @table.setColumnText(2, "Контакт")
77
+
78
+ # Масштабируем таблицу
79
+ @table.setRowHeaderWidth(30)
80
+ @table.setColumnWidth(0, 150)
81
+ @table.setColumnWidth(1, 150)
82
+ @table.setColumnWidth(2, 200)
83
+
84
+ @table.getColumnHeader.connect(SEL_COMMAND) do |a, b,col|
85
+ sort_table_by_column(@table,col)
86
+ end
87
+
88
+ add_crud(table_frame)
89
+ end
90
+
91
+
92
+ #добавление кнопок добавить, изменить, обновить, удалить
93
+ def add_crud(parent)
94
+ #добавление кнопок
95
+ btn_list = FXHorizontalFrame.new(parent)
96
+ btn_add = FXButton.new(btn_list, "Добавить", :opts=>BUTTON_NORMAL)
97
+ btn_update = FXButton.new(btn_list, "Обновить", :opts=>BUTTON_NORMAL)
98
+ combo_change = FXComboBox.new(btn_list, 20, :opts=> FRAME_SUNKEN|FRAME_THICK|LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
99
+ # btn_change = FXButton.new(btn_list, "Изменить", :opts=>BUTTON_NORMAL)
100
+ btn_delete = FXButton.new(btn_list, "Удалить", :opts=>BUTTON_NORMAL)
101
+
102
+ combo_change.disable
103
+ btn_delete.disable
104
+
105
+ combo_change.appendItem("Изменить ФИО")
106
+ combo_change.appendItem("Изменить Git")
107
+ combo_change.appendItem("Изменить контакт")
108
+
109
+
110
+ # Устанавливаем обработчик события SEL_CHANGED для таблицы
111
+ @table.connect(SEL_CHANGED) do
112
+ num_selected_rows = 0
113
+ (0...@table.getNumRows()).each { |row_index| num_selected_rows+=1 if @table.rowSelected?(row_index)}
114
+
115
+ # Если выделена только одна строка, кнопка должна быть неактивной
116
+ if num_selected_rows == 1
117
+ combo_change.enable
118
+ btn_delete.enable
119
+ # Если выделено несколько строк, кнопка должна быть активной
120
+ elsif num_selected_rows >1
121
+ combo_change.disable
122
+ btn_delete.enable
123
+ end
124
+ end
125
+
126
+ @table.getRowHeader.connect(SEL_RIGHTBUTTONPRESS) do
127
+ @table.killSelection(true)
128
+ combo_change.disable
129
+ btn_delete.disable
130
+ end
131
+
132
+ btn_add.connect(SEL_COMMAND) do
133
+ @controller.student_add
134
+ end
135
+
136
+ btn_update.connect(SEL_COMMAND) do
137
+ refresh
138
+ end
139
+
140
+ combo_change.connect(SEL_COMMAND) do
141
+ index = (0...@table.getNumRows).find {|row_index| @table.rowSelected?(row_index)}
142
+ case combo_change.currentItem
143
+ when 0
144
+ @controller.student_change_name(index)
145
+ when 1
146
+ @controller.student_change_git(index)
147
+ when 2
148
+ @controller.student_change_contact(index)
149
+ end
150
+ end
151
+
152
+ btn_delete.connect(SEL_COMMAND) do
153
+ indexes = (0...@table.getNumRows).select{|row_index| @table.rowSelected?(row_index)}
154
+ @controller.student_delete(indexes)
155
+ end
156
+ end
157
+
158
+ #отображение страниц
159
+ def page_change_buttons(parent)
160
+ change_page = FXHorizontalFrame.new(parent, :opts=> LAYOUT_CENTER_X)
161
+ btn_back=FXButton.new(change_page, "Назад", :opts=> BUTTON_INITIAL)
162
+ btn_back.textColor = Fox.FXRGB(0,23,175)
163
+ #добавить отображение со страницы, на которой мы сейчас
164
+ @page_label = FXLabel.new(change_page, '1')
165
+ btn_next=FXButton.new(change_page, "Далее", :opts=> BUTTON_INITIAL)
166
+ btn_next.textColor = Fox.FXRGB(0,23,175)
167
+
168
+ btn_back.connect(SEL_COMMAND) do
169
+ if @current_page!=1
170
+ @current_page-=1
171
+ refresh
172
+ update_page_label
173
+ end
174
+ end
175
+ btn_next.connect(SEL_COMMAND) do
176
+ if @current_page<(@count_student / @students_on_page.to_f).ceil
177
+ @current_page+=1
178
+ refresh
179
+ update_page_label
180
+ end
181
+ end
182
+ end
183
+
184
+ def sort_table_by_column(table, column_index)
185
+ table_data = []
186
+ (0...table.getNumRows()).each do |row_index|
187
+ if table.getItemText(row_index, column_index)!=''
188
+ row=[]
189
+ (0...table.getNumColumns()).each do |col_index|
190
+ row[col_index] = table.getItemText(row_index, col_index)
191
+ end
192
+ table_data<<row
193
+ end
194
+ end
195
+ sorted_table_data = table_data.sort_by { |row_data| row_data[column_index] }
196
+ sorted_table_data.each_with_index do |row_data, row_index|
197
+ row_data.each_with_index do |cell_data, col_index|
198
+ table.setItemText(row_index, col_index, cell_data)
199
+ end
200
+ end
201
+ end
202
+ def create_radio_group(field, parent)
203
+ #Фильтрация гита
204
+ frame_field = FXVerticalFrame.new(parent, LAYOUT_FILL_X||LAYOUT_SIDE_TOP)
205
+ label_field = FXLabel.new(frame_field, field[1])
206
+ line_radio = FXHorizontalFrame.new(frame_field, LAYOUT_FILL_X|LAYOUT_SIDE_TOP)
207
+ # Создаем radiobutton
208
+ radio_yes = FXRadioButton.new(line_radio, "Да")
209
+ radio_no = FXRadioButton.new(line_radio, "Нет")
210
+ radio_no_matter = FXRadioButton.new(line_radio, "Не важно")
211
+ #фильтр
212
+ text_field = FXTextField.new(line_radio, 40)
213
+ #прописываем доступность
214
+ text_field.setEnabled(false)
215
+ radio_yes.connect(SEL_COMMAND) do
216
+ radio_no.check=false
217
+ radio_no_matter.check=false
218
+ if radio_yes.checked?
219
+ text_field.setEnabled(true)
220
+ end
221
+ end
222
+ radio_no.connect(SEL_COMMAND) do
223
+ radio_no_matter.check=false
224
+ radio_yes.check=false
225
+ if radio_no.checked?
226
+ text_field.setEnabled(false)
227
+ end
228
+ end
229
+ radio_no_matter.connect(SEL_COMMAND) do
230
+ radio_no.check=false
231
+ radio_yes.check=false
232
+ if radio_no_matter.checked?
233
+ text_field.setEnabled(false)
234
+ end
235
+ end
236
+ frame_field
237
+ end
238
+ def update_page_label
239
+ @page_label.text = "#{@current_page} / #{(@count_student / @students_on_page.to_f).ceil}"
240
+ end
241
+ end
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shanti555890
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yana Galagan
@@ -53,6 +53,14 @@ files:
53
53
  - lib/source/database/update_git_controller.rb
54
54
  - lib/source/database/update_name_controller.rb
55
55
  - lib/source/database/window.rb
56
+ - lib/source/database_labs/data_list_student_lab.rb
57
+ - lib/source/database_labs/lab.rb
58
+ - lib/source/database_labs/lab_list.rb
59
+ - lib/source/database_labs/student_lab.rb
60
+ - lib/source/database_labs/student_lab_controller.rb
61
+ - lib/source/database_labs/student_lab_db_adapter.rb
62
+ - lib/source/database_labs/tab_lab.rb
63
+ - lib/source/database_labs/tab_student.rb
56
64
  - lib/source/strategy/student_list_base.rb
57
65
  - lib/source/strategy/student_list_json.rb
58
66
  - lib/source/strategy/student_list_strat.rb
@@ -65,6 +73,7 @@ files:
65
73
  - lib/source/update_controller.rb
66
74
  - lib/source/update_git_controller.rb
67
75
  - lib/source/update_name_controller.rb
76
+ - shanti555890-1.1.4.gem
68
77
  - shanti555890.gemspec
69
78
  - sig/shanti555890.rbs
70
79
  homepage: https://github.com/YanaGalagan