sis_core 1.0.4 → 1.0.9

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/sis/core/application_controller.rb +10 -2
  3. data/app/controllers/sis/core/menus_controller.rb +22 -0
  4. data/app/controllers/sis/core/users_controller.rb +71 -0
  5. data/app/models/sis/core/academic_programme.rb +15 -5
  6. data/app/models/sis/core/academic_year.rb +1 -1
  7. data/app/models/sis/core/applicant.rb +18 -1
  8. data/app/models/sis/core/application.rb +6 -3
  9. data/app/models/sis/core/application_document.rb +3 -1
  10. data/app/models/sis/core/application_module.rb +8 -0
  11. data/app/models/sis/core/application_setup.rb +18 -9
  12. data/app/models/sis/core/document_requirement.rb +4 -1
  13. data/app/models/sis/core/exam.rb +7 -1
  14. data/app/models/sis/core/exam_result.rb +7 -1
  15. data/app/models/sis/core/exam_setup.rb +12 -4
  16. data/app/models/sis/core/menu.rb +11 -0
  17. data/app/models/sis/core/programme_level.rb +1 -1
  18. data/app/models/sis/core/semester.rb +2 -0
  19. data/app/models/sis/core/user.rb +13 -0
  20. data/app/models/sis/core/user_role.rb +10 -0
  21. data/app/services/sis/core/user_service.rb +25 -0
  22. data/app/uploaders/sis/core/application_document_uploader.rb +51 -0
  23. data/app/uploaders/sis/core/photo_uploader.rb +51 -0
  24. data/config/routes.rb +6 -0
  25. data/db/migrate/20200106133937_create_sis_core_applicants.rb +17 -3
  26. data/db/migrate/20200107094449_create_sis_core_applications.rb +4 -6
  27. data/db/migrate/20200107101638_create_sis_core_application_documents.rb +1 -1
  28. data/db/migrate/20200507114003_create_sis_core_users.rb +17 -0
  29. data/db/migrate/20200511165121_create_sis_core_application_modules.rb +15 -0
  30. data/db/migrate/20200511165241_create_sis_core_user_roles.rb +15 -0
  31. data/db/migrate/20200511165450_create_sis_core_menus.rb +18 -0
  32. data/lib/authorize_request.rb +18 -0
  33. data/lib/json_web_token.rb +12 -0
  34. data/lib/sis/core/engine.rb +2 -0
  35. data/lib/sis/core/version.rb +1 -1
  36. data/lib/sis_core.rb +4 -2
  37. data/spec/factories/sis/core/academic_years.rb +1 -1
  38. data/spec/factories/sis/core/applicants.rb +11 -2
  39. data/spec/factories/sis/core/application_modules.rb +6 -0
  40. data/spec/factories/sis/core/applications.rb +1 -6
  41. data/spec/factories/sis/core/menus.rb +9 -0
  42. data/spec/factories/sis/core/programme_levels.rb +1 -1
  43. data/spec/factories/sis/core/user_roles.rb +6 -0
  44. data/spec/factories/sis/core/users.rb +9 -0
  45. metadata +87 -14
  46. data/app/models/sis/core/staff.rb +0 -21
  47. data/app/models/user.rb +0 -12
  48. data/config/initializers/devise_token_auth.rb +0 -55
  49. data/db/migrate/20200108092202_devise_token_auth_create_users.rb +0 -53
  50. data/db/migrate/20200316120805_create_sis_core_staffs.rb +0 -31
  51. data/db/migrate/20200319074334_modify_user.rb +0 -16
  52. data/spec/factories/sis/core/staffs.rb +0 -35
  53. data/spec/factories/users.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a88af09f30c54f634169a0051ad20db0e505581463a8b94af3baf6cfc10b9f17
4
- data.tar.gz: ebe53808d2a44ab226f2bc4f8d9e087e9504586eac604286bbe1c16b9d35dad0
3
+ metadata.gz: e6b30327db722ca3f16bad73c555e382920cacff0baec011f504b999840e9cc4
4
+ data.tar.gz: 9f92fb5a3268456261cf45e3cc870196705638c5b6fbc19c5effbc48157f3420
5
5
  SHA512:
6
- metadata.gz: a422801f3afecd6e9e7ca9f2b7c5aeb03caebdf0dcbdffe10e446b9aa4f7f87cbfc8f882a40879d21533fd3a049e04a91885d263c4b67c6656d7b25f411094c9
7
- data.tar.gz: 27c8b0c2091c608f81193b01af3973769e9281c161a2f6283b568fb6bca725bbe38543d6c00e814fc155774285d752e46e021524bc40e040dcb617e3abd2feee
6
+ metadata.gz: 2c2f778c15f7791999026a1a6e5114be6ac25e072abda5ce91535cad9818c9dfa90096fefe14eb4e50a0f284a295496ec0e7a6ff25f25ed7e193fa8aa8c105d0
7
+ data.tar.gz: b67d563aa05be808f40ea3618945e45a99538000ad225510158b0203636dba471604c6d949c9d26f5f2f867f8a8fc4ccb8071c3b21db4cecbebec59e49f6d446
@@ -1,7 +1,15 @@
1
1
  module Sis
2
2
  module Core
3
- class ApplicationController < ActionController::Base
4
- protect_from_forgery with: :exception
3
+ class ApplicationController < ActionController::API
4
+ before_action :authenticate_request
5
+ attr_reader :current_user
6
+
7
+ private
8
+
9
+ def authenticate_request
10
+ @current_user = AuthorizeRequest.user(request.headers)
11
+ render json: { error: 'Not Authorized' }, status: 401 unless @current_user
12
+ end
5
13
  end
6
14
  end
7
15
  end
@@ -0,0 +1,22 @@
1
+ module Sis
2
+ module Core
3
+ class MenusController < ApplicationController
4
+ skip_before_action :authenticate_request, only: [:menu]
5
+ def menu
6
+ app_module_id = ApplicationModule.find_by_code(params[:app_module]).id
7
+ user_roles = User.find(params[:user_id]).user_roles.where(application_module_id: app_module_id)
8
+ menus = []
9
+ user_roles.each do |role|
10
+ user_menus = role.menus.where(parent_id: nil, application_module_id: app_module_id)
11
+ user_menus.each do |user_menu|
12
+ children = user_menu.children.where(parent_id: user_menu.id, application_module_id: app_module_id)
13
+ children = children.map { |c| { label: c.text, icon: c.icon_cls, routerLink: [c.location] } }
14
+ menus.push({ id: user_menu.id, label: user_menu.text, icon: user_menu.icon_cls,
15
+ location: user_menu.location, items: children, expanded: true })
16
+ end
17
+ end
18
+ render json: { success: true, data: menus }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,71 @@
1
+ module Sis
2
+ module Core
3
+ class UsersController < ApplicationController
4
+ skip_before_action :authenticate_request, only: %i[login sign_in generate_recovery_token reset_password]
5
+ before_action :set_user_service, only: %i[generate_recovery_token reset_password]
6
+ def sign_in
7
+ applicant = Applicant&.find_by_email(params[:email])
8
+ if Applicant&.find_by_email(params[:email]) && applicant.authenticate(params[:password])
9
+ token = JsonWebToken.encode({ id: applicant.id, first_name: applicant.first_name,
10
+ middle_name: applicant.middle_name, last_name: applicant.last_name,
11
+ email: applicant.email })
12
+ render json: { success: true, jwt: token }
13
+ else
14
+ render json: { success: false, errors: ['Invalid username or password !'] }
15
+ end
16
+ end
17
+
18
+ def create
19
+ user = User.new(user_params)
20
+ if user.save
21
+ render json: { success: true, data: user }
22
+ else
23
+ render json: { success: false, errors: user.errors }
24
+ end
25
+ end
26
+
27
+ def login
28
+ user = User.find_by_email(params[:email])
29
+ if User&.find_by_email(params[:email]) && user.authenticate(params[:password])
30
+ token = JsonWebToken.encode({ id: user.id, first_name: user.first_name, last_name: user.last_name })
31
+ render json: { success: true, jwt: token }
32
+ else
33
+ render json: { success: false, errors: ['Invalid username or password !'] }
34
+ end
35
+ end
36
+
37
+ def generate_recovery_token
38
+ user = User.where(email: params[:email], secret_question_answer: params[:secret_question_answer])
39
+ if user.count.positive?
40
+ user = user[0]
41
+ @service.generate_password_token(user)
42
+ render json: { success: true, token: user.reset_password_token, email: user.email }
43
+ else
44
+ render json: { success: false, errors: ['User not found !'] }
45
+ end
46
+ end
47
+
48
+ def reset_password
49
+ token = params[:token].to_s
50
+ user = User.find_by_reset_password_token(token)
51
+ if user && @service.password_token_valid?(user.id)
52
+ @service.reset_password(user.id, params[:password])
53
+ render json: { success: true }
54
+ else
55
+ render json: { success: false, errors: ['Link not valid or expired. Try generating a new link !'] }
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def user_params
62
+ params.require('user').permit(:id, :first_name, :last_name, :email, :password, :reset_password_token,
63
+ :reset_password_sent_at, :secret_question, :secret_question_answer)
64
+ end
65
+
66
+ def set_user_service
67
+ @service = UserService.new
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,7 +1,17 @@
1
- module Sis::Core
2
- class AcademicProgramme < ApplicationRecord
3
- belongs_to :programme
4
- belongs_to :programme_type
5
- belongs_to :programme_level
1
+ module Sis
2
+ module Core
3
+ class AcademicProgramme < ApplicationRecord
4
+ belongs_to :programme
5
+ belongs_to :programme_type
6
+ belongs_to :programme_level
7
+
8
+ delegate(:name, to: :programme, prefix: true, allow_nil: false)
9
+ delegate(:name, to: :programme_type, prefix: true, allow_nil: false)
10
+ delegate(:name, to: :programme_level, prefix: true, allow_nil: false)
11
+
12
+ def name
13
+ programme.name.to_s + ' ' + programme_level.name.to_s + ' ' + programme_type.name.to_s
14
+ end
15
+ end
6
16
  end
7
17
  end
@@ -1,7 +1,7 @@
1
1
  module Sis
2
2
  module Core
3
3
  class AcademicYear < ApplicationRecord
4
- validates :year, presence: true, uniqueness: true, case_sensitive: false
4
+ validates :year, presence: true, uniqueness: { case_sensitive: false }
5
5
  end
6
6
  end
7
7
  end
@@ -1,8 +1,25 @@
1
1
  module Sis
2
2
  module Core
3
3
  class Applicant < ApplicationRecord
4
- validates :full_name, :email, :phone_number, presence: true
4
+ mount_base64_uploader :photo, PhotoUploader
5
+ validates :first_name, :middle_name, :last_name, :email, presence: true
5
6
  validates :email, uniqueness: true
7
+
8
+ has_many :applications
9
+
10
+ has_secure_password
11
+
12
+ def full_name
13
+ first_name + ' ' + middle_name + ' ' + last_name
14
+ end
15
+
16
+ scope :by_application_setup, (lambda do |app_setup_id|
17
+ joins(applications: :application_setup).where('sis_core_applications.application_setup_id': app_setup_id)
18
+ end)
19
+
20
+ scope :by_status, (lambda do |status|
21
+ joins(:applications).where('sis_core_applications.status': status)
22
+ end)
6
23
  end
7
24
  end
8
25
  end
@@ -16,7 +16,7 @@ module Sis
16
16
  end
17
17
 
18
18
  event :review do
19
- transitions from: [:submitted, :rejected, :accepted], to: :under_review
19
+ transitions from: %i[submitted rejected accepted], to: :under_review
20
20
  end
21
21
 
22
22
  event :accept do
@@ -34,14 +34,17 @@ module Sis
34
34
  event :select_for_admission do
35
35
  transitions from: :selected_for_exam, to: :selected_for_admission
36
36
  end
37
-
38
37
  end
39
38
 
40
- validates :full_name, :email, :age, :gender, :nationality, :status, presence: true
39
+ validates :status, presence: true
41
40
  validates :payment_information, presence: true, if: :payment_made
42
41
 
43
42
  belongs_to :applicant
44
43
  belongs_to :academic_programme
44
+ belongs_to :application_setup
45
+
46
+ delegate(:name, to: :academic_programme, prefix: true, allow_nil: false)
47
+ delegate(:full_name, to: :applicant, prefix: true, allow_nil: false)
45
48
  end
46
49
  end
47
50
  end
@@ -1,10 +1,12 @@
1
1
  module Sis
2
2
  module Core
3
3
  class ApplicationDocument < ApplicationRecord
4
- validates :document, presence: true
4
+ mount_base64_uploader :document, ApplicationDocumentUploader
5
5
 
6
6
  belongs_to :application
7
7
  belongs_to :document_requirement
8
+
9
+ delegate(:name, to: :document_requirement, prefix: true, allow_nil: false)
8
10
  end
9
11
  end
10
12
  end
@@ -0,0 +1,8 @@
1
+ module Sis
2
+ module Core
3
+ class ApplicationModule < ApplicationRecord
4
+ has_and_belongs_to_many :users
5
+ validates :code, :name, presence: true
6
+ end
7
+ end
8
+ end
@@ -1,14 +1,23 @@
1
- module Sis::Core
2
- class ApplicationSetup < ApplicationRecord
3
- OPEN = 'Open'.freeze
4
- CLOSED = 'Closed'.freeze
1
+ module Sis
2
+ module Core
3
+ class ApplicationSetup < ApplicationRecord
4
+ OPEN = 'Open'.freeze
5
+ CLOSED = 'Closed'.freeze
5
6
 
6
- belongs_to :academic_programme
7
- belongs_to :target_semester, class_name: 'Sis::Core::Semester'
8
- has_many :document_requirements
7
+ belongs_to :academic_programme
8
+ belongs_to :target_semester, class_name: 'Sis::Core::Semester'
9
+ has_many :document_requirements
9
10
 
10
- validates :start_date, :end_date, :status, presence: true
11
- validates :status, inclusion: [OPEN, CLOSED]
11
+ validates :start_date, :end_date, :status, presence: true
12
+ validates :status, inclusion: [OPEN, CLOSED]
12
13
 
14
+ delegate(:name, to: :target_semester, prefix: true, allow_nil: false)
15
+ delegate(:name, to: :academic_programme, prefix: true, allow_nil: false)
16
+
17
+ def name
18
+ academic_programme.programme.name.to_s + ' ' + academic_programme.programme_level.name.to_s + ' ' +
19
+ academic_programme.programme_type.name.to_s
20
+ end
21
+ end
13
22
  end
14
23
  end
@@ -5,6 +5,9 @@ module Sis
5
5
  belongs_to :application_setup
6
6
 
7
7
  validates :original, :copy, presence: true
8
+
9
+ delegate(:name, to: :document_type, prefix: true, allow_nil: false)
10
+ delegate(:name, to: :application_setup, prefix: true, allow_nil: false)
8
11
  end
9
12
  end
10
- end
13
+ end
@@ -8,8 +8,14 @@ module Sis
8
8
  before_validation :set_pass_point
9
9
 
10
10
  def set_pass_point
11
- self.pass_point = self.points / 2 unless self.pass_point || !self.points
11
+ self.pass_point = points / 2 unless pass_point || !points
12
12
  end
13
+
14
+ delegate(:name, to: :exam_setup, prefix: true, allow_nil: false)
15
+
16
+ scope :by_application_setup, (lambda do |app_setup_id|
17
+ joins(:exam_setup).where('sis_core_exam_setups.application_setup_id': app_setup_id)
18
+ end)
13
19
  end
14
20
  end
15
21
  end
@@ -8,11 +8,17 @@ module Sis
8
8
  belongs_to :application
9
9
 
10
10
  validates :points, presence: true
11
+ validates_uniqueness_of :application_id, scope: %i[exam_id]
12
+
11
13
 
12
14
  before_validation :set_status
13
15
 
14
16
  def set_status
15
- self.status = (exam && exam.pass_point >= points) ? PASS : FAIL
17
+ self.status = if exam && exam.pass_point >= points
18
+ PASS
19
+ else
20
+ FAIL
21
+ end
16
22
  end
17
23
  end
18
24
  end
@@ -1,7 +1,15 @@
1
- module Sis::Core
2
- class ExamSetup < ApplicationRecord
3
- belongs_to :application_setup
1
+ module Sis
2
+ module Core
3
+ class ExamSetup < ApplicationRecord
4
+ belongs_to :application_setup
4
5
 
5
- validates :date, :hours, :venue, presence: true
6
+ validates :date, :hours, :venue, presence: true
7
+
8
+ def name
9
+ application_setup.academic_programme.name
10
+ end
11
+
12
+ delegate(:name, to: :application_setup, prefix: true, allow_nil: false)
13
+ end
6
14
  end
7
15
  end
@@ -0,0 +1,11 @@
1
+ module Sis
2
+ module Core
3
+ class Menu < ApplicationRecord
4
+ has_and_belongs_to_many :user_roles
5
+ belongs_to :application_module
6
+ belongs_to :parent, class_name: 'Sis::Core::Menu', optional: true
7
+ has_many :children, class_name: 'Sis::Core::Menu', foreign_key: 'parent_id'
8
+ validates :text, presence: true
9
+ end
10
+ end
11
+ end
@@ -3,4 +3,4 @@ module Sis
3
3
  class ProgrammeLevel < Lookup
4
4
  end
5
5
  end
6
- end
6
+ end
@@ -4,6 +4,8 @@ module Sis
4
4
  belongs_to :academic_year
5
5
 
6
6
  validates :name, presence: true
7
+
8
+ delegate(:label, to: :academic_year, prefix: true, allow_nil: false)
7
9
  end
8
10
  end
9
11
  end
@@ -0,0 +1,13 @@
1
+ module Sis
2
+ module Core
3
+ class User < ApplicationRecord
4
+ validates :first_name, :last_name, :email, presence: true
5
+ validates :password, presence: true, on: :create
6
+ validates :email, uniqueness: true
7
+ has_and_belongs_to_many :application_modules
8
+ has_and_belongs_to_many :user_roles
9
+
10
+ has_secure_password
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Sis
2
+ module Core
3
+ class UserRole < ApplicationRecord
4
+ validates :name, presence: true
5
+ has_and_belongs_to_many :users
6
+ has_and_belongs_to_many :menus
7
+ belongs_to :application_module
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ module Sis
2
+ module Core
3
+ class UserService
4
+ def generate_password_token(user)
5
+ user.update({ reset_password_token: generate_token, reset_password_sent_at: Time.now.utc })
6
+ end
7
+
8
+ def password_token_valid?(user_id)
9
+ user = User.find(user_id)
10
+ (user.reset_password_sent_at + 4.hours) > Time.now.utc
11
+ end
12
+
13
+ def reset_password(user_id, password)
14
+ user = User.find(user_id)
15
+ user.reset_password_token = nil
16
+ user.password = password
17
+ user.save!
18
+ end
19
+
20
+ def generate_token
21
+ SecureRandom.hex(10)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ module Sis
2
+ module Core
3
+ class ApplicationDocumentUploader < CarrierWave::Uploader::Base
4
+ # Include RMagick or MiniMagick support:
5
+ # include CarrierWave::RMagick
6
+ # include CarrierWave::MiniMagick
7
+
8
+ # Choose what kind of storage to use for this uploader:
9
+ storage :file
10
+ # storage :fog
11
+
12
+ # Override the directory where uploaded files will be stored.
13
+ # This is a sensible default for uploaders that are meant to be mounted:
14
+ def store_dir
15
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
16
+ end
17
+
18
+ # Provide a default URL as a default if there hasn't been a file uploaded:
19
+ # def default_url(*args)
20
+ # # For Rails 3.1+ asset pipeline compatibility:
21
+ # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
22
+ #
23
+ # "/images/fallback/" + [version_name, "default.png"].compact.join('_')
24
+ # end
25
+
26
+ # Process files as they are uploaded:
27
+ # process scale: [200, 300]
28
+ #
29
+ # def scale(width, height)
30
+ # # do something
31
+ # end
32
+
33
+ # Create different versions of your uploaded files:
34
+ # version :thumb do
35
+ # process resize_to_fit: [50, 50]
36
+ # end
37
+
38
+ # Add a white list of extensions which are allowed to be uploaded.
39
+ # For images you might use something like this:
40
+ def extension_whitelist
41
+ %w[pdf]
42
+ end
43
+
44
+ # Override the filename of the uploaded files:
45
+ # Avoid using model.id or version_name here, see uploader/store.rb for details.
46
+ # def filename
47
+ # "something.jpg" if original_filename
48
+ # end
49
+ end
50
+ end
51
+ end