tmis 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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,933 @@
1
+ # coding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ # Copyright (C) 2013 Vladislav Mileshkin
4
+ #
5
+ # This file is part of TMIS.
6
+ #
7
+ # TMIS is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # TMIS is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with TMIS. If not, see <http://www.gnu.org/licenses/>.
19
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
20
+ require 'Qt'
21
+ require 'mail'
22
+ require 'tmpdir'
23
+ require 'fileutils'
24
+ #require '#Contracts'
25
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
26
+ require_relative '../engine/database'
27
+ require_relative '../engine/verificator'
28
+ require_relative '../engine/import/timetable_manager'
29
+ require_relative '../engine/import/timetable_reader'
30
+ require_relative '../engine/import/spreadsheet_roo'
31
+ require_relative '../engine/export/timetable_exporter.rb'
32
+ require_relative '../engine/mailer/mailer'
33
+ require_relative '../engine/models/cabinet'
34
+ require_relative '../engine/models/course'
35
+ require_relative '../engine/models/group'
36
+ require_relative '../engine/models/lecturer'
37
+ require_relative '../engine/models/semester'
38
+ require_relative '../engine/models/speciality'
39
+ require_relative '../engine/models/speciality_subject'
40
+ require_relative '../engine/models/study'
41
+ require_relative '../engine/models/subject'
42
+ require_relative '../engine/models/subgroup'
43
+ require_relative 'ui_mainwindow'
44
+ require_relative 'forms/about'
45
+ require_relative 'forms/find'
46
+ require_relative 'forms/settings'
47
+ require_relative 'forms/import'
48
+ require_relative 'forms/console'
49
+ require_relative 'forms/debug_console'
50
+ require_relative 'forms/export_general_timetable'
51
+ require_relative 'forms/export_lecturer_timetable'
52
+ require_relative 'forms/export_group_timetable'
53
+ require_relative 'forms/expand_changes'
54
+ require_relative 'models/cabinet_table_model'
55
+ require_relative 'models/course_table_model'
56
+ require_relative 'models/group_table_model'
57
+ require_relative 'models/lecturer_table_model'
58
+ require_relative 'models/semester_table_model'
59
+ require_relative 'models/speciality_table_model'
60
+ require_relative 'models/speciality_subject_table_model'
61
+ require_relative 'models/study_table_model'
62
+ require_relative 'models/subject_table_model'
63
+ require_relative 'models/subgroup_table_model'
64
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
65
+ #class Object
66
+ # def to_v
67
+ # Qt::Variant.new object_id
68
+ # end
69
+ #end
70
+ #
71
+ #class Qt::Variant
72
+ # def to_o
73
+ # ObjectSpace._id2ref to_int
74
+ # end
75
+ #end
76
+
77
+ class Object
78
+ def to_v
79
+ Qt::Variant.new(self)
80
+ end
81
+ end
82
+
83
+ class MainWindow < Qt::MainWindow
84
+
85
+ # File menu
86
+ slots 'on_newAction_triggered()'
87
+ slots 'on_openAction_triggered()'
88
+ slots 'on_saveAction_triggered()'
89
+ slots 'on_saveAsAction_triggered()'
90
+ slots 'on_importAction_triggered()'
91
+ slots 'on_closeAction_triggered()'
92
+ slots 'on_quitAction_triggered()'
93
+ # Tools menu
94
+ slots 'on_verifyLecturersAction_triggered()'
95
+ slots 'on_verifyCabinetsAction_triggered()'
96
+ slots 'on_showLecturerStubsAction_triggered()'
97
+ slots 'on_showCabinetStubsAction_triggered()'
98
+ slots 'on_showSubjectsStubsAction_triggered()'
99
+ slots 'on_verifyComputerCabinetsAction_triggered()'
100
+ slots 'on_verifyPreferredDaysAction_triggered()'
101
+ # Main
102
+ slots 'on_dateDateEdit_dateChanged()'
103
+ # Self
104
+ slots 'open_file()'
105
+ slots 'clear_recent_files()'
106
+ slots 'refreshTableViewModel(QVariant)'
107
+ # help
108
+ slots 'on_showManualAction_triggered()'
109
+ # views buttons
110
+ slots 'on_addCabinetPushButton_clicked()'
111
+ slots 'on_removeCabinetPushButton_clicked()'
112
+ slots 'on_addCoursePushButton_clicked()'
113
+ slots 'on_removeCoursePushButton_clicked()'
114
+ slots 'on_addGroupPushButton_clicked()'
115
+ slots 'on_removeGroupPushButton_clicked()'
116
+ slots 'on_addLecturerPushButton_clicked()'
117
+ slots 'on_removeLecturerPushButton_clicked()'
118
+ slots 'on_addSemesterPushButton_clicked()'
119
+ slots 'on_removeSemesterPushButton_clicked()'
120
+ slots 'on_addSpecialityPushButton_clicked()'
121
+ slots 'on_removeSpecialityPushButton_clicked()'
122
+ slots 'on_addSubgroupPushButton_clicked()'
123
+ slots 'on_removeSubgroupPushButton_clicked()'
124
+ slots 'on_addSubjectPushButton_clicked()'
125
+ slots 'on_removeSubjectPushButton_clicked()'
126
+ slots 'on_addSpecialitySubjectPushButton_clicked()'
127
+ slots 'on_removeSpecialitySubjectPushButton_clicked()'
128
+ #connect(@ui.addSpecialitySubjectPushButton, SLOT('clicked()'), self, SLOT('on_addSpecialitySubjectPushButton_clicked()'))
129
+ #connect(@ui.removeSpecialitySubjectPushButton, SLOT('clicked()'), self, SLOT('on_removeSpecialitySubjectPushButton_clicked()'))
130
+
131
+ slots 'on_tabWidget_currentChanged(int)'
132
+ slots 'on_dataTabWidget_currentChanged(int)'
133
+
134
+ slots 'on_findByLecturerAction_triggered()'
135
+ slots 'on_findBySubjectAction_triggered()'
136
+ slots 'on_findByCabinetAction_triggered()'
137
+
138
+ slots 'on_allAction_triggered()'
139
+ slots 'on_allCoincidenceAction_triggered()'
140
+ slots 'on_allNotAssignedAction_triggered()'
141
+
142
+ slots 'on_tarificationCheckBox_toggled(bool)'
143
+
144
+ slots 'on_debugConsoleAction_triggered()'
145
+
146
+ attr_reader :ui
147
+ attr_reader :study_table_models
148
+
149
+ def initialize(parent = nil)
150
+ super(parent)
151
+ @ui = Ui::MainWindow.new
152
+ @ui.setup_ui self
153
+ @ui.exportMenu.enabled = false
154
+ @study_table_views = [@ui.studiesTableView, @ui.studiesTableView2, @ui.studiesTableView3,
155
+ @ui.studiesTableView4, @ui.studiesTableView5, @ui.studiesTableView6]
156
+ @table_views = [[Cabinet, CabinetTableModel, @ui.cabinetsTableView], [Course, CourseTableModel, @ui.coursesTableView],
157
+ [Group, GroupTableModel, @ui.groupsTableView], [Lecturer, LecturerTableModel, @ui.lecturersTableView],
158
+ [Semester, SemesterTableModel, @ui.semestersTableView], [Speciality, SpecialityTableModel, @ui.specialitiesTableView],
159
+ [SpecialitySubject, SpecialitySubjectTableModel, @ui.specialitySubjectsTableView],
160
+ [Subgroup, SubgroupTableModel, @ui.subgroupsTableView], [Subject, SubjectTableModel, @ui.subjectsTableView]]
161
+ # Следующие два атрибута используются для обхода бага связанного с работой GC
162
+ # http://stackoverflow.com/questions/9715548/cant-display-more-than-one-table-model-inheriting-from-the-same-class-on-differ
163
+ @table_models = @study_table_models = []
164
+ @tables_views_to_hide = @study_table_views + [@ui.cabinetsTableView, @ui.coursesTableView, @ui.groupsTableView,
165
+ @ui.lecturersTableView, @ui.semestersTableView, @ui.specialitySubjectsTableView,
166
+ @ui.specialitiesTableView, @ui.subgroupsTableView, @ui.subjectsTableView, @ui.dateDateEdit,
167
+ @ui.dayLabel, @ui.dayLabel2, @ui.dayLabel3, @ui.dayLabel4, @ui.dayLabel5, @ui.dayLabel6,
168
+ @ui.subjectsListView, @ui.lecturersListView, @ui.cabinetsListView, @ui.tarificationCheckBox,
169
+ @ui.addCabinetPushButton, @ui.addCoursePushButton, @ui.addGroupPushButton,
170
+ @ui.addSubgroupPushButton, @ui.addLecturerPushButton, @ui.addSemesterPushButton,
171
+ @ui.addSpecialityPushButton, @ui.addSpecialitySubjectPushButton, @ui.addSubjectPushButton,
172
+ @ui.removeCabinetPushButton, @ui.removeCoursePushButton, @ui.removeGroupPushButton,
173
+ @ui.removeSubgroupPushButton, @ui.removeLecturerPushButton, @ui.removeSemesterPushButton,
174
+ @ui.removeSpecialityPushButton, @ui.removeSpecialitySubjectPushButton, @ui.removeSubjectPushButton]
175
+ @widgets_to_disable = [@ui.findMenu, @ui.exportMenu, @ui.verifyMenu, @ui.saveAsAction, @ui.expandChangesAction]
176
+ @tables_views_to_hide.each(&:hide)
177
+ @widgets_to_disable.each{ |x| x.enabled = false }
178
+ modeActionGroup = Qt::ActionGroup.new(self)
179
+ modeActionGroup.setExclusive(true)
180
+ modeActionGroup.addAction(@ui.weeklyViewAction)
181
+ modeActionGroup.addAction(@ui.dailyViewAction)
182
+ @temp = ->(){ "#{Dir.mktmpdir('tmis')}/temp.sqlite" }
183
+ connect(@ui.aboutQtAction, SIGNAL('triggered()')){ Qt::Application.aboutQt }
184
+ connect(@ui.aboutProgramAction, SIGNAL('triggered()')){ AboutDialog.new.exec }
185
+ connect(@ui.exportGeneralAction, SIGNAL('triggered()')) do
186
+ ExportGeneralTimetableDialog.new(Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))).exec
187
+ end
188
+ connect(@ui.exportForLecturersAction, SIGNAL('triggered()')) do
189
+ ExportLecturerTimetableDialog.new(Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))).exec
190
+ end
191
+ connect(@ui.exportForGroupsAction, SIGNAL('triggered()')) do
192
+ ExportGroupTimetableDialog.new(Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))).exec
193
+ end
194
+ connect(@ui.settingsAction, SIGNAL('triggered()')){ SettingsDialog.new.exec }
195
+ connect(@ui.expandChangesAction, SIGNAL('triggered()')){ ExpandChangesDialog.new(self).exec }
196
+ @clear_recent_action = Qt::Action.new('Очистить', self)
197
+ @clear_recent_action.setData Qt::Variant.new('clear')
198
+ connect(@clear_recent_action, SIGNAL('triggered()'), self, SLOT('clear_recent_files()'))
199
+ @ui.dateDateEdit.setDate(Qt::Date.fromString(Date.today.to_s, Qt::ISODate))
200
+ setup_dateEdit(Date.today)
201
+ @ui.recentMenu.clear
202
+ @ui.recentMenu.addActions([@clear_recent_action] + Settings[:recent, :files].split.map{ |path| create_recent_action(path) })
203
+ #Settings[:app, :first_run] = ''
204
+ Settings.set_defaults_if_first_run
205
+ @console = ConsoleDialog.new self
206
+ connect(@console, SIGNAL('dialogClosed()')){ @study_table_models.each(&:cancelColoring) }
207
+ $TARIFICATION_MODE = false
208
+ end
209
+
210
+ def on_newAction_triggered
211
+ Database.instance.connect_to(@temp.())
212
+ create_stubs
213
+ Group.create(title: 'New')
214
+ show_tables
215
+ end
216
+
217
+ def create_stubs
218
+ Lecturer.create(surname: Settings[:stubs, :lecturer], stub: true)
219
+ Cabinet.create(title: Settings[:stubs, :cabinet], stub: true)
220
+ Subject.create(title: Settings[:stubs, :subject], stub: true)
221
+ end
222
+
223
+ def on_openAction_triggered
224
+ if (filename = Qt::FileDialog::getOpenFileName(self, 'Open File', '', 'TMIS databases (SQLite3)(*.sqlite)'))
225
+ Database.instance.connect_to filename
226
+ update_recent filename
227
+ show_tables
228
+ end
229
+ end
230
+
231
+ def on_saveAction_triggered
232
+ end
233
+
234
+ def on_saveAsAction_triggered
235
+ if (filename = Qt::FileDialog::getSaveFileName(self, 'Save File', 'NewTimetable.sqlite', 'TMIS databases (SQLite3)(*.sqlite)'))
236
+ filename.force_encoding('UTF-8')
237
+ FileUtils.cp(Database.instance.path, filename) unless Database.instance.path == filename
238
+ Database.instance.connect_to filename
239
+ update_recent filename
240
+ show_tables
241
+ end
242
+ end
243
+
244
+ def on_importAction_triggered
245
+ please_wait do
246
+ if (filename = Qt::FileDialog::getOpenFileName(self, 'Open File', '', 'Spreadsheets(*.xls *.xlsx *.ods *.csv)'))
247
+ if Database.instance.connected?
248
+ (id = ImportDialog.new(Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate)))).exec
249
+ else
250
+ (id = ImportDialog.new(Date.today)).exec
251
+ end
252
+ unless id.params.empty?
253
+ begin
254
+ sheet = SpreadsheetCreater.create filename
255
+ reader = TimetableReader.new(sheet, id.params[:sheet])
256
+ monday = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate)).monday
257
+ if Database.instance.connected?
258
+ Database.instance.transaction do Study.where(date: (monday..(monday + 6))).each(&:delete) end
259
+ else
260
+ Database.instance.connect_to(@temp.())
261
+ create_stubs
262
+ end
263
+ TimetableManager.new(reader, id.params[:date]).save_to_db
264
+ show_tables
265
+ rescue => e
266
+ show_message "При импорте произошли ошибки,\nтаблица не была импортирована.\nПроверьте структуру таблицы."
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
272
+
273
+ def show_message(text)
274
+ box = Qt::MessageBox.new
275
+ box.setText text
276
+ box.exec
277
+ end
278
+
279
+ def on_closeAction_triggered
280
+ @tables_views_to_hide.each(&:hide)
281
+ @widgets_to_disable.each{ |x| x.enabled = false }
282
+ Database.instance.disconnect unless $TESTING
283
+ end
284
+
285
+ def on_quitAction_triggered
286
+ on_closeAction_triggered
287
+ recent = @ui.recentMenu.actions
288
+ Settings[:recent, :files] = recent[1..recent.size-1].map{ |a| a.data.value.to_s }.join(' ')
289
+ puts 'Sayonara!'
290
+ Qt::Application.quit
291
+ end
292
+
293
+ def on_allAction_triggered
294
+ begin
295
+ @console.browser.clear
296
+ @console.show
297
+ @console.browser.append verifyLecturers
298
+ @console.browser.append verifyCabinets
299
+ @console.browser.append showComputerCabinets
300
+ @console.browser.append showLecturerStubs
301
+ @console.browser.append showCabinetStubs
302
+ @console.browser.append showSubjectsStubs
303
+ @console.browser.append showPreferredDays
304
+ rescue
305
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
306
+ end
307
+ end
308
+
309
+ def on_allCoincidenceAction_triggered
310
+ begin
311
+ @console.browser.clear
312
+ @console.show
313
+ @console.browser.append verifyLecturers
314
+ @console.browser.append verifyCabinets
315
+ rescue
316
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
317
+ end
318
+ end
319
+
320
+ def on_allNotAssignedAction_triggered
321
+ begin
322
+ @console.browser.clear
323
+ @console.show
324
+ @console.browser.append showLecturerStubs
325
+ @console.browser.append showCabinetStubs
326
+ @console.browser.append showSubjectsStubs
327
+ rescue
328
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
329
+ end
330
+ end
331
+
332
+ def verifyLecturers
333
+ date = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))
334
+ dates = date.monday..date.monday + 6
335
+ v = Verificator.new(dates)
336
+ res = v.verify(:lecturer_studies).map do |k, v|
337
+ date = k[0]
338
+ lecturer = Lecturer.where(id: k[1]).first
339
+ number = k[2]
340
+ if lecturer.stub
341
+ nil
342
+ else
343
+ v.each do |study|
344
+ tst = @study_table_models[date.cwday - 1]
345
+ tst.setColor(study.id, Qt::red)
346
+ end
347
+ "#{date} | #{lecturer} ведёт несколько пар одновременно! Номер пары: #{number}"
348
+ end
349
+ end
350
+ res = res.compact.join("\n")
351
+ end
352
+
353
+ def on_verifyLecturersAction_triggered
354
+ begin
355
+ @console.browser.clear
356
+ @console.show
357
+ @console.browser.append verifyLecturers
358
+ rescue
359
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
360
+ end
361
+ end
362
+
363
+ def verifyCabinets
364
+ date = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))
365
+ dates = date.monday..date.monday + 6
366
+ v = Verificator.new(dates)
367
+ res = v.verify(:cabinet_studies).map do |k, v|
368
+ date = k[0]
369
+ cabinet= Cabinet.where(id: k[1]).first
370
+ number = k[2]
371
+ if cabinet.stub
372
+ nil
373
+ else
374
+ v.each{ |study| @study_table_models[date.cwday - 1].setColorCabinet(study.id, Qt::blue) }
375
+ "#{date} | В #{cabinet.title} проходит несколько пар одновременно! Номер пары: #{number}"
376
+ end
377
+ end
378
+ res = res.compact.join("\n")
379
+ end
380
+
381
+ def on_verifyCabinetsAction_triggered
382
+ begin
383
+ @console.browser.clear
384
+ @console.show
385
+ @console.browser.append verifyCabinets
386
+ rescue
387
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
388
+ end
389
+ end
390
+
391
+ def showLecturerStubs
392
+ date = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))
393
+ dates = date.monday..date.monday + 6
394
+ v = Verificator.new(dates)
395
+ res = v.verify(:lecturer_stubs).map do |date, studies|
396
+ studies.map do |study|
397
+ @study_table_models[date.cwday - 1].setColor(study.id, Qt::green)
398
+ "#{date} | Не назначен преподаватель! Группа: #{study.get_group.title} Номер пары: #{study.number}"
399
+ end.join("\n")
400
+ end
401
+ res = res.compact.join("\n")
402
+ end
403
+
404
+ def on_showLecturerStubsAction_triggered
405
+ begin
406
+ @console.browser.clear
407
+ @console.show
408
+ @console.browser.append showLecturerStubs
409
+ rescue
410
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
411
+ end
412
+ end
413
+
414
+ def showCabinetStubs
415
+ date = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))
416
+ dates = date.monday..date.monday + 6
417
+ v = Verificator.new(dates)
418
+ res = v.verify(:cabinet_stubs).map do |date, studies|
419
+ studies.map do |study|
420
+ @study_table_models[date.cwday - 1].setColorCabinet(study.id, Qt::green)
421
+ "#{date} | Не назначен кабинет! Группа: #{study.get_group.title} Номер пары: #{study.number}"
422
+ end.join("\n")
423
+ end
424
+ res = res.compact.join("\n")
425
+ end
426
+
427
+ def on_showCabinetStubsAction_triggered
428
+ begin
429
+ @console.browser.clear
430
+ @console.show
431
+ @console.browser.append showCabinetStubs
432
+ rescue
433
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
434
+ end
435
+ end
436
+
437
+ def showSubjectsStubs
438
+ date = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))
439
+ dates = date.monday..date.monday + 6
440
+ v = Verificator.new(dates)
441
+ res = v.verify(:subject_stubs).map do |date, studies|
442
+ studies.map do |study|
443
+ @study_table_models[date.cwday - 1].setColor(study.id, Qt::green)
444
+ "#{date} | Не назначен предмет! Группа: #{study.get_group.title} Номер пары: #{study.number}"
445
+ end.join("\n")
446
+ end
447
+ res = res.compact.join("\n")
448
+ end
449
+
450
+ def on_showSubjectsStubsAction_triggered
451
+ begin
452
+ @console.browser.clear
453
+ @console.show
454
+ @console.browser.append showSubjectsStubs
455
+ rescue
456
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
457
+ end
458
+ end
459
+
460
+ def showComputerCabinets
461
+ date = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))
462
+ dates = date.monday..date.monday + 6
463
+ v = Verificator.new(dates)
464
+ res = v.verify(:computer_cabinets).map do |date, studies|
465
+ studies.map do |study|
466
+ @study_table_models[date.cwday - 1].setColorCabinet(study.id, Qt::yellow)
467
+ "#{date} | Занятие подгруппы проходит не в компьютерном кабинете! Группа: #{study.get_group.title} Номер пары: #{study.number}"
468
+ end.join("\n")
469
+ end
470
+ res = res.compact.join("\n")
471
+ end
472
+
473
+ def on_verifyComputerCabinetsAction_triggered
474
+ begin
475
+ @console.browser.clear
476
+ @console.show
477
+ @console.browser.append showComputerCabinets
478
+ rescue
479
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
480
+ end
481
+ end
482
+
483
+ def showPreferredDays
484
+ date = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))
485
+ dates = date.monday..date.monday + 6
486
+ v = Verificator.new(dates)
487
+ res = v.verify(:preferred_days).map do |date, studies|
488
+ studies.map do |study|
489
+ @study_table_models[date.cwday - 1].setColorCabinet(study.id, Qt::yellow)
490
+ "#{date} | #{study.lecturer.to_s} предпочитает вести занятия в другой день! Группа: #{study.get_group.title} Номер пары: #{study.number}"
491
+ end.join("\n")
492
+ end
493
+ res = res.compact.join("\n")
494
+ end
495
+
496
+ def on_verifyPreferredDaysAction_triggered
497
+ begin
498
+ @console.browser.clear
499
+ @console.show
500
+ @console.browser.append showPreferredDays
501
+ rescue
502
+ show_message "При проверке произошли ошибки.\nПроверьте таблицы данных и расписание"
503
+ end
504
+ end
505
+
506
+ #def groupAndSubgroup
507
+ # date = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate))
508
+ # dates = date.monday..date.monday + 6
509
+ # v = Verificator.new(dates)
510
+ # res = v.verify(:group_and_subgroup).map do |k, v|
511
+ # date = k[0]
512
+ # lecturer = Lecturer.where(id: k[1]).first
513
+ # number = k[2]
514
+ # if lecturer.stub
515
+ # nil
516
+ # else
517
+ # v.each{ |study| @study_table_models[date.cwday - 1].setColor(study.id Qt::red) }
518
+ # "#{date} | #{lecturer} ведёт несколько пар одновременно! Номер пары: #{number}"
519
+ # end
520
+ # end
521
+ #end
522
+ #
523
+ #def on_verifyGroupAndSubgroup
524
+ # @console.browser.clear
525
+ # @console.show
526
+ # @console.browser.append groupAndSubgroup
527
+ #end
528
+
529
+ class EntityItemModel < Qt::AbstractItemModel
530
+ def initialize(lambda, parent = nil)
531
+ super(parent)
532
+ @get_entities = lambda
533
+ @entities = @get_entities.()
534
+ @size = @entities.size
535
+ end
536
+
537
+ def refresh
538
+ @entities = @get_entities.()
539
+ @size = @entities.size
540
+ emit layoutChanged()
541
+ end
542
+
543
+ def index(row, column, parent)
544
+ createIndex(row, column)
545
+ end
546
+
547
+ def parent(index)
548
+ Qt::ModelIndex.new()
549
+ end
550
+
551
+ def columnCount(parent = self)
552
+ 1
553
+ end
554
+
555
+ def rowCount(parent = self)
556
+ @size
557
+ end
558
+
559
+ def data(index, role = Qt::DisplayRole, data = nil)
560
+ if role == Qt::DisplayRole && index.valid?
561
+ @entities[index.row].to_s.to_v
562
+ else
563
+ Qt::Variant.new
564
+ end
565
+ end
566
+
567
+ def flags(index)
568
+ if index.valid?
569
+ Qt::ItemIsDragEnabled | super(index)
570
+ else
571
+ super(index)
572
+ end
573
+ end
574
+
575
+ def mimeData(indexes)
576
+ entity = @entities[indexes.first.row]
577
+ ba = Qt::ByteArray.new entity.id.to_s # Marshal.dump entity?
578
+ mime_type = "application/#{entity.class.to_s.downcase}"
579
+ mime_data = super indexes # для обхода ошибки сегментации Qt::MimeData создаётся с помощью родительского метода
580
+ mime_data.setData(mime_type, ba)
581
+ mime_data
582
+ end
583
+ end
584
+
585
+ def show_tables
586
+ @table_models = @table_views.map do |entity, table_model, table_view|
587
+ model = table_model.new(entity.all, table_view)
588
+ proxy_model = model
589
+ setup_table_view(table_view, proxy_model, Qt::HeaderView::Stretch)
590
+ model
591
+ end
592
+ setup_study_table_views
593
+ @ui.dateDateEdit.show
594
+ @tables_views_to_hide.each(&:show)
595
+ @widgets_to_disable.each{ |x| x.enabled = true }
596
+ #@ui.studiesTableView.setSpan(0, 0, 1, 3)
597
+ model = EntityItemModel.new(->(){ Subject.all }, self)
598
+ @ui.subjectsListView.setModel model
599
+ @ui.subjectsListView.show
600
+ model = EntityItemModel.new(->(){ Lecturer.all }, self)
601
+ @ui.lecturersListView.setModel model
602
+ @ui.lecturersListView.show
603
+ model = EntityItemModel.new(->(){ Cabinet.all }, self)
604
+ @ui.cabinetsListView.setModel model
605
+ @ui.cabinetsListView.show
606
+ end
607
+
608
+ def on_tarificationCheckBox_toggled(checked)
609
+ if checked
610
+ $TARIFICATION_MODE = true
611
+ model = EntityItemModel.new(->(){ [] }, self)
612
+ @ui.subjectsListView.setModel model
613
+ @ui.subjectsListView.show
614
+ model = EntityItemModel.new(->(){ [] }, self)
615
+ @ui.lecturersListView.setModel model
616
+ @ui.lecturersListView.show
617
+ else
618
+ $TARIFICATION_MODE = false
619
+ model = EntityItemModel.new(->(){ Subject.all }, self)
620
+ @ui.subjectsListView.setModel model
621
+ @ui.subjectsListView.show
622
+ model = EntityItemModel.new(->(){ Lecturer.all }, self)
623
+ @ui.lecturersListView.setModel model
624
+ @ui.lecturersListView.show
625
+ end
626
+ end
627
+
628
+ def setupListViews(index)
629
+ return false unless $TARIFICATION_MODE
630
+ string = index.model.data(index, Qt::UserRole).toString
631
+ if string.nil? || string.empty?
632
+ model = EntityItemModel.new(->(){ [] }, self)
633
+ @ui.subjectsListView.setModel model
634
+ @ui.subjectsListView.show
635
+ model = EntityItemModel.new(->(){ [] }, self)
636
+ @ui.lecturersListView.setModel model
637
+ @ui.lecturersListView.show
638
+ else
639
+ entity = Marshal.load(Base64.decode64(string))
640
+ p entity
641
+ if entity.class == Study
642
+ group = entity.groupable.get_group
643
+ course = group.course
644
+ if course.nil?
645
+ model = EntityItemModel.new(->(){ [] }, self)
646
+ @ui.subjectsListView.setModel model
647
+ @ui.subjectsListView.show
648
+ model = EntityItemModel.new(->(){ [] }, self)
649
+ @ui.lecturersListView.setModel model
650
+ @ui.lecturersListView.show
651
+ else
652
+ semester = course.current_semester
653
+ get_subjects = ->() do
654
+ if entity.lecturer && entity.subject.stub
655
+ SpecialitySubject.where(lecturer_id: entity.lecturer, speciality_id: group.speciality, semester_id: semester).map(&:subject)
656
+ else
657
+ SpecialitySubject.where(speciality_id: group.speciality, semester_id: semester).map(&:subject)
658
+ end
659
+ end
660
+ get_lecturers = ->() do
661
+ if entity.subject && entity.lecturer.stub
662
+ SpecialitySubject.where(subject_id: entity.subject, speciality_id: group.speciality, semester_id: semester).map(&:lecturer)
663
+ else
664
+ SpecialitySubject.where(speciality_id: group.speciality, semester_id: semester).map(&:lecturer)
665
+ end
666
+ end
667
+ model = EntityItemModel.new(get_subjects, self)
668
+ @ui.subjectsListView.setModel model
669
+ @ui.subjectsListView.show
670
+ model = EntityItemModel.new(get_lecturers, self)
671
+ @ui.lecturersListView.setModel model
672
+ @ui.lecturersListView.show
673
+ end
674
+ elsif entity.class == Group
675
+ group = entity
676
+ course = group.course
677
+ if course.nil?
678
+ model = EntityItemModel.new(->(){ [] }, self)
679
+ @ui.subjectsListView.setModel model
680
+ @ui.subjectsListView.show
681
+ model = EntityItemModel.new(->(){ [] }, self)
682
+ @ui.lecturersListView.setModel model
683
+ @ui.lecturersListView.show
684
+ else
685
+ semester = course.current_semester
686
+ get_subjects = ->() do
687
+ SpecialitySubject.where(speciality_id: group.speciality, semester_id: semester).map(&:subject)
688
+ end
689
+ get_lecturers = ->() do
690
+ SpecialitySubject.where(speciality_id: group.speciality, semester_id: semester).map(&:lecturer)
691
+ end
692
+ model = EntityItemModel.new(get_subjects, self)
693
+ @ui.subjectsListView.setModel model
694
+ @ui.subjectsListView.show
695
+ model = EntityItemModel.new(get_lecturers, self)
696
+ @ui.lecturersListView.setModel model
697
+ @ui.lecturersListView.show
698
+ end
699
+ end
700
+ end
701
+ end
702
+
703
+ def filterListViews(study64)
704
+ if study.subject
705
+
706
+ end
707
+ if study.lecturer
708
+
709
+ end
710
+ end
711
+
712
+ def setup_study_table_views
713
+ @ui.deleteAction.disconnect(SIGNAL('triggered()'))
714
+ monday = Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate)).monday
715
+ @study_table_models = @study_table_views.each_with_index.map do |view, index|
716
+ model = setup_study_table_view(view, monday + index)
717
+ model.disconnect(SIGNAL('studySaved(QVariant)'))
718
+ view.disconnect(SIGNAL('doubleClicked(QModelIndex)'))
719
+ view.disconnect(SIGNAL('customContextMenuRequested(QPoint)'))
720
+ view.disconnect(SIGNAL('clicked(QModelIndex)'))
721
+ model.disconnect(SIGNAL('refreshTarification(QModelIndex)'))
722
+ connect(view, SIGNAL('customContextMenuRequested(QPoint)'), model, SLOT('displayMenu(QPoint)'))
723
+ connect(view, SIGNAL('clicked(QModelIndex)')){ |index| setupListViews(index) }
724
+ connect(model, SIGNAL('refreshTarification(QModelIndex)')){ |index| setupListViews(index) }
725
+ connect(view, SIGNAL('doubleClicked(QModelIndex)'), model, SLOT('editStudy(QModelIndex)'))
726
+ #connect(model, SIGNAL('studySaved(QString)')){|study64| filterListViews(study64) }
727
+ connect(model, SIGNAL('studySaved(QVariant)'), self, SLOT('refreshTableViewModel(QVariant)'))
728
+ connect(@ui.deleteAction, SIGNAL('triggered()'), model, SLOT('removeData()'))
729
+ connect(@ui.cancelVerifyingAction, SIGNAL('triggered()'), model, SLOT('cancelColoring()'))
730
+ view.setContextMenuPolicy(Qt::CustomContextMenu)
731
+ model
732
+ end
733
+ end
734
+
735
+ def refreshTableViewModel(date_variant)
736
+ @study_table_models[date_variant.value.dayOfWeek - 1].refresh
737
+ end
738
+
739
+ def setup_study_table_view(view, date)
740
+ model = StudyTableModel.new(date, view)
741
+ view = setup_table_view2(view, model, Qt::HeaderView::Interactive)
742
+ model.columnCount.times{ |i| i.odd? ? view.setColumnWidth(i, 50) : view.setColumnWidth(i, 150) }
743
+ model.rowCount.times{ |i| view.setRowHeight(i, 50) }
744
+ model
745
+ end
746
+
747
+ def setup_table_view2(table_view, table_model, resize_mode)
748
+ table_view.setModel(table_model)
749
+ table_view.horizontalHeader.setResizeMode(resize_mode)
750
+ table_view.verticalHeader.setResizeMode(resize_mode)
751
+ table_view.show
752
+ table_view
753
+ end
754
+
755
+ ##Contract IsA[Qt::TableView], IsA[Qt::AbstractTableModel], IsA[Qt::Enum] => IsA[Qt::TableView]
756
+ def setup_table_view(table_view, table_model, resize_mode)
757
+ table_view.setModel(table_model)
758
+ table_view.horizontalHeader.setResizeMode(resize_mode)
759
+ table_view.verticalHeader.setResizeMode(Qt::HeaderView::ResizeToContents)
760
+ table_view.show
761
+ table_view
762
+ end
763
+
764
+ def open_file
765
+ filename = sender.data.value.to_s
766
+ if File.exist? filename
767
+ Database.instance.connect_to filename
768
+ update_recent filename
769
+ show_tables
770
+ end
771
+ end
772
+
773
+ #Contract String => Qt::Action
774
+ def create_recent_action(path)
775
+ action = Qt::Action.new(path[path.size-10..path.size], self)
776
+ connect(action, SIGNAL('triggered()'), self, SLOT('open_file()'))
777
+ action.setData Qt::Variant.new(path); action
778
+ end
779
+
780
+ #Contract String => Any
781
+ def update_recent(filename)
782
+ actions = @ui.recentMenu.actions
783
+ if actions.size > 5
784
+ @ui.recentMenu.clear
785
+ @ui.recentMenu.addActions([@clear_recent_action] + actions[1..actions.size-1])
786
+ else
787
+ @ui.recentMenu.addAction create_recent_action(filename)
788
+ end
789
+ end
790
+
791
+ def clear_recent_files
792
+ @ui.recentMenu.clear
793
+ @ui.recentMenu.addAction @clear_recent_action
794
+ end
795
+
796
+ def on_dateDateEdit_dateChanged
797
+ setup_dateEdit(Date.parse(@ui.dateDateEdit.date.toString(Qt::ISODate)))
798
+ setup_study_table_views if Database.instance.connected?
799
+ end
800
+
801
+ def setup_dateEdit(date)
802
+ type = date.cweek.even? ? "Чётная" : "Нечётная"
803
+ @ui.dateDateEdit.displayFormat = "Неделя №#{date.cweek} (#{type}) dddd - d MMMM yy"
804
+ end
805
+
806
+ def please_wait(&block)
807
+ @ui.statusbar.showMessage 'Please, wait...'
808
+ yield block
809
+ @ui.statusbar.clearMessage
810
+ end
811
+
812
+ def on_showManualAction_triggered
813
+ # binding doesn't include QHelpEngine
814
+ #helpEngine Qt::HelpEngineCore('test.qhc')
815
+ #links = helpEngine.linksForIdentifier('MyDialog::ChangeButton')
816
+ #if links.count
817
+ # helpData = helpEngine.fileData links.constBegin.value
818
+ # if !helpData.isEmpty
819
+ # displayHelp helpData
820
+ # end
821
+ #end
822
+ Qt::DesktopServices::openUrl(Qt::Url.new('https://github.com/Noein/TMIS/wiki'))
823
+ end
824
+
825
+ def on_addCabinetPushButton_clicked
826
+ @ui.cabinetsTableView.model.insert_new
827
+ end
828
+
829
+ def on_removeCabinetPushButton_clicked
830
+ @ui.cabinetsTableView.model.remove_current
831
+ end
832
+
833
+ def on_addCoursePushButton_clicked
834
+ @ui.coursesTableView.model.insert_new
835
+ end
836
+
837
+ def on_removeCoursePushButton_clicked
838
+ @ui.coursesTableView.model.remove_current
839
+ end
840
+
841
+ def on_addGroupPushButton_clicked
842
+ @ui.groupsTableView.model.insert_new
843
+ end
844
+
845
+ def on_removeGroupPushButton_clicked
846
+ @ui.groupsTableView.model.remove_current
847
+ end
848
+
849
+ def on_addLecturerPushButton_clicked
850
+ @ui.lecturersTableView.model.insert_new
851
+ end
852
+
853
+ def on_removeLecturerPushButton_clicked
854
+ @ui.lecturersTableView.model.remove_current
855
+ end
856
+
857
+ def on_addSemesterPushButton_clicked
858
+ @ui.semestersTableView.model.insert_new
859
+ end
860
+
861
+ def on_removeSemesterPushButton_clicked
862
+ @ui.semestersTableView.model.remove_current
863
+ end
864
+
865
+ def on_addSpecialitySubjectPushButton_clicked
866
+ @ui.specialitySubjectsTableView.model.insert_new
867
+ end
868
+
869
+ def on_removeSpecialitySubjectPushButton_clicked
870
+ @ui.specialitySubjectsTableView.model.remove_current
871
+ end
872
+
873
+ def on_addSpecialityPushButton_clicked
874
+ @ui.specialitiesTableView.model.insert_new
875
+ end
876
+
877
+ def on_removeSpecialityPushButton_clicked
878
+ @ui.specialitiesTableView.model.remove_current
879
+ end
880
+
881
+ def on_addSubgroupPushButton_clicked
882
+ @ui.subgroupsTableView.model.insert_new
883
+ end
884
+
885
+ def on_removeSubgroupPushButton_clicked
886
+ @ui.subgroupsTableView.model.remove_current
887
+ end
888
+
889
+ def on_addSubjectPushButton_clicked
890
+ @ui.subjectsTableView.model.insert_new
891
+ end
892
+
893
+ def on_removeSubjectPushButton_clicked
894
+ @ui.subjectsTableView.model.remove_current
895
+ end
896
+
897
+ def on_dataTabWidget_currentChanged(index)
898
+ if Database.instance.connected?
899
+ @table_views.each do |c, m, view|
900
+ model = view.model
901
+ model.refresh
902
+ proxy_model = model
903
+ view.model = proxy_model
904
+ end
905
+ end
906
+ end
907
+
908
+ def on_tabWidget_currentChanged(index)
909
+ if Database.instance.connected?
910
+ if index == 0
911
+ @study_table_models.each(&:refresh)
912
+ [@ui.subjectsListView, @ui.lecturersListView, @ui.cabinetsListView].each{|view| view.model.refresh }
913
+ end
914
+ end
915
+ end
916
+
917
+ def on_findByLecturerAction_triggered
918
+ FindDialog.new(:lecturer, self).show
919
+ end
920
+
921
+ def on_findBySubjectAction_triggered
922
+ FindDialog.new(:subject, self).show
923
+ end
924
+
925
+ def on_findByCabinetAction_triggered
926
+ FindDialog.new(:cabinet, self).show
927
+ end
928
+
929
+ def on_debugConsoleAction_triggered
930
+ DebugConsoleDialog.new(self).show
931
+ end
932
+
933
+ end