tmis 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/COPYING +674 -0
- data/README.md +44 -0
- data/Rakefile +64 -0
- data/bin/tmis +4 -0
- data/lib/tmis/engine/database.rb +58 -0
- data/lib/tmis/engine/export/timetable_exporter.rb +366 -0
- data/lib/tmis/engine/import/abstract_spreadsheet.rb +53 -0
- data/lib/tmis/engine/import/spreadsheet_roo.rb +136 -0
- data/lib/tmis/engine/import/timetable_manager.rb +110 -0
- data/lib/tmis/engine/import/timetable_reader.rb +79 -0
- data/lib/tmis/engine/mailer/mailer.rb +51 -0
- data/lib/tmis/engine/migrations/10_create_speciality_subjects.rb +17 -0
- data/lib/tmis/engine/migrations/11_create_emails.rb +10 -0
- data/lib/tmis/engine/migrations/12_add_indexes.rb +32 -0
- data/lib/tmis/engine/migrations/1_create_groups.rb +11 -0
- data/lib/tmis/engine/migrations/2_create_subgroups.rb +10 -0
- data/lib/tmis/engine/migrations/3_create_subjects.rb +11 -0
- data/lib/tmis/engine/migrations/4_create_cabinets.rb +12 -0
- data/lib/tmis/engine/migrations/5_create_lecturers.rb +14 -0
- data/lib/tmis/engine/migrations/6_create_studies.rb +15 -0
- data/lib/tmis/engine/migrations/7_create_courses.rb +9 -0
- data/lib/tmis/engine/migrations/8_create_specialities.rb +9 -0
- data/lib/tmis/engine/migrations/9_create_semesters.rb +10 -0
- data/lib/tmis/engine/models/cabinet.rb +18 -0
- data/lib/tmis/engine/models/course.rb +11 -0
- data/lib/tmis/engine/models/email.rb +19 -0
- data/lib/tmis/engine/models/group.rb +31 -0
- data/lib/tmis/engine/models/lecturer.rb +45 -0
- data/lib/tmis/engine/models/semester.rb +4 -0
- data/lib/tmis/engine/models/speciality.rb +3 -0
- data/lib/tmis/engine/models/speciality_subject.rb +6 -0
- data/lib/tmis/engine/models/study.rb +56 -0
- data/lib/tmis/engine/models/subgroup.rb +21 -0
- data/lib/tmis/engine/models/subject.rb +19 -0
- data/lib/tmis/engine/verificator.rb +96 -0
- data/lib/tmis/interface/forms/about.rb +24 -0
- data/lib/tmis/interface/forms/console.rb +28 -0
- data/lib/tmis/interface/forms/debug_console.rb +32 -0
- data/lib/tmis/interface/forms/edit_study.rb +110 -0
- data/lib/tmis/interface/forms/expand_changes.rb +128 -0
- data/lib/tmis/interface/forms/export_general_timetable.rb +68 -0
- data/lib/tmis/interface/forms/export_group_timetable.rb +158 -0
- data/lib/tmis/interface/forms/export_lecturer_timetable.rb +171 -0
- data/lib/tmis/interface/forms/find.rb +71 -0
- data/lib/tmis/interface/forms/import.rb +36 -0
- data/lib/tmis/interface/forms/settings.rb +125 -0
- data/lib/tmis/interface/forms/ui_about.rb +88 -0
- data/lib/tmis/interface/forms/ui_console.rb +68 -0
- data/lib/tmis/interface/forms/ui_debug_console.rb +82 -0
- data/lib/tmis/interface/forms/ui_edit_study.rb +202 -0
- data/lib/tmis/interface/forms/ui_expand_changes.rb +134 -0
- data/lib/tmis/interface/forms/ui_export_general_timetable.rb +142 -0
- data/lib/tmis/interface/forms/ui_export_group_timetable.rb +160 -0
- data/lib/tmis/interface/forms/ui_export_lecturer_timetable.rb +160 -0
- data/lib/tmis/interface/forms/ui_find.rb +77 -0
- data/lib/tmis/interface/forms/ui_import.rb +134 -0
- data/lib/tmis/interface/forms/ui_settings.rb +417 -0
- data/lib/tmis/interface/mainwindow.rb +933 -0
- data/lib/tmis/interface/models/cabinet_table_model.rb +133 -0
- data/lib/tmis/interface/models/course_table_model.rb +87 -0
- data/lib/tmis/interface/models/group_table_model.rb +190 -0
- data/lib/tmis/interface/models/lecturer_table_model.rb +111 -0
- data/lib/tmis/interface/models/semester_table_model.rb +137 -0
- data/lib/tmis/interface/models/speciality_subject_table_model.rb +288 -0
- data/lib/tmis/interface/models/speciality_table_model.rb +87 -0
- data/lib/tmis/interface/models/study_table_model.rb +323 -0
- data/lib/tmis/interface/models/subgroup_table_model.rb +136 -0
- data/lib/tmis/interface/models/subject_table_model.rb +90 -0
- data/lib/tmis/interface/ui_mainwindow.rb +928 -0
- data/lib/tmis.rb +45 -0
- data/spec/config.rb +49 -0
- data/spec/database_spec.rb +18 -0
- data/spec/export/timetable_exporter_mocks.rb +20 -0
- data/spec/export/timetable_exporter_spec.rb +34 -0
- data/spec/factories/factories.rb +65 -0
- data/spec/import/test_data/raspisanie_2013.csv +104 -0
- data/spec/import/timetable_importer_mocks.rb +6 -0
- data/spec/import/timetable_manager_spec.rb +16 -0
- data/spec/import/timetable_reader_spec.rb +111 -0
- data/spec/import/timetable_roo_spec.rb +48 -0
- data/spec/mailer/mailer_spec.rb +37 -0
- data/spec/mainwindow_spec.rb +18 -0
- data/spec/models/cabinet_spec.rb +33 -0
- data/spec/models/course_spec.rb +26 -0
- data/spec/models/group_spec.rb +33 -0
- data/spec/models/lecturer_spec.rb +38 -0
- data/spec/models/semester_spec.rb +26 -0
- data/spec/models/speciality_spec.rb +26 -0
- data/spec/models/speciality_subject_spec.rb +9 -0
- data/spec/models/study_spec.rb +9 -0
- data/spec/models/subgroup_spec.rb +26 -0
- data/spec/models/subject_spec.rb +39 -0
- 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,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,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,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,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
|