znamenica/dneslov

View on GitHub
app/models/event.rb

Summary

Maintainability
B
6 hrs
Test Coverage
F
37%
# kind_code[string]   наименование класса события
# memory_id[int] id памяти, событие которой произошло
# place_id[int]  id места, где произошло событие
# item_id[int]   id предмета, к которому применяется событие
#    t.string "happened_at"
#    t.string "person_name"
#    t.integer "type_number"
#    t.string "order"
#    t.string "council"
#
class Event < ActiveRecord::Base
   extend Informatible
   extend TotalSize
   include WithTitles
   include WithDescriptions

   NOTICE = [
      'Repose',
      'Veneration',
      'Writing',
      'Appearance',
      'Council',
      'Miracle',
      'Resurrection',
      'Monasticry',
      'Council',
      'Marriage'
   ]

   USUAL = [
      'Marriage',
      'Exaltation',
      'Ascension',
      'Restoration',
      'Resurrection',
      'Entrance',
      'Conception',
      'Protection',
      'Conceiving',
      'Writing',
      'Sanctification',
      'Uncovering',
      'Circumcision',
      'Revival',
      'Renunciation',
      'Placing',
      'Translation',
      'Adoration',
      'Transfiguration',
      'Repose',
      'Nativity',
      'Council',
      'Meeting',
      'Passion',
      'Supper',
      'Assurance',
      'Miracle',
      'Appearance',
   ]

   SORT = [
      'Genum',
      'Nativity',
      'Resurrection',
   ]

   belongs_to :memory
   belongs_to :place, optional: true
   belongs_to :item, optional: true
   belongs_to :kind, primary_key: :key, foreign_key: :kind_code, class_name: :Subject
   has_one :coordinate, as: :info, inverse_of: :info, class_name: :CoordLink
   has_many :kind_titles, through: :kind, source: :names
   has_many :thumbs, -> { where(thumbs: { thumbable_type: "Event" }) }, foreign_key: :thumbable_id, dependent: :destroy
   has_many :attitudes, as: :imageable, class_name: :ImageAttitude
   has_many :pictures, through: :attitudes
   has_many :memos, dependent: :delete_all do
      def for calendary_slugs = nil
         if calendary_slugs
            calendary_ids = Calendary.by_slugs(calendary_slugs).unscope(:select).select(:id)
            self.where(calendary_id: calendary_ids)
         else
            self
         end
      end
   end
   has_many :calendaries, -> { distinct }, through: :memos
   scope :notice, -> { where(kind_code: NOTICE) }
   scope :usual, -> { where(kind_code: USUAL) }
   scope :calendaried, ->(calendary_slugs) { self.joins(:memos).where(memoes: { calendary_id: Calendary.by_slugs(calendary_slugs).unscope(:select).select(:id) }) }
   scope :memoed, -> { joins(:memos).merge(Memo.licit).distinct }
   scope :memoed_for, ->(calendary_slugs) { joins(:memos).calendaried(calendary_slugs).merge(Memo.licit).distinct }
   scope :by_token, -> text do
      left_outer_joins( :kind, :titles ).
         merge(Subject.by_token(text)).
         where("unaccent(events.kind_code) ~* unaccent(?)", "\\m#{text}.*").or(
         where(type_number: text.to_i).or(
         where("unaccent(descriptions.text) ~* unaccent(?)", "\\m#{text}.*").or(
         where("unaccent(names_subjects.text) ~* unaccent(?)", "\\m#{text}.*").or(
         where("unaccent(descriptions_subjects.text) ~* unaccent(?)", "\\m#{text}.*")))))
   end
   scope :by_event_code, -> code do
      if code =~ /^\d+$/
         where(id: code)
      else
         left_outer_joins(:kind_titles).where(kind_titles: { text: code })
      end
   end

   scope :by_memory_id, -> memory_id do
      where(memory_id: memory_id)
   end

   scope :by_title_and_short_name, -> title, short_name do
      joins(:memory).merge(Memory.by_short_name(short_name)).by_title(title)
   end
   scope :by_did_and_short_name, -> did, short_name do
      if /^[0-9]+$/ =~ did
         where(id: did)
      else
         by_title(did)
      end.joins(:memory).merge(Memory.by_short_name(short_name))
   end
   # required for short list
   scope :with_key, -> _ do
      selector = [ 'events.id AS _key' ]

      select(selector).group('_key').reorder("_key")
   end

   scope :with_value, -> context do
      language_codes = [ context[:locales] ].flatten
      alphabeth_codes = Languageble.alphabeth_list_for( language_codes ).flatten
      selector = self.select_values.dup

      /`(?<method_name>[^']*)'/ =~ caller.grep(/delegation/)[1]

      join = "LEFT OUTER JOIN subjects AS event_kinds_#{method_name}
                           ON event_kinds_#{method_name}.kind_code = 'EventKind'
                          AND event_kinds_#{method_name}.key = events.kind_code
              LEFT OUTER JOIN descriptions AS titles
                           ON titles.id IS NOT NULL
                          AND (titles.describable_id = events.id
                          AND titles.describable_type = 'Event'
                          AND titles.type = 'Title'
                           OR titles.describable_id = event_kinds_#{method_name}.id
                          AND titles.describable_type = 'Subject'
                          AND titles.type = 'Appellation')
                          AND titles.language_code IN ('#{language_codes.join("', '")}')
              LEFT OUTER JOIN subjects AS languages
                           ON languages.key = titles.language_code
              LEFT OUTER JOIN descriptions AS language_names
                           ON language_names.describable_id = languages.id
                          AND language_names.describable_type = 'Event'
                          AND language_names.language_code IN ('#{language_codes.join("', '")}')
              LEFT OUTER JOIN subjects AS alphabeths
                           ON alphabeths.key = titles.alphabeth_code
              LEFT OUTER JOIN descriptions AS alphabeth_names
                           ON alphabeth_names.describable_id = alphabeths.id
                          AND alphabeth_names.describable_type = 'Event'
                          AND alphabeth_names.alphabeth_code IN ('#{alphabeth_codes.join("', '")}')"

      selector << "titles.text || ' (' || events.happened_at || ')' AS _value"
      joins(join).select(selector).group(:id, "titles.text", "events.happened_at")
   end

   scope :with_memory, -> context do
      join_name = table.table_alias || table.name
      selector = self.select_values.dup << "#{join_name}.*" <<
         "COALESCE(jsonb_build_object(
            'slug', memory_slugs.text,
            'order', #{join_name}_memories.order,
            'short_name', #{join_name}_memories.short_name)) AS _memory"

      join = "LEFT OUTER JOIN memories AS #{join_name}_memories
                           ON #{join_name}_memories.id = #{join_name}.memory_id
              LEFT OUTER JOIN slugs AS memory_slugs
                           ON memory_slugs.sluggable_type = 'Memory'
                          AND memory_slugs.sluggable_id = #{join_name}.memory_id"

      joins(join, :memory).merge(Memory.with_names(context)).select(selector).group(:id, 'memory_slugs.text')
   end

   scope :with_icon, -> context do
      as = table.table_alias || table.name
      language_codes = [ context[:locales] ].flatten
      selector = self.select_values.dup
      selector << "#{as}.*" if selector.empty?
      selector << "COALESCE((WITH __picture AS (
                      SELECT #{as}_pictures.url AS url,
                             #{as}_pictures.width AS width,
                             #{as}_pictures.height AS height,
                             #{as}_image_attitudes.pos AS pos,
                             #{as}_titles.text AS title
                        FROM pictures AS #{as}_pictures
             LEFT OUTER JOIN image_attitudes AS #{as}_image_attitudes
                          ON #{as}_image_attitudes.imageable_id = #{as}.id
                         AND #{as}_image_attitudes.imageable_type = 'Event'
             LEFT OUTER JOIN descriptions AS #{as}_titles
                          ON #{as}_titles.describable_id = #{as}_pictures.id
                         AND #{as}_titles.describable_type = 'Picture'
                         AND #{as}_titles.type = 'Title'
                         AND #{as}_titles.language_code IN ('#{language_codes.join("', '")}')
                       WHERE #{as}_image_attitudes.picture_id = #{as}_pictures.id
                         AND #{as}_pictures.type = 'Icon'
                    ORDER BY random()
                       LIMIT 1)
                      SELECT jsonb_agg(__picture)
                        FROM __picture), '[]'::jsonb) AS _picture"

      select(selector).group(:id)
   end

   scope :with_place, -> context do
      language_codes = [ context[:locales] ].flatten
      selector = "jsonb_build_object('id', places.id, 'name', place_descriptions.text) AS _place"
      join = "LEFT OUTER JOIN places
                           ON events.place_id = places.id
                          AND places.id IS NOT NULL
              LEFT OUTER JOIN descriptions AS place_descriptions
                           ON place_descriptions.describable_id = places.id
                          AND place_descriptions.describable_type = 'Place'
                          AND place_descriptions.language_code IN ('#{language_codes.join("', '")}')"

      joins(join).select(selector).group(:id, 'places.id', 'place_descriptions.text')
   end

   scope :with_short_memoes, -> context do
      language_codes = [ context[:locales] ].flatten
      cslugs_rule = context[:calendary_slugs] ? "AND calendary_slugs.text IN ('#{context[:calendary_slugs].join("','")}')" : ""

      selector = "COALESCE((WITH __memoes AS (
                       SELECT DISTINCT ON(memoes.id)
                              memoes.id AS id,
                              memoes.year_date AS year_date,
                              jsonb_object_agg(DISTINCT COALESCE(memo_slugs.text, 'Null'),
                                                        order_titles_memoes.text) AS orders,
                              memo_titles.text AS title
                         FROM memoes
                         JOIN calendaries
                           ON calendaries.id = memoes.calendary_id
                         JOIN slugs AS calendary_slugs
                           ON calendary_slugs.sluggable_id = calendaries.id
                          AND calendary_slugs.sluggable_type = 'Calendary'
                          #{cslugs_rule}
              LEFT OUTER JOIN descriptions AS memo_titles
                           ON memo_titles.describable_id = memoes.id
                          AND memo_titles.describable_type = 'Memo'
                          AND memo_titles.type = 'Title'
                          AND memo_titles.language_code IN ('#{language_codes.join("', '")}')
              LEFT OUTER JOIN memo_orders
                           ON memo_orders.memo_id = memoes.id
              LEFT OUTER JOIN orders
                           ON orders.id = memo_orders.order_id
              LEFT OUTER JOIN slugs AS memo_slugs
                           ON memo_slugs.sluggable_id = orders.id
                          AND memo_slugs.sluggable_type = 'Order'
              LEFT OUTER JOIN memo_orders AS memo_orders_memoes_join
                           ON memo_orders_memoes_join.memo_id = memoes.id
              LEFT OUTER JOIN orders AS orders_memoes_join
                           ON orders_memoes_join.id = memo_orders_memoes_join.order_id
              LEFT OUTER JOIN descriptions AS order_titles_memoes
                           ON order_titles_memoes.describable_id = orders_memoes_join.id
                          AND order_titles_memoes.describable_type = 'Order'
                          AND order_titles_memoes.type IN ('Tweet')
                          AND order_titles_memoes.language_code IN ('#{language_codes.join("', '")}')
                        WHERE memoes.event_id = events.id
                          AND memoes.bond_to_id IS NULL
                          AND memoes.id IS NOT NULL
                     GROUP BY memoes.id, year_date, title)
                       SELECT jsonb_agg(__memoes)
                         FROM __memoes), '[]'::jsonb) AS _memoes"

      #binding.pry
      select(selector).group(:id)
   end

   scope :with_orders, -> context do
      language_codes = [ context[:locales] ].flatten
      cslugs_rule = context[:calendary_slugs] ? "AND calendary_slugs.text IN ('#{context[:calendary_slugs].join("','")}')" : ""
      as = table.table_alias || table.name

      selector = "COALESCE((WITH __orders AS (
                       SELECT DISTINCT ON(memo_slugs.text)
                              memo_slugs.text AS slug,
                              order_titles_memoes.text AS name
                         FROM orders
              LEFT OUTER JOIN memoes
                           ON #{as}.id = memoes.event_id
              LEFT OUTER JOIN calendaries
                           ON calendaries.id = memoes.calendary_id
              LEFT OUTER JOIN slugs AS calendary_slugs
                           ON calendary_slugs.sluggable_id = calendaries.id
                          AND calendary_slugs.sluggable_type = 'Calendary'
                              #{cslugs_rule}
              LEFT OUTER JOIN memo_orders
                           ON memo_orders.memo_id = memoes.id
              LEFT OUTER JOIN slugs AS memo_slugs
                           ON memo_slugs.sluggable_id = orders.id
                          AND memo_slugs.sluggable_type = 'Order'
              LEFT OUTER JOIN descriptions AS order_titles_memoes
                           ON order_titles_memoes.describable_id = orders.id
                          AND order_titles_memoes.describable_type = 'Order'
                          AND order_titles_memoes.type IN ('Tweet')
                          AND order_titles_memoes.language_code IN ('#{language_codes.join("', '")}')
                        WHERE orders.id = memo_orders.order_id
                          AND calendaries.licit = 't'
                     GROUP BY order_titles_memoes.text, memo_slugs.text)
                       SELECT jsonb_agg(__orders)
                         FROM __orders), '[]'::jsonb) AS _orders"

      #binding.pry
      select(selector).group(:id)
   end

   scope :with_memoes, -> context do
      language_codes = [ context[:locales] ].flatten
      cslugs_rule = context[:calendary_slugs] ? "AND calendary_slugs.text IN ('#{context[:calendary_slugs].join("','")}')" : ""

      selector = "COALESCE((WITH __memoes AS (
                       SELECT DISTINCT ON(memoes.id)
                              memoes.id AS id,
                              memoes.year_date AS year_date,
                              jsonb_object_agg(DISTINCT COALESCE(memo_slugs.text, 'Null'),
                                                        order_titles_memoes.text) AS orders,
                              memo_titles.text AS title,
                              memo_descriptions.text AS description,
                              memo_links.url AS url,
                              calendary_links.url AS calendary_url,
                              calendary_slugs.text AS calendary_slug,
                              calendary_titles.text AS calendary_title
                         FROM memoes
                         JOIN calendaries
                           ON calendaries.id = memoes.calendary_id
                         JOIN slugs AS calendary_slugs
                           ON calendary_slugs.sluggable_id = calendaries.id
                          AND calendary_slugs.sluggable_type = 'Calendary'
                          #{cslugs_rule}
              LEFT OUTER JOIN links AS calendary_links
                           ON calendary_links.info_id = calendaries.id
                          AND calendary_links.info_type = 'Calendary'
              LEFT OUTER JOIN descriptions AS memo_titles
                           ON memo_titles.describable_id = memoes.id
                          AND memo_titles.describable_type = 'Memo'
                          AND memo_titles.type = 'Title'
                          AND memo_titles.language_code IN ('#{language_codes.join("', '")}')
              LEFT OUTER JOIN descriptions AS memo_descriptions
                           ON memo_descriptions.describable_id = memoes.id
                          AND memo_descriptions.describable_type = 'Memo'
                          AND memo_descriptions.type = 'Description'
                          AND memo_descriptions.language_code IN ('#{language_codes.join("', '")}')
              LEFT OUTER JOIN links AS memo_links
                           ON memo_links.info_id = memoes.id
                          AND memo_links.info_type = 'Memo'
              LEFT OUTER JOIN descriptions AS calendary_titles
                           ON calendary_titles.describable_id = calendaries.id
                          AND calendary_titles.describable_type = 'Calendary'
                          AND calendary_titles.type = 'Appellation'
                          AND calendary_titles.language_code IN ('#{language_codes.join("', '")}')
              LEFT OUTER JOIN memo_orders
                           ON memo_orders.memo_id = memoes.id
              LEFT OUTER JOIN orders
                           ON orders.id = memo_orders.order_id
              LEFT OUTER JOIN slugs AS memo_slugs
                           ON memo_slugs.sluggable_id = orders.id
                          AND memo_slugs.sluggable_type = 'Order'
              LEFT OUTER JOIN memo_orders AS memo_orders_memoes_join
                           ON memo_orders_memoes_join.memo_id = memoes.id
              LEFT OUTER JOIN orders AS orders_memoes_join
                           ON orders_memoes_join.id = memo_orders_memoes_join.order_id
              LEFT OUTER JOIN descriptions AS order_titles_memoes
                           ON order_titles_memoes.describable_id = orders_memoes_join.id
                          AND order_titles_memoes.describable_type = 'Order'
                          AND order_titles_memoes.type IN ('Tweet')
                          AND order_titles_memoes.language_code IN ('#{language_codes.join("', '")}')
                        WHERE memoes.event_id = events.id
                          AND memoes.bond_to_id IS NULL
                          AND memoes.id IS NOT NULL
                          AND calendaries.licit = 't'
                     GROUP BY memoes.id, year_date, title, description, calendary_slug,
                              calendary_title, calendary_url, memo_links.url)
                       SELECT jsonb_agg(__memoes)
                         FROM __memoes), '[]'::jsonb) AS _memoes"

      #binding.pry
      select(selector).group(:id)
   end

   scope :with_scripta, -> context do
      language_codes = [ context[:locales] ].flatten
      selector = self.select_values.dup
      if selector.empty?
         selector << 'events.*'
      end
      selector << "COALESCE((SELECT jsonb_agg(scripta)
                               FROM scripta
                    LEFT OUTER JOIN services
                                 ON services.info_id = events.id
                                AND services.info_type = 'Event'
                    LEFT OUTER JOIN service_scripta
                                 ON service_scripta.service_id = services.id
                              WHERE scripta.id = service_scripta.scriptum_id
                                AND scripta.language_code IN ('#{language_codes.join("', '")}')), '[]'::jsonb) AS _scripta"

      select(selector).group(:id)
   end

   accepts_nested_attributes_for :place, reject_if: :all_blank
   accepts_nested_attributes_for :coordinate, reject_if: :all_blank
   accepts_nested_attributes_for :item, reject_if: :all_blank

   singleton_class.send(:alias_method, :t, :by_token)
   singleton_class.send(:alias_method, :mid, :by_memory_id)

   validates_presence_of :kind, :kind_code

   def self.year_date_for year_date, date_in, julian
      return nil if date_in.blank?

      date =  [ Time, Date, DateTime ].any? {|c| date_in.is_a?(c) } && date_in || Time.parse(date_in)
      if /(?<day>\d+)\.(?<month>\d+)%(?<weekday>\d+)$/ =~ year_date
         base_date = Date.parse("#{date.year}-#{month}-#{day}")
         base_offset = weekday.to_i - base_date.wday
         (base_date + (base_offset < 1 && (base_offset + 7) || base_offset)).strftime("%d.%m")
      elsif /(?<offset>[+-]\d+)/ =~ year_date
         gap = (julian && 13.days || 0)
         easter = WhenEaster::EasterCalendar.find_greek_easter_date(date.year) - gap
         (easter + offset.to_i.days).strftime("%d.%m")
      else
         year_date
      end
   end
end