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,136 @@
1
+ # coding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require 'roo'
4
+ require 'fileutils'
5
+ require 'spreadsheet'
6
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7
+ #require '#Contracts'
8
+ require_relative 'abstract_spreadsheet'
9
+ #include #Contracts
10
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11
+ class SpreadsheetRoo < AbstractSpreadsheet
12
+
13
+ attr_reader :filepath
14
+
15
+ ##Contract String => Any
16
+ def initialize(filepath)
17
+ @filepath = filepath
18
+ @sheet = Roo::Spreadsheet.open(filepath)
19
+ @sheet.default_sheet = 0
20
+ end
21
+
22
+ #Contract None => Pos
23
+ def last_row
24
+ @sheet.last_row
25
+ end
26
+
27
+ #Contract None => Pos
28
+ def last_column
29
+ @sheet.last_column
30
+ end
31
+
32
+ #Contract Not[Neg] => Any
33
+ def sheet(number)
34
+ @sheet.default_sheet = @sheet.sheets[number-1]
35
+ end
36
+
37
+ #Contract Pos => Any
38
+ def row(n)
39
+ @sheet.row(n)
40
+ end
41
+
42
+ #Contract Pos => Any
43
+ def column(n)
44
+ @sheet.column(n)
45
+ end
46
+
47
+ #Contract Pos, Pos => Any
48
+ def [](r, c)
49
+ @sheet.cell(r, c)
50
+ end
51
+ end
52
+
53
+ class SpreadsheetSpreadsheet < AbstractSpreadsheet
54
+ include WritableSpreadsheet
55
+
56
+ attr_reader :filepath
57
+
58
+ #Contract String => Any
59
+ def initialize(filepath)
60
+ @filepath = filepath
61
+ Spreadsheet.client_encoding = 'UTF-8'
62
+ if File.file?(@filepath)
63
+ @book = Spreadsheet.open(@filepath)
64
+ else
65
+ @book = Spreadsheet::Workbook.new(@filepath)
66
+ @book.create_worksheet
67
+ end
68
+ @sheet = @book.worksheet(0)
69
+ fmt = Spreadsheet::Format.new text_wrap: true
70
+ fmt.horizontal_align = :center
71
+ fmt.vertical_align = :middle
72
+ fmt.font = Spreadsheet::Font.new('Times New Roman', :size => 12)
73
+ @sheet.default_format = fmt
74
+ @sheet
75
+ end
76
+
77
+ #Contract None => Pos
78
+ def last_row
79
+ @sheet.last_row_index
80
+ end
81
+
82
+ #Contract None => Pos
83
+ def last_column
84
+ @sheet.column_count
85
+ end
86
+
87
+ #Contract Not[Neg] => Any
88
+ def sheet(number)
89
+ @sheet = @book.worksheet(number - 1)
90
+ end
91
+
92
+ #Contract Pos => Any
93
+ def row(n)
94
+ @sheet.row(n - 1)
95
+ end
96
+
97
+ #Contract Pos => Any
98
+ def column(n)
99
+ @sheet.column(n - 1)
100
+ end
101
+
102
+ #Contract Pos, Pos => Any
103
+ def [](r, c)
104
+ @sheet[r - 1, c - 1]
105
+ end
106
+
107
+ #Contract Pos, Pos, Any => Any
108
+ def []=(r, c, obj)
109
+ @sheet[r - 1, c - 1] = obj
110
+ end
111
+
112
+ #Contract None => Any
113
+ def save
114
+ @book.write("#{@filepath}_temp")
115
+ FileUtils.mv("#{@filepath}_temp", @filepath) # Обход бага в библиотеке
116
+ end
117
+
118
+ def merge(start_row, start_col, end_row, end_col)
119
+ @sheet.merge_cells(start_row - 1, start_col - 1, end_row - 1, end_col - 1)
120
+ end
121
+
122
+ def format(r, c, fmt)
123
+ @sheet.row(r - 1).set_format(c - 1, fmt)
124
+ end
125
+ end
126
+
127
+ class SpreadsheetCreater
128
+ #Contract String => Or[IsA[AbstractSpreadsheet], IsA[WritableSpreadsheet]]
129
+ def self.create(filename)
130
+ if filename =~ /.*.csv/
131
+ SpreadsheetRoo.new(File.expand_path(filename))
132
+ else
133
+ SpreadsheetSpreadsheet.new(File.expand_path(filename))
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,110 @@
1
+ # coding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require 'active_record'
4
+ require 'date'
5
+ require_relative '../models/group'
6
+ require_relative '../models/subgroup'
7
+ require_relative '../models/subject'
8
+ require_relative '../models/lecturer'
9
+ require_relative '../models/cabinet'
10
+ require_relative '../models/study'
11
+ require_relative '../models/course'
12
+ require_relative '../models/semester'
13
+ require_relative 'timetable_reader'
14
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
15
+ require 'contracts'
16
+ require_relative 'abstract_spreadsheet'
17
+ #include Contracts
18
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
19
+ class TimetableManager
20
+ #Contract IsA[TimetableReader], Any => Any
21
+ def initialize(timetable_reader, date)
22
+ @reader = timetable_reader
23
+ @days = {'понедельник' => date, 'вторник' => date + 1, 'среда' => date + 2, 'четверг' => date + 3, 'пятница' => date + 4, 'суббота' => date + 5 }
24
+ end
25
+
26
+ #Contract None => Any
27
+ def save_to_db
28
+ Database.instance.transaction do
29
+ create_subgroups
30
+ (1..4).each{ |n| add(Course, number: n) }
31
+ Course.all.zip(Course.all).flatten.each_with_index { |c, i| add(Semester, {title: i + 1, course_id: c}) }
32
+ end
33
+ end
34
+
35
+ private
36
+ #Contract None => Any
37
+ def create_subgroups
38
+ @reader.groups.each do |data|
39
+ group = add(Group, title: data[:title] || "")
40
+ subgroups = (1..2).map{ |i| add(Subgroup, {group_id: group, number: i}) }
41
+ create_studies(data, group, subgroups)
42
+ end
43
+ end
44
+
45
+ #Contract Hash, Group, ArrayOf[Subgroup] => Any
46
+ def create_studies(data, group, subgroups)
47
+ data[:days].each do |day|
48
+ day[:studies].each_with_index do |study, number|
49
+ if study.size == 1
50
+ if (s = study.first[:info][:subgroup])
51
+ s = s.to_i
52
+ if s > 0 && s < 3
53
+ Study.create( get_study_options(study[0], day[:name], number.succ, subgroups[s.to_i - 1]) )
54
+ else
55
+ next
56
+ end
57
+ else
58
+ Study.create( get_study_options(study[0], day[:name], number.succ, group) )
59
+ end
60
+ else
61
+ study.each do |sepstudy|
62
+ Study.create( get_study_options(sepstudy, day[:name], number.succ,
63
+ sepstudy[:info][:subgroup] ? subgroups[sepstudy[:info][:subgroup].to_i - 1] : group) )
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ #Contract IsA[Class], Hash => IsA[ActiveRecord::Base]
71
+ def add(model, options)
72
+ model.where(options).first_or_create
73
+ end
74
+
75
+ #Contract Hash, String, Pos, Or[Group, Subgroup] => Hash
76
+ def get_study_options(study, day, study_number, groupable)
77
+ { subject: add(Subject, title: study[:info][:subject]),
78
+ cabinet: new_cabinet_or_stub(study),
79
+ lecturer: new_lecturer_or_stub(study),
80
+ date: @days[day.mb_chars.downcase.to_s.gsub(' ', '')],
81
+ number: study_number,
82
+ groupable: groupable }
83
+ end
84
+
85
+ def new_lecturer_or_stub(study)
86
+ case study[:info][:lecturer][:surname]
87
+ when nil
88
+ Lecturer.where(stub: true).first
89
+ when /#{Settings[:stubs, :lecturer]}/i
90
+ Lecturer.where(stub: true).first
91
+ else
92
+ add(Lecturer, { surname: study[:info][:lecturer][:surname],
93
+ name: study[:info][:lecturer][:name],
94
+ patronymic: study[:info][:lecturer][:patronymic] })
95
+ end
96
+ end
97
+
98
+ def new_cabinet_or_stub(study)
99
+ if study[:cabinet]
100
+ add(Cabinet, title: fix_cabinet(study[:cabinet]))
101
+ else
102
+ Cabinet.where(stub: true).first
103
+ end
104
+ end
105
+
106
+ #Contract Or[Num, String] => String
107
+ def fix_cabinet(title)
108
+ title.is_a?(Float) ? title.ceil.to_s : title.to_s
109
+ end
110
+ end
@@ -0,0 +1,79 @@
1
+ # coding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require 'contracts'
4
+ require_relative 'abstract_spreadsheet'
5
+ require_relative '../../interface/forms/settings'
6
+ #include Contracts
7
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8
+ class TimetableReader
9
+ #Contract IsA[AbstractSpreadsheet], Pos => Any
10
+ def initialize(spreadsheet, sheet_number=1)
11
+ @table = spreadsheet
12
+ week(sheet_number)
13
+ end
14
+
15
+ #Contract Pos => TimetableReader
16
+ def week(num)
17
+ @table.sheet(num); self
18
+ end
19
+
20
+ #Contract None => Array
21
+ def groups
22
+ (3..@table.last_column).each_slice(2).map do |cols|
23
+ { title: @table[7, cols.first], days: get_days(cols) }
24
+ end
25
+ end
26
+
27
+ private
28
+ #Contract [Pos, Pos] => Array
29
+ def get_days(cols)
30
+ (7..84).each_slice(13).map{ |i| [i[1], i.last] }.map do |rows|
31
+ p @table[rows.first, 1]
32
+ { name: @table[rows.first, 1], studies: get_studies(rows, cols) }
33
+ end
34
+ end
35
+
36
+ #Contract [Pos, Pos], [Pos, Pos] => Array
37
+ def get_studies(rows, cols)
38
+ (rows.first..rows.last).each_slice(2).map do |study_rows|
39
+ study_rows.each_with_index.map do |row, index|
40
+ cabinet = @table[row, cols.last]
41
+ { info: parse_info(@table[row, cols.first]),
42
+ cabinet: if cabinet.nil? || if cabinet.kind_of?(String); cabinet.empty?; else; false end;
43
+ @table[row - index, cols.last]
44
+ else
45
+ cabinet
46
+ end }
47
+ end.reject{ |s| s[:info].nil? }
48
+ end
49
+ end
50
+
51
+ #Contract Maybe[String] => Maybe[Hash]
52
+ def parse_info(info)
53
+ unless info.nil? || info.empty?
54
+ reversed_info = info.split(/[\s\n\.\(\)\-_,]/).reverse.join(' ')
55
+ # TODO Переписать регулярку с использованием условий в движке Onigmo (Ruby 2.0)
56
+ r = /
57
+ (
58
+ ([Пп]|[Пп][Гг]|[Пп][Оо][Дд][Гг][Рр]|[Пп][Оо][Дд][Гг][Рр][Уу][Пп][Пп][Аа])*\s*
59
+ (?<subgroup>[[:digit:]])
60
+ \s*([Пп]|[Пп][Гг]|[Пп][Оо][Дд][Гг][Рр]|[Пп][Оо][Дд][Гг][Рр][Уу][Пп][Пп][Аа])*\s*
61
+ )?
62
+ (
63
+ (?<patronymic>[[:upper:]])\s
64
+ (?<name>[[:upper:]])\s*
65
+ )?
66
+ (
67
+ (?<surname>
68
+ ([Вв][Аа][Кк][Аа][Нн][Сс][Ии][Яя]|[[:upper:]][[:lower:]]+)
69
+ )
70
+ )?
71
+ (?<title>.*)/mx
72
+ data = r.match(reversed_info.strip)
73
+ res = { subject: data[:title].split.reverse.join(' ').strip,
74
+ lecturer: { surname: data[:surname], name: data[:name], patronymic: data[:patronymic] },
75
+ subgroup: data[:subgroup] }
76
+ res
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,51 @@
1
+ # coding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require 'mail'
4
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
5
+ require 'contracts'
6
+ #include Contracts
7
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~
8
+ class Mailer
9
+ #Contract String, String, Proc => Any
10
+ def initialize(email, password, &block)
11
+ @email = Mailer.email_valid?(email) ? email : (raise ArgumentError, "Incorrect email: #{email}")
12
+ @password = password
13
+ set_defaults
14
+ @mail = Mail.new(&block)
15
+ @mail.charset = 'UTF-8'
16
+ end
17
+
18
+ #Contract String => Bool
19
+ def self.email_valid?(email)
20
+ true & (/[\w\d\._\-]+@[\w\d\.\-]+[\.][\w\d\.\-]+/i =~ email)
21
+ end
22
+
23
+ #Contract None => Any
24
+ def send!
25
+ @mail.deliver!
26
+ end
27
+
28
+ private
29
+ #Contract None => ({ local: String, domain: String })
30
+ def email_parts
31
+ @email[/([\w\d._-]+)@([\w\d.-]+)/i]
32
+ { local: $1, domain: $2 }
33
+ end
34
+
35
+ #Contract None => Any
36
+ def set_defaults
37
+ parts = email_parts
38
+ p email_parts
39
+ pass = @password
40
+ Mail.defaults do
41
+ delivery_method :smtp, {
42
+ address: "smtp.#{parts[:domain]}",
43
+ port: '587',
44
+ user_name: parts[:local],
45
+ password: pass,
46
+ authentication: :plain,
47
+ enable_starttls_auto: true
48
+ }
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ class CreateSpecialitySubjects < ActiveRecord::Migration
2
+ def change
3
+ create_table :speciality_subjects do |t|
4
+ t.integer :lecturer_id
5
+ t.integer :subject_id
6
+ t.integer :semester_id
7
+ t.integer :speciality_id
8
+ t.integer :lecture_hours
9
+ t.integer :practical_hours
10
+ t.integer :consultations_hours
11
+ t.string :preffered_days
12
+ t.boolean :facultative
13
+
14
+ t.timestamps
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ class CreateEmails < ActiveRecord::Migration
2
+ def change
3
+ create_table :emails do |t|
4
+ t.references :emailable, :polymorphic => true
5
+ t.string :email
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,32 @@
1
+ class AddIndexes < ActiveRecord::Migration
2
+ def change
3
+ add_index(:groups, :title)
4
+
5
+ add_index(:subgroups, :group_id)
6
+ add_index(:subgroups, :number)
7
+
8
+ add_index(:subjects, :title)
9
+
10
+ add_index(:cabinets, :title)
11
+
12
+ add_index(:lecturers, :surname)
13
+
14
+ add_index(:studies, :subject_id)
15
+ add_index(:studies, :lecturer_id)
16
+ add_index(:studies, :cabinet_id)
17
+ add_index(:studies, [:groupable_id, :groupable_type])
18
+
19
+ add_index(:courses, :number)
20
+
21
+ add_index(:specialities, :title)
22
+
23
+ add_index(:semesters, :course_id)
24
+ add_index(:semesters, :title)
25
+
26
+ add_index(:speciality_subjects, :subject_id)
27
+ add_index(:speciality_subjects, :lecturer_id)
28
+ add_index(:speciality_subjects, [:speciality_id, :semester_id])
29
+
30
+ add_index(:emails, [:emailable_id, :emailable_type])
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ class CreateGroups < ActiveRecord::Migration
2
+ def change
3
+ create_table :groups do |t|
4
+ t.string :title
5
+ t.integer :speciality_id
6
+ t.integer :course_id
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ class CreateSubgroups < ActiveRecord::Migration
2
+ def change
3
+ create_table :subgroups do |t|
4
+ t.integer :number
5
+ t.integer :group_id
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ # coding: UTF-8
2
+ class CreateSubjects < ActiveRecord::Migration
3
+ def change
4
+ create_table :subjects do |t|
5
+ t.string :title
6
+ t.boolean :stub, default: false
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # coding: UTF-8
2
+ class CreateCabinets < ActiveRecord::Migration
3
+ def change
4
+ create_table :cabinets do |t|
5
+ t.string :title
6
+ t.boolean :stub, default: false
7
+ t.boolean :with_computers, default: false
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # coding: UTF-8
2
+ class CreateLecturers < ActiveRecord::Migration
3
+ def change
4
+ create_table :lecturers do |t|
5
+ t.string :surname
6
+ t.string :name
7
+ t.string :patronymic
8
+ t.string :preferred_days
9
+ t.boolean :stub, default: false
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ class CreateStudies < ActiveRecord::Migration
2
+ def change
3
+ create_table :studies do |t|
4
+ t.references :groupable, :polymorphic => true
5
+ t.integer :subject_id
6
+ t.integer :lecturer_id
7
+ t.integer :cabinet_id
8
+ t.integer :number
9
+ t.date :date
10
+ t.string :color
11
+
12
+ t.timestamps
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ class CreateCourses < ActiveRecord::Migration
2
+ def change
3
+ create_table :courses do |t|
4
+ t.integer :number
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class CreateSpecialities < ActiveRecord::Migration
2
+ def change
3
+ create_table :specialities do |t|
4
+ t.string :title
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class CreateSemesters < ActiveRecord::Migration
2
+ def change
3
+ create_table :semesters do |t|
4
+ t.string :title
5
+ t.integer :course_id
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ class Cabinet < ActiveRecord::Base
2
+ has_many :studies
3
+
4
+ before_destroy :set_stubs_for_studies
5
+
6
+ def to_s
7
+ title
8
+ end
9
+
10
+ def set_stubs_for_studies
11
+ raise "Stub can't be destroyed!" if self.stub
12
+ stub = Cabinet.where(stub: true).first
13
+ studies.each do |s|
14
+ s.cabinet = stub
15
+ s.save
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ class Course < ActiveRecord::Base
2
+ has_many :semesters, :dependent => :destroy
3
+
4
+ def current_semester
5
+ if Date.today.yday > 182
6
+ semesters.first
7
+ else
8
+ semesters.last
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ # coding: UTF-8
2
+ class Email < ActiveRecord::Base
3
+ belongs_to :emailable, :polymorphic => true
4
+
5
+ def validate
6
+ end
7
+
8
+ def to_group?
9
+ emailable_type == 'Group'
10
+ end
11
+
12
+ def to_lecturer?
13
+ emailable_type == 'Lecturer'
14
+ end
15
+
16
+ def email_valid?
17
+ true & (/[\w\d._-]+@[\w\d.-]+[.][\w\d.-]+/i =~ email)
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ class Group < ActiveRecord::Base
2
+ belongs_to :speciality
3
+ belongs_to :course
4
+ has_many :subgroups, :dependent => :destroy
5
+ has_many :studies, :as => :groupable, :dependent => :destroy
6
+ has_many :emails, :as => :emailable, :dependent => :destroy
7
+
8
+ def group?
9
+ true
10
+ end
11
+
12
+ def subgroup?
13
+ false
14
+ end
15
+
16
+ def get_group
17
+ self
18
+ end
19
+
20
+ def number
21
+ 0
22
+ end
23
+
24
+ def title_for_sort
25
+ self.title[/(.*)-(.*)/i]; "#{$2}-#{$1}"
26
+ end
27
+
28
+ def to_s
29
+ title
30
+ end
31
+ end
@@ -0,0 +1,45 @@
1
+ # coding: UTF-8
2
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ require 'contracts'
4
+ #include Contracts
5
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6
+ class Lecturer < ActiveRecord::Base
7
+ has_many :studies
8
+ has_many :speciality_subjects
9
+ has_many :emails, :as => :emailable, :dependent => :destroy
10
+
11
+ before_destroy :set_stubs_for_studies
12
+
13
+ #Contract None => String
14
+ def to_s
15
+ first = surname
16
+ if name.nil?
17
+ second = ""
18
+ else
19
+ if name.empty?
20
+ second = name
21
+ else
22
+ second = name[0].mb_chars.capitalize.to_s
23
+ end
24
+ end
25
+ if patronymic.nil?
26
+ third = ""
27
+ else
28
+ if patronymic.empty?
29
+ third = name
30
+ else
31
+ third = patronymic[0].mb_chars.capitalize.to_s
32
+ end
33
+ end
34
+ "#{surname} #{second}.#{third}."
35
+ end
36
+
37
+ def set_stubs_for_studies
38
+ raise "Stub can't be destroyed!" if self.stub
39
+ stub = Lecturer.where(stub: true).first
40
+ studies.each do |s|
41
+ s.lecturer = stub
42
+ s.save
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,4 @@
1
+ class Semester < ActiveRecord::Base
2
+ belongs_to :course
3
+ has_many :speciality_subjects, :dependent => :destroy
4
+ end
@@ -0,0 +1,3 @@
1
+ class Speciality < ActiveRecord::Base
2
+ has_many :speciality_subjects, :dependent => :destroy
3
+ end