diff --git a/app/models/available_course.rb b/app/models/available_course.rb new file mode 100644 index 000000000..7ba166cb9 --- /dev/null +++ b/app/models/available_course.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AvailableCourse < ApplicationRecord + # relations + belongs_to :academic_term + belongs_to :curriculum + belongs_to :course + has_many :groups, class_name: 'AvailableCourseGroup', dependent: :destroy +end diff --git a/app/models/available_course_group.rb b/app/models/available_course_group.rb new file mode 100644 index 000000000..3b851273d --- /dev/null +++ b/app/models/available_course_group.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class AvailableCourseGroup < ApplicationRecord + # relations + belongs_to :available_course + has_many :lecturers, class_name: 'AvailableCourseLecturer', foreign_key: :group_id, + inverse_of: :group, dependent: :destroy + + # validations + validates :name, presence: true, uniqueness: { scope: :available_course } + validates :quota, numericality: { only_integer: true, greater_than_or_equal_to: 1 }, allow_blank: true +end diff --git a/app/models/available_course_lecturer.rb b/app/models/available_course_lecturer.rb new file mode 100644 index 000000000..aa89450f9 --- /dev/null +++ b/app/models/available_course_lecturer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class AvailableCourseLecturer < ApplicationRecord + # relations + belongs_to :group, class_name: 'AvailableCourseGroup' + belongs_to :lecturer, class_name: 'Employee' + + # validations + validates :coordinator, presence: true, inclusion: { in: [true, false] } + + # scopes + scope :coordinator, -> { where(coordinator: true) } +end diff --git a/app/models/curriculum.rb b/app/models/curriculum.rb index d41d56426..3d5d68b89 100644 --- a/app/models/curriculum.rb +++ b/app/models/curriculum.rb @@ -21,6 +21,7 @@ class Curriculum < ApplicationRecord has_many :semesters, class_name: 'CurriculumSemester', inverse_of: :curriculum, dependent: :destroy has_many :courses, through: :semesters + has_many :available_courses, dependent: :destroy # nested models accepts_nested_attributes_for :semesters, reject_if: :all_blank, allow_destroy: true diff --git a/app/models/employee.rb b/app/models/employee.rb index 072d4c97b..1bb292939 100644 --- a/app/models/employee.rb +++ b/app/models/employee.rb @@ -8,6 +8,7 @@ class Employee < ApplicationRecord has_many :units, through: :duties has_many :positions, through: :duties has_many :administrative_functions, through: :duties + has_many :available_course_lecturers, foreign_key: :lecturer_id, inverse_of: :lecturer, dependent: :destroy # validations validates :title_id, uniqueness: { scope: %i[user active] } diff --git a/db/migrate/20181112174827_create_available_courses.rb b/db/migrate/20181112174827_create_available_courses.rb new file mode 100644 index 000000000..b5354e2e7 --- /dev/null +++ b/db/migrate/20181112174827_create_available_courses.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateAvailableCourses < ActiveRecord::Migration[5.2] + def change + create_table :available_courses do |t| + t.references :academic_term + t.references :curriculum + t.references :course + t.timestamps + end + end +end diff --git a/db/migrate/20181112175328_create_available_course_groups.rb b/db/migrate/20181112175328_create_available_course_groups.rb new file mode 100644 index 000000000..d99a82e04 --- /dev/null +++ b/db/migrate/20181112175328_create_available_course_groups.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateAvailableCourseGroups < ActiveRecord::Migration[5.2] + def change + create_table :available_course_groups do |t| + t.string :name, null: false, limit: 50 + t.integer :quota + t.references :available_course + t.timestamps + end + end +end diff --git a/db/migrate/20181112175343_create_available_course_lecturers.rb b/db/migrate/20181112175343_create_available_course_lecturers.rb new file mode 100644 index 000000000..b759e0acf --- /dev/null +++ b/db/migrate/20181112175343_create_available_course_lecturers.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateAvailableCourseLecturers < ActiveRecord::Migration[5.2] + def change + create_table :available_course_lecturers do |t| + t.boolean :coordinator, default: false, null: false + t.references :group, foreign_key: { to_table: :available_course_groups } + t.references :lecturer, foreign_key: { to_table: :employees } + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index b48120d3d..ccd532d9d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -129,6 +129,36 @@ t.index ["user_id"], name: "index_articles_on_user_id" end + create_table "available_course_groups", force: :cascade do |t| + t.string "name", limit: 50, null: false + t.integer "quota" + t.bigint "available_course_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["available_course_id"], name: "index_available_course_groups_on_available_course_id" + end + + create_table "available_course_lecturers", force: :cascade do |t| + t.boolean "coordinator", default: false, null: false + t.bigint "group_id" + t.bigint "lecturer_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["group_id"], name: "index_available_course_lecturers_on_group_id" + t.index ["lecturer_id"], name: "index_available_course_lecturers_on_lecturer_id" + end + + create_table "available_courses", force: :cascade do |t| + t.bigint "academic_term_id" + t.bigint "curriculum_id" + t.bigint "course_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["academic_term_id"], name: "index_available_courses_on_academic_term_id" + t.index ["course_id"], name: "index_available_courses_on_course_id" + t.index ["curriculum_id"], name: "index_available_courses_on_curriculum_id" + end + create_table "calendar_events", force: :cascade do |t| t.datetime "start_date", null: false t.datetime "end_date" @@ -632,6 +662,8 @@ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end + add_foreign_key "available_course_lecturers", "available_course_groups", column: "group_id" + add_foreign_key "available_course_lecturers", "employees", column: "lecturer_id" add_foreign_key "calendar_title_types", "calendar_titles", column: "title_id" add_foreign_key "calendar_title_types", "calendar_types", column: "type_id" add_foreign_key "courses", "languages" diff --git a/test/fixtures/available_course_groups.yml b/test/fixtures/available_course_groups.yml new file mode 100644 index 000000000..7deb2acdf --- /dev/null +++ b/test/fixtures/available_course_groups.yml @@ -0,0 +1,4 @@ +ati_group_1: + available_course: ati_fall_2017_2018 + name: Group 1 + quota: 10 diff --git a/test/fixtures/available_course_lecturers.yml b/test/fixtures/available_course_lecturers.yml new file mode 100644 index 000000000..7c2928fa5 --- /dev/null +++ b/test/fixtures/available_course_lecturers.yml @@ -0,0 +1,8 @@ +ati_group_1_lecturer_john: + group: ati_group_1 + lecturer: chief_john + coordinator: true +ati_group_1_lecturer_serhat: + group: ati_group_1 + lecturer: serhat_active + coordinator: false diff --git a/test/fixtures/available_courses.yml b/test/fixtures/available_courses.yml new file mode 100644 index 000000000..ef3271b6f --- /dev/null +++ b/test/fixtures/available_courses.yml @@ -0,0 +1,4 @@ +ati_fall_2017_2018: + academic_term: fall_2017_2018 + curriculum: one + course: ati diff --git a/test/models/course/available_course_group_test.rb b/test/models/course/available_course_group_test.rb new file mode 100644 index 000000000..798f38676 --- /dev/null +++ b/test/models/course/available_course_group_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'test_helper' + +class AvailableCourseGroupTest < ActiveSupport::TestCase + # relations + %i[ + available_course + lecturers + ].each do |property| + test "a available_course_group can communicate with #{property}" do + assert available_course_groups(:ati_group_1).send(property) + end + end + + # validations: presence + test 'should not save available_course_group without name' do + available_course_groups(:ati_group_1).name = nil + assert_not available_course_groups(:ati_group_1).valid? + assert_not_empty available_course_groups(:ati_group_1).errors[:name] + end + + # validations: uniqueness + test 'name should be unique' do + fake = available_course_groups(:ati_group_1).dup + assert_not fake.valid? + assert_not_empty fake.errors[:name] + end +end diff --git a/test/models/course/available_course_lecturer_test.rb b/test/models/course/available_course_lecturer_test.rb new file mode 100644 index 000000000..9c8344d76 --- /dev/null +++ b/test/models/course/available_course_lecturer_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'test_helper' + +class AvailableCourseLecturerTest < ActiveSupport::TestCase + # relations + %i[ + group + lecturer + ].each do |property| + test "a available_course_lecturer can communicate with #{property}" do + assert available_course_lecturers(:ati_group_1_lecturer_john).send(property) + end + end + + # validations: presence + test 'should not save available_course_group without coordinator info' do + available_course_lecturers(:ati_group_1_lecturer_john).coordinator = nil + assert_not available_course_lecturers(:ati_group_1_lecturer_john).valid? + assert_not_empty available_course_lecturers(:ati_group_1_lecturer_john).errors[:coordinator] + end + + # scopes + test 'coordinator scope returns coordinator lecturers' do + lecturers = available_course_groups(:ati_group_1).lecturers + assert lecturers.coordinator.to_a.include?(available_course_lecturers(:ati_group_1_lecturer_john)) + assert_not lecturers.coordinator.to_a.include?(available_course_lecturers(:ati_group_1_lecturer_serhat)) + end +end diff --git a/test/models/course/available_course_test.rb b/test/models/course/available_course_test.rb new file mode 100644 index 000000000..17f247cb7 --- /dev/null +++ b/test/models/course/available_course_test.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'test_helper' + +class AvailableCourseTest < ActiveSupport::TestCase + # relations + %i[ + academic_term + curriculum + course + groups + ].each do |property| + test "a available_course can communicate with #{property}" do + assert available_courses(:ati_fall_2017_2018).send(property) + end + end +end diff --git a/test/models/course/curriculum_test.rb b/test/models/course/curriculum_test.rb index dbf84218d..94531a2f4 100644 --- a/test/models/course/curriculum_test.rb +++ b/test/models/course/curriculum_test.rb @@ -13,6 +13,7 @@ class CurriculumTest < ActiveSupport::TestCase programs semesters courses + available_courses ].each do |relation| test "curriculum can communicate with #{relation}" do assert @curriculum.send(relation) diff --git a/test/models/employee/employee_test.rb b/test/models/employee/employee_test.rb index 5d484f1c9..8c141ba8c 100644 --- a/test/models/employee/employee_test.rb +++ b/test/models/employee/employee_test.rb @@ -11,6 +11,7 @@ class EmployeeTest < ActiveSupport::TestCase units positions administrative_functions + available_course_lecturers ].each do |property| test "an employee can communicate with #{property}" do assert employees(:serhat_active).send(property)