tmis 0.1.3

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.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +674 -0
  3. data/README.md +44 -0
  4. data/Rakefile +64 -0
  5. data/bin/tmis +4 -0
  6. data/lib/tmis/engine/database.rb +58 -0
  7. data/lib/tmis/engine/export/timetable_exporter.rb +366 -0
  8. data/lib/tmis/engine/import/abstract_spreadsheet.rb +53 -0
  9. data/lib/tmis/engine/import/spreadsheet_roo.rb +136 -0
  10. data/lib/tmis/engine/import/timetable_manager.rb +110 -0
  11. data/lib/tmis/engine/import/timetable_reader.rb +79 -0
  12. data/lib/tmis/engine/mailer/mailer.rb +51 -0
  13. data/lib/tmis/engine/migrations/10_create_speciality_subjects.rb +17 -0
  14. data/lib/tmis/engine/migrations/11_create_emails.rb +10 -0
  15. data/lib/tmis/engine/migrations/12_add_indexes.rb +32 -0
  16. data/lib/tmis/engine/migrations/1_create_groups.rb +11 -0
  17. data/lib/tmis/engine/migrations/2_create_subgroups.rb +10 -0
  18. data/lib/tmis/engine/migrations/3_create_subjects.rb +11 -0
  19. data/lib/tmis/engine/migrations/4_create_cabinets.rb +12 -0
  20. data/lib/tmis/engine/migrations/5_create_lecturers.rb +14 -0
  21. data/lib/tmis/engine/migrations/6_create_studies.rb +15 -0
  22. data/lib/tmis/engine/migrations/7_create_courses.rb +9 -0
  23. data/lib/tmis/engine/migrations/8_create_specialities.rb +9 -0
  24. data/lib/tmis/engine/migrations/9_create_semesters.rb +10 -0
  25. data/lib/tmis/engine/models/cabinet.rb +18 -0
  26. data/lib/tmis/engine/models/course.rb +11 -0
  27. data/lib/tmis/engine/models/email.rb +19 -0
  28. data/lib/tmis/engine/models/group.rb +31 -0
  29. data/lib/tmis/engine/models/lecturer.rb +45 -0
  30. data/lib/tmis/engine/models/semester.rb +4 -0
  31. data/lib/tmis/engine/models/speciality.rb +3 -0
  32. data/lib/tmis/engine/models/speciality_subject.rb +6 -0
  33. data/lib/tmis/engine/models/study.rb +56 -0
  34. data/lib/tmis/engine/models/subgroup.rb +21 -0
  35. data/lib/tmis/engine/models/subject.rb +19 -0
  36. data/lib/tmis/engine/verificator.rb +96 -0
  37. data/lib/tmis/interface/forms/about.rb +24 -0
  38. data/lib/tmis/interface/forms/console.rb +28 -0
  39. data/lib/tmis/interface/forms/debug_console.rb +32 -0
  40. data/lib/tmis/interface/forms/edit_study.rb +110 -0
  41. data/lib/tmis/interface/forms/expand_changes.rb +128 -0
  42. data/lib/tmis/interface/forms/export_general_timetable.rb +68 -0
  43. data/lib/tmis/interface/forms/export_group_timetable.rb +158 -0
  44. data/lib/tmis/interface/forms/export_lecturer_timetable.rb +171 -0
  45. data/lib/tmis/interface/forms/find.rb +71 -0
  46. data/lib/tmis/interface/forms/import.rb +36 -0
  47. data/lib/tmis/interface/forms/settings.rb +125 -0
  48. data/lib/tmis/interface/forms/ui_about.rb +88 -0
  49. data/lib/tmis/interface/forms/ui_console.rb +68 -0
  50. data/lib/tmis/interface/forms/ui_debug_console.rb +82 -0
  51. data/lib/tmis/interface/forms/ui_edit_study.rb +202 -0
  52. data/lib/tmis/interface/forms/ui_expand_changes.rb +134 -0
  53. data/lib/tmis/interface/forms/ui_export_general_timetable.rb +142 -0
  54. data/lib/tmis/interface/forms/ui_export_group_timetable.rb +160 -0
  55. data/lib/tmis/interface/forms/ui_export_lecturer_timetable.rb +160 -0
  56. data/lib/tmis/interface/forms/ui_find.rb +77 -0
  57. data/lib/tmis/interface/forms/ui_import.rb +134 -0
  58. data/lib/tmis/interface/forms/ui_settings.rb +417 -0
  59. data/lib/tmis/interface/mainwindow.rb +933 -0
  60. data/lib/tmis/interface/models/cabinet_table_model.rb +133 -0
  61. data/lib/tmis/interface/models/course_table_model.rb +87 -0
  62. data/lib/tmis/interface/models/group_table_model.rb +190 -0
  63. data/lib/tmis/interface/models/lecturer_table_model.rb +111 -0
  64. data/lib/tmis/interface/models/semester_table_model.rb +137 -0
  65. data/lib/tmis/interface/models/speciality_subject_table_model.rb +288 -0
  66. data/lib/tmis/interface/models/speciality_table_model.rb +87 -0
  67. data/lib/tmis/interface/models/study_table_model.rb +323 -0
  68. data/lib/tmis/interface/models/subgroup_table_model.rb +136 -0
  69. data/lib/tmis/interface/models/subject_table_model.rb +90 -0
  70. data/lib/tmis/interface/ui_mainwindow.rb +928 -0
  71. data/lib/tmis.rb +45 -0
  72. data/spec/config.rb +49 -0
  73. data/spec/database_spec.rb +18 -0
  74. data/spec/export/timetable_exporter_mocks.rb +20 -0
  75. data/spec/export/timetable_exporter_spec.rb +34 -0
  76. data/spec/factories/factories.rb +65 -0
  77. data/spec/import/test_data/raspisanie_2013.csv +104 -0
  78. data/spec/import/timetable_importer_mocks.rb +6 -0
  79. data/spec/import/timetable_manager_spec.rb +16 -0
  80. data/spec/import/timetable_reader_spec.rb +111 -0
  81. data/spec/import/timetable_roo_spec.rb +48 -0
  82. data/spec/mailer/mailer_spec.rb +37 -0
  83. data/spec/mainwindow_spec.rb +18 -0
  84. data/spec/models/cabinet_spec.rb +33 -0
  85. data/spec/models/course_spec.rb +26 -0
  86. data/spec/models/group_spec.rb +33 -0
  87. data/spec/models/lecturer_spec.rb +38 -0
  88. data/spec/models/semester_spec.rb +26 -0
  89. data/spec/models/speciality_spec.rb +26 -0
  90. data/spec/models/speciality_subject_spec.rb +9 -0
  91. data/spec/models/study_spec.rb +9 -0
  92. data/spec/models/subgroup_spec.rb +26 -0
  93. data/spec/models/subject_spec.rb +39 -0
  94. metadata +290 -0
@@ -0,0 +1,133 @@
1
+ # encoding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require_relative '../../engine/models/cabinet'
4
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
5
+ class CabinetTableModel < Qt::AbstractTableModel
6
+
7
+ def initialize(cabinets, parent)
8
+ super()
9
+ @cabinets = cabinets
10
+ @view = parent
11
+ @view.setItemDelegateForColumn(1, RadioButtonDelegate.new(self))
12
+ end
13
+
14
+ def refresh
15
+ @cabinets = Cabinet.all
16
+ emit layoutChanged()
17
+ end
18
+
19
+ def rowCount(parent)
20
+ @cabinets.size
21
+ end
22
+
23
+ def columnCount(parent)
24
+ 2
25
+ end
26
+
27
+ def data(index, role = Qt::DisplayRole)
28
+ cabinet = @cabinets[index.row]
29
+ default = Qt::Variant.new
30
+ case role
31
+ when Qt::DisplayRole
32
+ case index.column
33
+ when 0
34
+ cabinet.title.to_v
35
+ when 1
36
+ cabinet.with_computers.to_s.to_v
37
+ else
38
+ raise "invalid column #{index.column}"
39
+ end
40
+ when Qt::EditRole
41
+ case index.column
42
+ when 0
43
+ cabinet.title.to_v
44
+ when 1
45
+ cabinet.with_computers.to_v
46
+ else
47
+ raise "invalid column #{index.column}"
48
+ end
49
+ else
50
+ default
51
+ end
52
+ end
53
+
54
+ def headerData(section, orientation, role = Qt::DisplayRole)
55
+ invalid = Qt::Variant.new
56
+ return invalid unless role == Qt::DisplayRole
57
+ v = case orientation
58
+ when Qt::Horizontal
59
+ %w(Название Компьютерный)[section]
60
+ else
61
+ ''
62
+ end
63
+ Qt::Variant.new(v)
64
+ end
65
+
66
+ def flags(index)
67
+ Qt::ItemIsEditable | super(index)
68
+ end
69
+
70
+ def setData(index, variant, role = Qt::EditRole)
71
+ if index.valid? and role == Qt::EditRole
72
+ cabinet = @cabinets[index.row]
73
+ case index.column
74
+ when 0
75
+ cabinet.title = variant.toString.force_encoding('UTF-8')
76
+ when 1
77
+ cabinet.with_computers = variant.toBool
78
+ else
79
+ raise "invalid column #{index.column}"
80
+ end
81
+ cabinet.save
82
+ emit dataChanged(index, index)
83
+ true
84
+ else
85
+ false
86
+ end
87
+ end
88
+
89
+ def insert_new
90
+ @cabinets.prepend(Cabinet.new)
91
+ emit layoutChanged()
92
+ end
93
+
94
+ def remove_current
95
+ if @view.currentIndex.valid?
96
+ cabinet = @cabinets[@view.currentIndex.row]
97
+ unless cabinet.stub
98
+ cabinet.try(:destroy)
99
+ @cabinets.delete_at(@view.currentIndex.row)
100
+ emit layoutChanged()
101
+ @view.currentIndex = createIndex(-1, -1)
102
+ end
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+ class RadioButtonDelegate < Qt::ItemDelegate
109
+
110
+ def initialize(parent)
111
+ super
112
+ end
113
+
114
+ def createEditor(parent, option, index)
115
+ Qt::CheckBox.new(parent)
116
+ end
117
+
118
+ def setEditorData(editor, index)
119
+ value = index.data.toBool #index.model.data(index, Qt::EditRole)
120
+ button = editor
121
+ button.checked = value # button.setValue(value)
122
+ end
123
+
124
+ def setModelData(editor, model, index)
125
+ button = editor
126
+ value = button.isChecked # button.value
127
+ model.setData(index, value.to_v, Qt::EditRole)
128
+ end
129
+
130
+ def updateEditorGeometry(editor, option, index)
131
+ editor.setGeometry(option.rect)
132
+ end
133
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require_relative '../../engine/models/course'
4
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
5
+ class CourseTableModel < Qt::AbstractTableModel
6
+
7
+ def initialize(courses, parent)
8
+ super()
9
+ @courses = courses
10
+ @view = parent
11
+ end
12
+
13
+ def refresh
14
+ @courses = Course.all
15
+ emit layoutChanged()
16
+ end
17
+
18
+ def rowCount(parent)
19
+ @courses.size
20
+ end
21
+
22
+ def columnCount(parent)
23
+ 1
24
+ end
25
+
26
+ def data(index, role = Qt::DisplayRole)
27
+ invalid = Qt::Variant.new
28
+ return invalid unless role == Qt::DisplayRole or role == Qt::EditRole
29
+ course = @courses[index.row]
30
+ return invalid if course.nil?
31
+ v = case index.column
32
+ when 0
33
+ course.number
34
+ else
35
+ raise "invalid column #{index.column}"
36
+ end || ''
37
+ Qt::Variant.new(v)
38
+ end
39
+
40
+ def headerData(section, orientation, role = Qt::DisplayRole)
41
+ invalid = Qt::Variant.new
42
+ return invalid unless role == Qt::DisplayRole
43
+ v = case orientation
44
+ when Qt::Horizontal
45
+ %w(Номер)[section]
46
+ else
47
+ ''
48
+ end
49
+ Qt::Variant.new(v)
50
+ end
51
+
52
+ def flags(index)
53
+ Qt::ItemIsEditable | super(index)
54
+ end
55
+
56
+ def setData(index, variant, role = Qt::EditRole)
57
+ if index.valid? and role == Qt::EditRole
58
+ course = @courses[index.row]
59
+ case index.column
60
+ when 0
61
+ course.number = variant.toInt
62
+ else
63
+ raise "invalid column #{index.column}"
64
+ end
65
+ course.save
66
+ emit dataChanged(index, index)
67
+ true
68
+ else
69
+ false
70
+ end
71
+ end
72
+
73
+ def insert_new
74
+ @courses.prepend(Course.new)
75
+ emit layoutChanged()
76
+ end
77
+
78
+ def remove_current
79
+ if @view.currentIndex.valid?
80
+ @courses[@view.currentIndex.row].try(:destroy)
81
+ @courses.delete_at(@view.currentIndex.row)
82
+ emit layoutChanged()
83
+ @view.currentIndex = createIndex(-1, -1)
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,190 @@
1
+ # encoding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require_relative '../../engine/models/group'
4
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
5
+ class GroupTableModel < Qt::AbstractTableModel
6
+
7
+ def initialize(groups, parent)
8
+ super()
9
+ @groups = groups
10
+ @groups.size
11
+ @view = parent
12
+ @SpecialityComboBoxDelegate = SpecialityComboBoxDelegate.new(self)
13
+ @CourseComboBoxDelegate = CourseComboBoxDelegate.new(self)
14
+ @view.setItemDelegateForColumn(1, @SpecialityComboBoxDelegate)
15
+ @view.setItemDelegateForColumn(2, @CourseComboBoxDelegate)
16
+ end
17
+
18
+ def refresh
19
+ @groups = Group.all
20
+ @SpecialityComboBoxDelegate.setup
21
+ @CourseComboBoxDelegate.setup
22
+ emit layoutChanged()
23
+ end
24
+
25
+ def rowCount(parent)
26
+ @groups.size
27
+ end
28
+
29
+ def columnCount(parent)
30
+ 4
31
+ end
32
+
33
+ def data(index, role = Qt::DisplayRole)
34
+ group = @groups[index.row]
35
+ default = Qt::Variant.new
36
+ case role
37
+ when Qt::DisplayRole
38
+ case index.column
39
+ when 0
40
+ group.title
41
+ when 1
42
+ group.speciality.try(:title)
43
+ when 2
44
+ group.course.try(:number)
45
+ when 3
46
+ group.emails.map(&:email).join(', ')
47
+ else
48
+ raise "invalid column #{index.column}"
49
+ end.try(:to_v) || default
50
+ when Qt::EditRole
51
+ case index.column
52
+ when 0
53
+ group.title
54
+ when 1
55
+ group.speciality_id
56
+ when 2
57
+ group.course_id
58
+ when 3
59
+ group.emails.map(&:email).join(', ')
60
+ else
61
+ raise "invalid column #{index.column}"
62
+ end.try(:to_v) || default
63
+ else
64
+ default
65
+ end
66
+ end
67
+
68
+ def headerData(section, orientation, role = Qt::DisplayRole)
69
+ invalid = Qt::Variant.new
70
+ return invalid unless role == Qt::DisplayRole
71
+ v = case orientation
72
+ when Qt::Horizontal
73
+ %w(Название Специальность Курс Email)[section]
74
+ else
75
+ ''
76
+ end
77
+ Qt::Variant.new(v)
78
+ end
79
+
80
+ def flags(index)
81
+ Qt::ItemIsEditable | super(index)
82
+ end
83
+
84
+ def setData(index, variant, role = Qt::EditRole)
85
+ if index.valid? and role == Qt::EditRole
86
+ group = @groups[index.row]
87
+ case index.column
88
+ when 0
89
+ group.title = variant.toString.force_encoding('UTF-8')
90
+ when 1
91
+ group.speciality_id = variant.toInt
92
+ when 2
93
+ group.course_id = variant.toInt
94
+ when 3
95
+ emails = variant.toString.force_encoding('UTF-8').split(/,\s*/)
96
+ group.emails.destroy_all
97
+ emails.each do |email|
98
+ group.emails.create(email: email)
99
+ end
100
+ else
101
+ raise "invalid column #{index.column}"
102
+ end
103
+ group.save
104
+ p group
105
+ emit dataChanged(index, index)
106
+ true
107
+ else
108
+ false
109
+ end
110
+ end
111
+
112
+ def insert_new
113
+ new_group = Group.new
114
+ @groups.prepend(new_group)
115
+ (1..2).map{ |i| Subgroup.create(group: new_group, number: i) }
116
+ emit layoutChanged()
117
+ end
118
+
119
+ def remove_current
120
+ if @view.currentIndex.valid?
121
+ @groups[@view.currentIndex.row].try(:destroy)
122
+ @groups.delete_at(@view.currentIndex.row)
123
+ emit layoutChanged()
124
+ @view.currentIndex = createIndex(-1, -1)
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ class SpecialityComboBoxDelegate < Qt::ItemDelegate
131
+ def initialize(parent)
132
+ super
133
+ setup
134
+ end
135
+
136
+ def setup
137
+ @specialities = Speciality.all.sort_by(&:title)
138
+ end
139
+
140
+ def createEditor(parent, option, index)
141
+ editor = Qt::ComboBox.new(parent)
142
+ @specialities.each{ |x| editor.addItem(x.title.to_s, x.id.to_v) }
143
+ editor
144
+ end
145
+
146
+ def setEditorData(editor, index)
147
+ value = index.data
148
+ editor.setCurrentIndex(editor.findData(value))
149
+ end
150
+
151
+ def setModelData(editor, model, index)
152
+ value = editor.itemData(editor.currentIndex)
153
+ model.setData(index, value, Qt::EditRole)
154
+ end
155
+
156
+ def updateEditorGeometry(editor, option, index)
157
+ editor.setGeometry(option.rect)
158
+ end
159
+ end
160
+
161
+ class CourseComboBoxDelegate < Qt::ItemDelegate
162
+ def initialize(parent)
163
+ super
164
+ setup
165
+ end
166
+
167
+ def setup
168
+ @courses = Course.all.sort_by(&:number)
169
+ end
170
+
171
+ def createEditor(parent, option, index)
172
+ editor = Qt::ComboBox.new(parent)
173
+ @courses.each{ |x| editor.addItem(x.number.to_s, x.id.to_v) }
174
+ editor
175
+ end
176
+
177
+ def setEditorData(editor, index)
178
+ value = index.data
179
+ editor.setCurrentIndex(editor.findData(value))
180
+ end
181
+
182
+ def setModelData(editor, model, index)
183
+ value = editor.itemData(editor.currentIndex)
184
+ model.setData(index, value, Qt::EditRole)
185
+ end
186
+
187
+ def updateEditorGeometry(editor, option, index)
188
+ editor.setGeometry(option.rect)
189
+ end
190
+ end
@@ -0,0 +1,111 @@
1
+ # encoding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require_relative '../../engine/models/lecturer'
4
+ require_relative '../../engine/models/email'
5
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
6
+ class LecturerTableModel < Qt::AbstractTableModel
7
+
8
+ def initialize(lecturers, parent)
9
+ super()
10
+ @lecturers = lecturers
11
+ @view = parent
12
+ end
13
+
14
+ def refresh
15
+ @lecturers = Lecturer.all
16
+ emit layoutChanged()
17
+ end
18
+
19
+ def rowCount(parent)
20
+ @lecturers.size
21
+ end
22
+
23
+ def columnCount(parent)
24
+ 5
25
+ end
26
+
27
+ def data(index, role = Qt::DisplayRole)
28
+ invalid = Qt::Variant.new
29
+ return invalid unless role == Qt::DisplayRole or role == Qt::EditRole
30
+ lecturer = @lecturers[index.row]
31
+ return invalid if lecturer.nil?
32
+ v = case index.column
33
+ when 0
34
+ lecturer.surname
35
+ when 1
36
+ lecturer.name
37
+ when 2
38
+ lecturer.patronymic
39
+ when 3
40
+ lecturer.emails.map(&:email).join(', ')
41
+ when 4
42
+ lecturer.preferred_days
43
+ else
44
+ raise "invalid column #{index.column}"
45
+ end || ''
46
+ Qt::Variant.new(v)
47
+ end
48
+
49
+ def headerData(section, orientation, role = Qt::DisplayRole)
50
+ invalid = Qt::Variant.new
51
+ return invalid unless role == Qt::DisplayRole
52
+ v = case orientation
53
+ when Qt::Horizontal
54
+ ['Фамилия', 'Имя', 'Отчество', 'Email', 'Предпочитаемые дни'][section]
55
+ else
56
+ ''
57
+ end
58
+ Qt::Variant.new(v)
59
+ end
60
+
61
+ def flags(index)
62
+ Qt::ItemIsEditable | super(index)
63
+ end
64
+
65
+ def setData(index, variant, role = Qt::EditRole)
66
+ if index.valid? and role == Qt::EditRole
67
+ lecturer = @lecturers[index.row]
68
+ case index.column
69
+ when 0
70
+ lecturer.surname = variant.toString.force_encoding('UTF-8')
71
+ when 1
72
+ lecturer.name = variant.toString.force_encoding('UTF-8')
73
+ when 2
74
+ lecturer.patronymic = variant.toString.force_encoding('UTF-8')
75
+ when 3
76
+ emails = variant.toString.force_encoding('UTF-8').split(/,\s*/)
77
+ lecturer.emails.destroy_all
78
+ emails.each do |email|
79
+ lecturer.emails.create(email: email)
80
+ end
81
+ when 4
82
+ lecturer.preferred_days = variant.toString.force_encoding('UTF-8').split(/,\s*/).select{|x| x[/^[1-7]$/]}.uniq.join(', ')
83
+ else
84
+ raise "invalid column #{index.column}"
85
+ end
86
+ lecturer.save
87
+ emit dataChanged(index, index)
88
+ true
89
+ else
90
+ false
91
+ end
92
+ end
93
+
94
+ def insert_new
95
+ @lecturers.prepend(Lecturer.new)
96
+ emit layoutChanged()
97
+ end
98
+
99
+ def remove_current
100
+ if @view.currentIndex.valid?
101
+ lecturer = @lecturers[@view.currentIndex.row]
102
+ unless lecturer.stub
103
+ lecturer.try(:destroy)
104
+ @lecturers.delete_at(@view.currentIndex.row)
105
+ emit layoutChanged()
106
+ @view.currentIndex = createIndex(-1, -1)
107
+ end
108
+ end
109
+ end
110
+
111
+ end
@@ -0,0 +1,137 @@
1
+ # encoding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require_relative '../../engine/models/semester'
4
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
5
+ class SemesterTableModel < Qt::AbstractTableModel
6
+
7
+ def initialize(semesters, parent)
8
+ super()
9
+ @semesters = semesters
10
+ @view = parent
11
+ @CourseComboBoxDelegate = CourseComboBoxDelegate.new(self)
12
+ @view.setItemDelegateForColumn(1, @CourseComboBoxDelegate)
13
+ end
14
+
15
+ def refresh
16
+ @semesters = Semester.all
17
+ @CourseComboBoxDelegate.setup
18
+ emit layoutChanged()
19
+ end
20
+
21
+ def rowCount(parent)
22
+ @semesters.size
23
+ end
24
+
25
+ def columnCount(parent)
26
+ 2
27
+ end
28
+
29
+ def data(index, role = Qt::DisplayRole)
30
+ semester = @semesters[index.row]
31
+ default = Qt::Variant.new
32
+ case role
33
+ when Qt::DisplayRole
34
+ case index.column
35
+ when 0
36
+ semester.title
37
+ when 1
38
+ semester.course.try(:number)
39
+ else
40
+ raise "invalid column #{index.column}"
41
+ end.try(:to_v) || default
42
+ when Qt::EditRole
43
+ case index.column
44
+ when 0
45
+ semester.title
46
+ when 1
47
+ semester.course_id
48
+ else
49
+ raise "invalid column #{index.column}"
50
+ end.try(:to_v) || default
51
+ else
52
+ default
53
+ end
54
+ end
55
+
56
+ def headerData(section, orientation, role = Qt::DisplayRole)
57
+ invalid = Qt::Variant.new
58
+ return invalid unless role == Qt::DisplayRole
59
+ v = case orientation
60
+ when Qt::Horizontal
61
+ %w(Название Курс)[section]
62
+ else
63
+ ''
64
+ end
65
+ Qt::Variant.new(v)
66
+ end
67
+
68
+ def flags(index)
69
+ Qt::ItemIsEditable | super(index)
70
+ end
71
+
72
+ def setData(index, variant, role = Qt::EditRole)
73
+ if index.valid? and role == Qt::EditRole
74
+ semester = @semesters[index.row]
75
+ case index.column
76
+ when 0
77
+ semester.title = variant.toString.force_encoding('UTF-8')
78
+ when 1
79
+ semester.course_id = variant.toInt
80
+ else
81
+ raise "invalid column #{index.column}"
82
+ end
83
+ semester.save
84
+ p semester
85
+ emit dataChanged(index, index)
86
+ true
87
+ else
88
+ false
89
+ end
90
+ end
91
+
92
+ def insert_new
93
+ @semesters.prepend(Semester.new)
94
+ emit layoutChanged()
95
+ end
96
+
97
+ def remove_current
98
+ if @view.currentIndex.valid?
99
+ @semesters[@view.currentIndex.row].try(:destroy)
100
+ @semesters.delete_at(@view.currentIndex.row)
101
+ emit layoutChanged()
102
+ @view.currentIndex = createIndex(-1, -1)
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+ class CourseComboBoxDelegate < Qt::ItemDelegate
109
+ def initialize(parent)
110
+ super
111
+ setup
112
+ end
113
+
114
+ def setup
115
+ @courses = Course.all.sort_by(&:number)
116
+ end
117
+
118
+ def createEditor(parent, option, index)
119
+ editor = Qt::ComboBox.new(parent)
120
+ @courses.each{ |x| editor.addItem(x.number.to_s, x.id.to_v) }
121
+ editor
122
+ end
123
+
124
+ def setEditorData(editor, index)
125
+ value = index.data
126
+ editor.setCurrentIndex(editor.findData(value))
127
+ end
128
+
129
+ def setModelData(editor, model, index)
130
+ value = editor.itemData(editor.currentIndex)
131
+ model.setData(index, value, Qt::EditRole)
132
+ end
133
+
134
+ def updateEditorGeometry(editor, option, index)
135
+ editor.setGeometry(option.rect)
136
+ end
137
+ end