biovision 0.0.200518.1

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 (170) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +214 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/config/biovision_manifest.js +1 -0
  6. data/app/assets/images/biovision/components/carousel/arrow-left.svg +4 -0
  7. data/app/assets/images/biovision/components/carousel/arrow-right.svg +4 -0
  8. data/app/assets/images/biovision/icons/breadcrumb-hover.svg +1 -0
  9. data/app/assets/images/biovision/icons/breadcrumb.svg +1 -0
  10. data/app/assets/images/biovision/icons/components/contact.svg +26 -0
  11. data/app/assets/images/biovision/icons/components/content.svg +20 -0
  12. data/app/assets/images/biovision/icons/components/users.svg +21 -0
  13. data/app/assets/images/biovision/icons/key.svg +16 -0
  14. data/app/assets/images/biovision/icons/log_in.svg +15 -0
  15. data/app/assets/images/biovision/icons/log_out.svg +15 -0
  16. data/app/assets/images/biovision/icons/messages/error.svg +14 -0
  17. data/app/assets/images/biovision/icons/messages/info.svg +8 -0
  18. data/app/assets/images/biovision/icons/messages/success.svg +12 -0
  19. data/app/assets/images/biovision/icons/messages/warning.svg +13 -0
  20. data/app/assets/images/biovision/icons/settings.svg +41 -0
  21. data/app/assets/images/biovision/placeholders/16x9.svg +11 -0
  22. data/app/assets/images/biovision/placeholders/1x1.svg +11 -0
  23. data/app/assets/images/biovision/placeholders/3x2.svg +11 -0
  24. data/app/assets/images/biovision/placeholders/user.svg +15 -0
  25. data/app/assets/stylesheets/biovision/admin.scss +6 -0
  26. data/app/assets/stylesheets/biovision/admin/components.scss +59 -0
  27. data/app/assets/stylesheets/biovision/admin/default.scss +57 -0
  28. data/app/assets/stylesheets/biovision/admin/layout.scss +95 -0
  29. data/app/assets/stylesheets/biovision/admin/vars.scss +56 -0
  30. data/app/assets/stylesheets/biovision/biovision.scss +134 -0
  31. data/app/assets/stylesheets/biovision/biovision/messages.scss +41 -0
  32. data/app/assets/stylesheets/biovision/components/carousel.scss +53 -0
  33. data/app/assets/stylesheets/biovision/components/forms.scss +85 -0
  34. data/app/assets/stylesheets/biovision/components/hamburger.scss +56 -0
  35. data/app/assets/stylesheets/biovision/components/lists.scss +91 -0
  36. data/app/assets/stylesheets/biovision/default.scss +57 -0
  37. data/app/assets/stylesheets/biovision/themes/default_theme.scss +2 -0
  38. data/app/assets/stylesheets/biovision/themes/default_theme/components.scss +3 -0
  39. data/app/assets/stylesheets/biovision/themes/default_theme/layout.scss +44 -0
  40. data/app/assets/stylesheets/biovision/themes/default_theme/layout/footer.scss +0 -0
  41. data/app/assets/stylesheets/biovision/themes/default_theme/layout/header.scss +39 -0
  42. data/app/assets/stylesheets/biovision/vars.scss +53 -0
  43. data/app/controllers/admin/agents_controller.rb +15 -0
  44. data/app/controllers/admin/components_controller.rb +174 -0
  45. data/app/controllers/admin/index_controller.rb +11 -0
  46. data/app/controllers/admin/ip_addresses_controller.rb +15 -0
  47. data/app/controllers/admin_controller.rb +14 -0
  48. data/app/controllers/authentication_controller.rb +79 -0
  49. data/app/controllers/concerns/authentication.rb +45 -0
  50. data/app/controllers/index_controller.rb +8 -0
  51. data/app/controllers/my/confirmations_controller.rb +2 -0
  52. data/app/controllers/my/index_controller.rb +8 -0
  53. data/app/controllers/my/profiles_controller.rb +128 -0
  54. data/app/controllers/my/recoveries_controller.rb +2 -0
  55. data/app/helpers/biovision_components_helper.rb +14 -0
  56. data/app/helpers/users_helper.rb +11 -0
  57. data/app/lib/biovision/components/base_component.rb +177 -0
  58. data/app/lib/biovision/components/component_settings.rb +30 -0
  59. data/app/lib/biovision/components/privilege_handler.rb +77 -0
  60. data/app/lib/biovision/components/track_component.rb +9 -0
  61. data/app/lib/biovision/components/users/authentication.rb +47 -0
  62. data/app/lib/biovision/components/users/code_handler.rb +23 -0
  63. data/app/lib/biovision/components/users/profile_handler.rb +12 -0
  64. data/app/lib/biovision/components/users/registration_handler.rb +99 -0
  65. data/app/lib/biovision/components/users_component.rb +47 -0
  66. data/app/lib/biovision/notifiers/base_notifier.rb +69 -0
  67. data/app/lib/biovision/notifiers/socialization_notifier.rb +31 -0
  68. data/app/lib/biovision/notifiers/users_notifier.rb +15 -0
  69. data/app/models/agent.rb +35 -0
  70. data/app/models/biovision_component.rb +45 -0
  71. data/app/models/biovision_component_user.rb +21 -0
  72. data/app/models/browser.rb +34 -0
  73. data/app/models/code.rb +71 -0
  74. data/app/models/concerns/checkable.rb +22 -0
  75. data/app/models/concerns/flat_priority.rb +50 -0
  76. data/app/models/concerns/has_language.rb +10 -0
  77. data/app/models/concerns/has_owner.rb +22 -0
  78. data/app/models/concerns/has_simple_image.rb +18 -0
  79. data/app/models/concerns/has_track.rb +10 -0
  80. data/app/models/concerns/has_uuid.rb +12 -0
  81. data/app/models/concerns/meta_texts.rb +16 -0
  82. data/app/models/concerns/nested_priority.rb +58 -0
  83. data/app/models/concerns/required_unique_name.rb +16 -0
  84. data/app/models/concerns/required_unique_slug.rb +15 -0
  85. data/app/models/concerns/toggleable.rb +33 -0
  86. data/app/models/foreign_site.rb +34 -0
  87. data/app/models/foreign_user.rb +21 -0
  88. data/app/models/ip_address.rb +31 -0
  89. data/app/models/language.rb +31 -0
  90. data/app/models/login_attempt.rb +35 -0
  91. data/app/models/metric.rb +48 -0
  92. data/app/models/metric_value.rb +11 -0
  93. data/app/models/notification.rb +37 -0
  94. data/app/models/simple_image.rb +47 -0
  95. data/app/models/simple_image_tag.rb +30 -0
  96. data/app/models/simple_image_tag_image.rb +13 -0
  97. data/app/models/token.rb +96 -0
  98. data/app/models/user.rb +136 -0
  99. data/app/models/user_language.rb +15 -0
  100. data/app/uploaders/simple_image_uploader.rb +96 -0
  101. data/app/uploaders/user_image_uploader.rb +58 -0
  102. data/app/views/admin/agents/_nav_item.html.erb +6 -0
  103. data/app/views/admin/agents/entity/_in_list.html.erb +6 -0
  104. data/app/views/admin/agents/index.html.erb +13 -0
  105. data/app/views/admin/components/_list.html.erb +12 -0
  106. data/app/views/admin/components/entity/_links.html.erb +35 -0
  107. data/app/views/admin/components/entity/_section.html.erb +12 -0
  108. data/app/views/admin/components/index.html.erb +12 -0
  109. data/app/views/admin/components/links/_track.html.erb +2 -0
  110. data/app/views/admin/components/links/_users.html.erb +11 -0
  111. data/app/views/admin/components/privileges.html.erb +20 -0
  112. data/app/views/admin/components/privileges/_component_user.html.erb +17 -0
  113. data/app/views/admin/components/privileges/_links.html.erb +35 -0
  114. data/app/views/admin/components/privileges/_privilege_flag.html.erb +28 -0
  115. data/app/views/admin/components/privileges/_users.html.erb +23 -0
  116. data/app/views/admin/components/settings.html.erb +39 -0
  117. data/app/views/admin/components/settings/_new_parameter.html.erb +46 -0
  118. data/app/views/admin/components/settings/_parameters.html.erb +20 -0
  119. data/app/views/admin/components/settings/_setting.html.erb +18 -0
  120. data/app/views/admin/components/settings/_settings.html.erb +31 -0
  121. data/app/views/admin/components/show.html.erb +14 -0
  122. data/app/views/admin/index/index.html.erb +14 -0
  123. data/app/views/admin/ip_addresses/_nav_item.html.erb +6 -0
  124. data/app/views/admin/ip_addresses/entity/_in_list.html.erb +6 -0
  125. data/app/views/admin/ip_addresses/index.html.erb +13 -0
  126. data/app/views/admin/unauthorized.html.erb +19 -0
  127. data/app/views/authentication/_form.html.erb +40 -0
  128. data/app/views/authentication/failed.js.erb +3 -0
  129. data/app/views/authentication/new.html.erb +21 -0
  130. data/app/views/index/index.html.erb +0 -0
  131. data/app/views/layouts/admin.html.erb +23 -0
  132. data/app/views/layouts/admin/_breadcrumbs.html.erb +8 -0
  133. data/app/views/layouts/admin/_footer.html.erb +10 -0
  134. data/app/views/layouts/admin/_header.html.erb +18 -0
  135. data/app/views/layouts/admin/header/_logo.html.erb +3 -0
  136. data/app/views/layouts/application/_footer.html.erb +8 -0
  137. data/app/views/layouts/application/_header.html.erb +9 -0
  138. data/app/views/layouts/application/header/_authentication.html.erb +13 -0
  139. data/app/views/my/index/index.html.erb +0 -0
  140. data/app/views/my/profiles/closed.html.erb +12 -0
  141. data/app/views/my/profiles/new.html.erb +17 -0
  142. data/app/views/my/profiles/new/_form.html.erb +147 -0
  143. data/app/views/shared/_breadcrumbs.html.erb +8 -0
  144. data/app/views/shared/_cookie_notification.html.erb +4 -0
  145. data/app/views/shared/_flash_messages.html.erb +7 -0
  146. data/app/views/shared/_meta_texts.html.erb +31 -0
  147. data/app/views/shared/_nothing_found.html.erb +1 -0
  148. data/app/views/shared/_pagination.jbuilder +15 -0
  149. data/app/views/shared/admin/_list.html.erb +19 -0
  150. data/app/views/shared/admin/_list_with_priority.html.erb +19 -0
  151. data/app/views/shared/admin/_toggleable.html.erb +8 -0
  152. data/app/views/shared/entity/_list_of_errors.html.erb +7 -0
  153. data/app/views/shared/forms/errors.js.erb +5 -0
  154. data/config/initializers/carrierwave.rb +27 -0
  155. data/config/initializers/pluralization.rb +2 -0
  156. data/config/locales/biovision-ru.yml +90 -0
  157. data/config/locales/components-ru.yml +81 -0
  158. data/config/locales/track-ru.yml +40 -0
  159. data/config/locales/users-ru.yml +130 -0
  160. data/config/routes.rb +75 -0
  161. data/db/migrate/20191228000000_create_biovision_components.rb +72 -0
  162. data/db/migrate/20200224000000_create_track_component.rb +59 -0
  163. data/db/migrate/20200224000010_create_users_component.rb +189 -0
  164. data/db/migrate/20200404000000_create_simple_images.rb +54 -0
  165. data/lib/biovision.rb +9 -0
  166. data/lib/biovision/base_methods.rb +167 -0
  167. data/lib/biovision/engine.rb +40 -0
  168. data/lib/biovision/version.rb +5 -0
  169. data/lib/tasks/biovision_tasks.rake +4 -0
  170. metadata +392 -0
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Model references to language
4
+ module HasLanguage
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ belongs_to :language, optional: true, counter_cache: :object_count
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Adds ownership to model
4
+ module HasOwner
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ scope :owned_by, ->(v) { where(user: v) }
9
+ scope :with_user_id, ->(v) { where(user_id: v) unless v.blank? }
10
+ end
11
+
12
+ # @param [User] user
13
+ # @return [Boolean]
14
+ def owned_by?(user)
15
+ !user.nil? && (self.user == user)
16
+ end
17
+
18
+ # @return [String]
19
+ def owner_name
20
+ user&.profile_name || I18n.t(:anonymous)
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Model references to simple image
4
+ module HasSimpleImage
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ belongs_to :simple_image, optional: true, counter_cache: :object_count
9
+ end
10
+
11
+ def image
12
+ simple_image&.image
13
+ end
14
+
15
+ def image_alt_text
16
+ simple_image&.image_alt_text
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HasTrack
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ belongs_to :agent, optional: true, counter_cache: :object_count
8
+ belongs_to :ip_address, optional: true, counter_cache: :object_count
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Adds UUID field and constraints to model
4
+ module HasUuid
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ after_initialize { self.uuid = SecureRandom.uuid if uuid.nil? }
9
+ validates_presence_of :uuid
10
+ validates_uniqueness_of :uuid
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Model has meta description, keywords and title
4
+ module MetaTexts
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ validates_length_of :meta_description, maximum: 500
9
+ validates_length_of :meta_keywords, maximum: 250
10
+ validates_length_of :meta_title, maximum: 250
11
+
12
+ def self.meta_text_fields
13
+ %i[meta_description meta_keywords meta_title]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Model has nested priority field
4
+ #
5
+ # Adds sorting items by priority with siblings
6
+ # @author Maxim Khan-Magomedov <maxim.km@gmail.com>
7
+ module NestedPriority
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ after_initialize :set_next_priority
12
+ before_validation :normalize_priority
13
+
14
+ scope :ordered_by_priority, -> { order('priority asc, id asc') }
15
+
16
+ def self.priority_range
17
+ (1..32_767)
18
+ end
19
+
20
+ # Change this method in models to get siblings
21
+ #
22
+ # @param [ApplicationRecord] _entity
23
+ def self.siblings(_entity)
24
+ all
25
+ end
26
+ end
27
+
28
+ # @param [Integer] delta
29
+ def change_priority(delta)
30
+ swap_priority_with_adjacent(priority + delta)
31
+
32
+ siblings = self.class.siblings(self)
33
+ siblings.ordered_by_priority.map { |e| [e.id, e.priority] }.to_h
34
+ end
35
+
36
+ protected
37
+
38
+ # @param [Integer] new_priority
39
+ def swap_priority_with_adjacent(new_priority)
40
+ adjacent = self.class.siblings(self).find_by(priority: new_priority)
41
+ if adjacent.is_a?(self.class) && (adjacent.id != id)
42
+ adjacent.update!(priority: priority)
43
+ end
44
+ update(priority: new_priority)
45
+ end
46
+
47
+ def set_next_priority
48
+ return unless id.nil? && priority == 1
49
+
50
+ self.priority = self.class.siblings(self).maximum(:priority).to_i + 1
51
+ end
52
+
53
+ def normalize_priority
54
+ range = self.class.priority_range
55
+ self.priority = range.first if priority < range.first
56
+ self.priority = range.last if priority > range.last
57
+ end
58
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Mixin for adding required unique name constrain
4
+ module RequiredUniqueName
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_validation { self.name = name.strip unless name.nil? }
9
+ validates_presence_of :name
10
+ validates_uniqueness_of :name
11
+
12
+ scope :ordered_by_name, -> { order('name asc') }
13
+ scope :with_name_like, ->(v) { where('name ilike ?', "%#{v}%") unless v.blank? }
14
+ scope :with_name, ->(v) { where('lower(name) = lower(?)', v) unless v.blank? }
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Mixin for adding required unique slug constrain
4
+ module RequiredUniqueSlug
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_validation { self.slug = slug.strip unless slug.nil? }
9
+ validates :slug, uniqueness: { case_sensitive: false }, presence: true
10
+
11
+ scope :ordered_by_slug, -> { order('slug asc') }
12
+ scope :with_slug_like, ->(v) { where('slug ilike ?', "%#{v}%") unless v.blank? }
13
+ scope :with_slug, ->(v) { where('lower(slug) = lower(?)', v) unless v.blank? }
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Adds list of toggleable attributes to model
4
+ #
5
+ # @author Maxim Khan-Magomedov <maxim.km@gmail.com>
6
+ module Toggleable
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :toggleable_attributes, instance_predicate: false, instance_accessor: false
11
+
12
+ # @param [String, Symbol] attribute
13
+ # @return [Hash]
14
+ def toggle_parameter(attribute)
15
+ return unless toggleable_attributes.include? attribute.to_sym
16
+
17
+ toggle! attribute
18
+ { attribute => self[attribute] }
19
+ end
20
+ end
21
+
22
+ # Additional class methods
23
+ module ClassMethods
24
+
25
+ private
26
+
27
+ # @param [Array<Symbol>] attributes
28
+ def toggleable(*attributes)
29
+ cattr_accessor :toggleable_attributes
30
+ self.toggleable_attributes = attributes.flatten
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Foreign site for external authentication
4
+ #
5
+ # Attributes:
6
+ # slug [string]
7
+ # name [string]
8
+ # foreign_users_count [integer]
9
+ class ForeignSite < ApplicationRecord
10
+ include RequiredUniqueName
11
+ include RequiredUniqueSlug
12
+
13
+ NAME_LIMIT = 50
14
+ SLUG_LIMIT = 50
15
+
16
+ has_many :foreign_users, dependent: :delete_all
17
+
18
+ validates_length_of :name, maximum: NAME_LIMIT
19
+ validates_length_of :slug, maximum: SLUG_LIMIT
20
+
21
+ scope :list_for_administration, -> { ordered_by_name }
22
+
23
+ # @param [String] slug
24
+ def self.[](slug)
25
+ find_by(slug: slug)
26
+ end
27
+
28
+ # @param [Hash] data
29
+ # @param [Hash] tracking
30
+ def authenticate(data, tracking)
31
+ user = foreign_users.find_by(slug: data[:uid])&.user
32
+ user || create_user(data, tracking)
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Foreign user
4
+ #
5
+ # Attributes:
6
+ # agent_id [Agent], optional
7
+ # created_at [DateTime]
8
+ # data [jsonb]
9
+ # foreign_site_id [ForeignSite]
10
+ # ip_address_id [IpAddress], optional
11
+ # updated_at [DateTime]
12
+ # user_id [User]
13
+ # slug [string]
14
+ class ForeignUser < ApplicationRecord
15
+ include HasOwner
16
+
17
+ belongs_to :foreign_site
18
+ belongs_to :user
19
+ belongs_to :agent
20
+ belongs_to :ip_address
21
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # IP address
4
+ #
5
+ # Attributes:
6
+ # banned [boolean]
7
+ # created_at [DateTime]
8
+ # ip [inet]
9
+ # object_count [integer]
10
+ # updated_at [DateTime]
11
+ class IpAddress < ApplicationRecord
12
+ include Toggleable
13
+
14
+ toggleable :banned
15
+
16
+ validates_uniqueness_of :ip
17
+
18
+ scope :list_for_administration, -> { order('ip asc') }
19
+
20
+ # @param [Integer] page
21
+ def self.page_for_administration(page = 1)
22
+ list_for_administration.page(page)
23
+ end
24
+
25
+ # @param [String] ip
26
+ def self.[](ip)
27
+ return if ip.blank?
28
+
29
+ find_or_create_by(ip: ip)
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Language
4
+ #
5
+ # Attributes:
6
+ # active [Boolean]
7
+ # code [String]
8
+ # created_at [DateTime]
9
+ # object_count [Integer]
10
+ # priority [Integer]
11
+ # slug [String]
12
+ # updated_at [DateTime]
13
+ class Language < ApplicationRecord
14
+ include FlatPriority
15
+ include RequiredUniqueSlug
16
+
17
+ CODE_PATTERN = /\A[a-z]{2,3}(-[A-z][a-z]{3})?(-[A-Z]{2})?\z/.freeze
18
+ SLUG_PATTERN = /\A[a-z][-a-z0-9]{,18}[a-z0-9]\z/.freeze
19
+
20
+ validates_presence_of :code
21
+ validates_uniqueness_of :code
22
+ validates_format_of :code, with: CODE_PATTERN
23
+ validates_format_of :slug, with: SLUG_PATTERN
24
+
25
+ scope :active, -> { where(active: true) }
26
+
27
+ # @param [String|Symbol] code
28
+ def self.[](code)
29
+ find_by(code: code)
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Failed login attempt
4
+ #
5
+ # Attributes:
6
+ # agent_id [Agent], optional
7
+ # created_at [DateTime]
8
+ # ip_address_id [IpAddress], optional
9
+ # updated_at [DateTime]
10
+ # user_id [User]
11
+ # password [string]
12
+ class LoginAttempt < ApplicationRecord
13
+ include HasOwner
14
+ include HasTrack
15
+
16
+ belongs_to :user
17
+
18
+ before_validation { self.password = password.to_s[0..254] }
19
+
20
+ scope :recent, -> { order('id desc') }
21
+ scope :since, ->(time) { where('created_at > ?', time) }
22
+ scope :list_for_administration, -> { recent }
23
+ scope :list_for_owner, ->(u) { owned_by(u).recent }
24
+
25
+ # @param [Integer] page
26
+ def self.page_for_administration(page = 1)
27
+ list_for_administration.page(page)
28
+ end
29
+
30
+ # @param [User] user
31
+ # @param [Integer] page
32
+ def self.page_for_owner(user, page = 1)
33
+ list_for_owner(user).page(page)
34
+ end
35
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Metric for component
4
+ #
5
+ # Attributes:
6
+ # biovision_component_id [BiovisionComponent]
7
+ # created_at [DateTime]
8
+ # default_period [integer]
9
+ # incremental [boolean]
10
+ # previous_value [integer]
11
+ # show_on_dashboard [boolean]
12
+ # start_with_zero [boolean]
13
+ # name [string]
14
+ # updated_at [DateTime]
15
+ # value [integer]
16
+ class Metric < ApplicationRecord
17
+ include RequiredUniqueName
18
+ include Toggleable
19
+
20
+ NAME_LIMIT = 255
21
+ PERIOD_RANGE = (1..366).freeze
22
+
23
+ belongs_to :biovision_component
24
+ has_many :metric_values, dependent: :delete_all
25
+
26
+ validates_length_of :name, maximum: NAME_LIMIT
27
+
28
+
29
+ def quantity
30
+ metric_values.sum(:quantity)
31
+ end
32
+
33
+ # @param [Integer] input
34
+ def <<(input)
35
+ metric_values.create(time: Time.now, quantity: input)
36
+
37
+ update(value: incremental? ? quantity : input, previous_value: value)
38
+ end
39
+
40
+ # @param [Integer] period
41
+ def values(period = 7)
42
+ current_value = 0
43
+ metric_values.since(period.days.ago).ordered_by_time.map do |v|
44
+ current_value = incremental? ? current_value + v.quantity : v.quantity
45
+ [v.time.strftime('%d.%m.%Y %H:%M'), current_value]
46
+ end.to_h
47
+ end
48
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Metric value
4
+ #
5
+ # Attributes:
6
+ # metric_id [Metric]
7
+ # quantity [Integer]
8
+ # value [Integer]
9
+ class MetricValue < ApplicationRecord
10
+ belongs_to :metric
11
+ end